mirror of
https://github.com/morten-olsen/bob.git
synced 2026-02-08 01:46:29 +01:00
feat: improved plugin structure
This commit is contained in:
@@ -25,5 +25,8 @@
|
||||
"build": "tsc --build configs/tsconfig.libs.json"
|
||||
},
|
||||
"types": "./dist/cjs/types/index.d.ts",
|
||||
"version": "0.1.8"
|
||||
"version": "0.1.8",
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "^0.31.14"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,103 @@
|
||||
import { Attributes, GraphNode } from '../types/node';
|
||||
import { GraphNode } from '../types/node';
|
||||
import { Planable } from '../types/planable';
|
||||
import { PluginAttributes, Plugins } from '../types/plugin';
|
||||
import { PluginAttributes, PluginContext, Plugins } from '../types/plugin';
|
||||
import { expandNode } from './expand-node';
|
||||
|
||||
type CalulationOptions<TPlugins extends Plugins> = {
|
||||
location: string;
|
||||
time: number;
|
||||
planables: Planable<PluginAttributes<TPlugins>>[];
|
||||
plugins: TPlugins;
|
||||
};
|
||||
|
||||
type CalulationResult<TPlugins extends Plugins> = {
|
||||
root: GraphNode<PluginAttributes<TPlugins>, PluginContext<TPlugins>>;
|
||||
nodes: GraphNode<PluginAttributes<TPlugins>, PluginContext<TPlugins>>[];
|
||||
completed: GraphNode<PluginAttributes<TPlugins>, PluginContext<TPlugins>>[];
|
||||
planables: Planable<PluginAttributes<TPlugins>>[];
|
||||
};
|
||||
|
||||
type RunOptions<TPlugins extends Plugins> = {
|
||||
start: number;
|
||||
context: PluginContext<TPlugins>;
|
||||
planables: Planable<PluginAttributes<TPlugins>>[];
|
||||
heuristic?: (result: any) => boolean;
|
||||
onUpdated?: (result: any) => void;
|
||||
};
|
||||
|
||||
type CalulationResult<TAttributes extends Attributes> = {
|
||||
root: GraphNode<TAttributes>;
|
||||
nodes: GraphNode<TAttributes>[];
|
||||
completed: GraphNode<TAttributes>[];
|
||||
planables: Planable<TAttributes>[];
|
||||
};
|
||||
class Bob<TPlugins extends Plugins> {
|
||||
#options: CalulationOptions<TPlugins>;
|
||||
#id: number = 0;
|
||||
|
||||
const idGen = () => {
|
||||
let id = 0;
|
||||
return () => {
|
||||
id += 1;
|
||||
return id.toString();
|
||||
};
|
||||
};
|
||||
|
||||
const calulation = async <TPlugins extends Plugins>({
|
||||
location,
|
||||
time,
|
||||
planables,
|
||||
plugins,
|
||||
heuristic,
|
||||
onUpdated,
|
||||
}: CalulationOptions<TPlugins>): Promise<
|
||||
CalulationResult<PluginAttributes<TPlugins>>
|
||||
> => {
|
||||
const generateId = idGen();
|
||||
let exploreId = 1;
|
||||
const root: GraphNode<PluginAttributes<TPlugins>> = {
|
||||
id: generateId(),
|
||||
type: 'root',
|
||||
score: 0,
|
||||
parent: null,
|
||||
duration: 0,
|
||||
time,
|
||||
location,
|
||||
exploreId: 0,
|
||||
remaining: planables,
|
||||
};
|
||||
|
||||
const nodes: GraphNode<PluginAttributes<TPlugins>>[] = [root];
|
||||
const leafNodes: GraphNode<PluginAttributes<TPlugins>>[] = [root];
|
||||
const completed: GraphNode<PluginAttributes<TPlugins>>[] = [];
|
||||
|
||||
const popHighestScore = () => {
|
||||
const highestScore = Math.max(...leafNodes.map((n) => n.score));
|
||||
const highestScoreNode = leafNodes.find((n) => n.score === highestScore);
|
||||
if (!highestScoreNode) {
|
||||
throw new Error('No highest score node');
|
||||
}
|
||||
leafNodes.splice(leafNodes.indexOf(highestScoreNode), 1);
|
||||
return highestScoreNode;
|
||||
};
|
||||
|
||||
while (leafNodes.length > 0) {
|
||||
const node = popHighestScore();
|
||||
node.exploreId = exploreId++;
|
||||
const expanded = await expandNode({ node, generateId, plugins });
|
||||
nodes.push(...expanded);
|
||||
completed.push(...expanded.filter((n) => n.remaining.length === 0));
|
||||
leafNodes.push(...expanded.filter((n) => n.remaining.length > 0));
|
||||
if (heuristic && heuristic({ root, nodes, completed, planables })) {
|
||||
break;
|
||||
}
|
||||
if (onUpdated) {
|
||||
onUpdated({ root, nodes, completed, planables });
|
||||
}
|
||||
constructor(options: CalulationOptions<TPlugins>) {
|
||||
this.#options = options;
|
||||
}
|
||||
|
||||
return { root, nodes, completed, planables };
|
||||
};
|
||||
#getNextId = (): string => {
|
||||
return (this.#id++).toString();
|
||||
};
|
||||
|
||||
public run = async ({
|
||||
planables,
|
||||
start,
|
||||
context,
|
||||
heuristic,
|
||||
}: RunOptions<TPlugins>): Promise<CalulationResult<TPlugins>> => {
|
||||
const { plugins } = this.#options;
|
||||
let exploreId = 1;
|
||||
const root: GraphNode<
|
||||
PluginAttributes<TPlugins>,
|
||||
PluginContext<TPlugins>
|
||||
> = {
|
||||
id: this.#getNextId(),
|
||||
context,
|
||||
type: 'root',
|
||||
score: 0,
|
||||
parent: null,
|
||||
duration: 0,
|
||||
time: start,
|
||||
exploreId: 0,
|
||||
remaining: planables,
|
||||
};
|
||||
|
||||
const nodes: GraphNode<
|
||||
PluginAttributes<TPlugins>,
|
||||
PluginContext<TPlugins>
|
||||
>[] = [root];
|
||||
const leafNodes: GraphNode<
|
||||
PluginAttributes<TPlugins>,
|
||||
PluginContext<TPlugins>
|
||||
>[] = [root];
|
||||
const completed: GraphNode<
|
||||
PluginAttributes<TPlugins>,
|
||||
PluginContext<TPlugins>
|
||||
>[] = [];
|
||||
|
||||
const popHighestScore = () => {
|
||||
const highestScore = Math.max(...leafNodes.map((n) => n.score));
|
||||
const highestScoreNode = leafNodes.find((n) => n.score === highestScore);
|
||||
if (!highestScoreNode) {
|
||||
throw new Error('No highest score node');
|
||||
}
|
||||
leafNodes.splice(leafNodes.indexOf(highestScoreNode), 1);
|
||||
return highestScoreNode;
|
||||
};
|
||||
|
||||
while (leafNodes.length > 0) {
|
||||
const node = popHighestScore();
|
||||
node.exploreId = exploreId++;
|
||||
const expanded = await expandNode({
|
||||
node,
|
||||
generateId: this.#getNextId,
|
||||
plugins,
|
||||
});
|
||||
nodes.push(...expanded);
|
||||
completed.push(...expanded.filter((n) => n.remaining.length === 0));
|
||||
leafNodes.push(...expanded.filter((n) => n.remaining.length > 0));
|
||||
if (heuristic && heuristic({ root, nodes, completed, planables })) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { root, nodes, completed, planables };
|
||||
};
|
||||
}
|
||||
|
||||
export type { CalulationOptions, CalulationResult };
|
||||
export { calulation };
|
||||
export { Bob };
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import { Attributes, GraphNode } from '../types/node';
|
||||
import { Plugin } from '../types/plugin';
|
||||
import { Plugins } from '../types/plugin';
|
||||
import { hasImpossible } from './is-impossible';
|
||||
|
||||
type ExpandOptions<TAttributes extends Attributes> = {
|
||||
node: GraphNode<TAttributes>;
|
||||
type ExpandOptions<
|
||||
TAttributes extends Attributes,
|
||||
TContext extends Attributes,
|
||||
> = {
|
||||
node: GraphNode<TAttributes, TContext>;
|
||||
generateId: () => string;
|
||||
plugins: Plugin[];
|
||||
plugins: Plugins;
|
||||
};
|
||||
|
||||
const expandNode = async <TAttributes extends Attributes>({
|
||||
const expandNode = async <
|
||||
TAttributes extends Attributes,
|
||||
TContext extends Attributes,
|
||||
>({
|
||||
node,
|
||||
generateId,
|
||||
plugins,
|
||||
}: ExpandOptions<TAttributes>): Promise<GraphNode<TAttributes>[]> => {
|
||||
}: ExpandOptions<TAttributes, TContext>): Promise<
|
||||
GraphNode<TAttributes, TContext>[]
|
||||
> => {
|
||||
const isImpossible = hasImpossible({ node });
|
||||
|
||||
if (isImpossible) {
|
||||
@@ -21,7 +29,7 @@ const expandNode = async <TAttributes extends Attributes>({
|
||||
}
|
||||
|
||||
const metaNodes = await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
Object.values(plugins).map(async (plugin) => {
|
||||
if (!plugin.getMetaNodes) {
|
||||
return [];
|
||||
}
|
||||
@@ -39,7 +47,7 @@ const expandNode = async <TAttributes extends Attributes>({
|
||||
);
|
||||
|
||||
const planables = node.remaining.filter((planable) => {
|
||||
const hasNonPlanable = plugins.some(
|
||||
const hasNonPlanable = Object.values(plugins).some(
|
||||
(plugin) => plugin.isPlanable && !plugin.isPlanable(node, planable),
|
||||
);
|
||||
return !hasNonPlanable;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type { GraphNode } from './types/node';
|
||||
export type { Planable } from './types/planable';
|
||||
export { expandNode } from './algorithm/expand-node';
|
||||
export { calulation, type CalulationResult } from './algorithm/calulation';
|
||||
export { Bob, type CalulationResult } from './algorithm/calulation';
|
||||
export { plugins } from './plugins/index';
|
||||
|
||||
20
packages/algorithm/src/plugins/create.ts
Normal file
20
packages/algorithm/src/plugins/create.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { TSchema, Static } from '@sinclair/typebox';
|
||||
import type { Plugin } from '../types/plugin';
|
||||
|
||||
const createPlugin = <
|
||||
TAttribuesSchema extends TSchema,
|
||||
TContextSchema extends TSchema,
|
||||
>(
|
||||
attributes: TAttribuesSchema,
|
||||
context: TContextSchema,
|
||||
plugin: Omit<
|
||||
Plugin<Static<TAttribuesSchema>, Static<TContextSchema>>,
|
||||
'attributes' | 'context'
|
||||
>,
|
||||
): Plugin<Static<TAttribuesSchema>, Static<TContextSchema>> => ({
|
||||
...plugin,
|
||||
attributes,
|
||||
context,
|
||||
});
|
||||
|
||||
export { createPlugin };
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Attributes } from '../types/node';
|
||||
import { Plugin } from '../types/plugin';
|
||||
import { transport } from './transport';
|
||||
|
||||
const plugins = {
|
||||
transport,
|
||||
} satisfies Record<string, (...args: any[]) => Plugin<Attributes>>;
|
||||
} satisfies Record<string, (...args: any[]) => Plugin>;
|
||||
|
||||
export { plugins };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GraphNode } from '../types/node';
|
||||
import { Plugin } from '../types/plugin';
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { createPlugin } from './create';
|
||||
|
||||
type GetTravelTime = (from: string, to: string) => Promise<number>;
|
||||
|
||||
@@ -7,58 +7,66 @@ type TransportOptions = {
|
||||
getTravelTime: GetTravelTime;
|
||||
};
|
||||
|
||||
type TransportAttributes = {
|
||||
locations?: string[];
|
||||
};
|
||||
const transport = ({ getTravelTime }: TransportOptions) =>
|
||||
createPlugin(
|
||||
Type.Object({
|
||||
locations: Type.Optional(Type.Array(Type.String())),
|
||||
}),
|
||||
Type.Object({
|
||||
location: Type.String(),
|
||||
}),
|
||||
{
|
||||
getMetaNodes: async (node) => {
|
||||
const locations =
|
||||
(node.type !== 'travel' &&
|
||||
[
|
||||
...new Set(
|
||||
node.remaining
|
||||
.map((planable) => planable.attributes?.locations)
|
||||
.flat(),
|
||||
),
|
||||
]
|
||||
.filter((location) => location !== node.context.location)
|
||||
.filter(Boolean)
|
||||
.map((l) => l!)) ||
|
||||
[];
|
||||
|
||||
const transport = ({
|
||||
getTravelTime,
|
||||
}: TransportOptions): Plugin<TransportAttributes> => ({
|
||||
getMetaNodes: async (node) => {
|
||||
const locations =
|
||||
(node.type !== 'travel' &&
|
||||
[
|
||||
...new Set(
|
||||
node.remaining
|
||||
.map((planable) => planable.attributes?.locations)
|
||||
.flat(),
|
||||
),
|
||||
]
|
||||
.filter((location) => location !== node.location)
|
||||
.filter(Boolean)
|
||||
.map((l) => l!)) ||
|
||||
[];
|
||||
const travelNodes = await Promise.all(
|
||||
locations.map(async (location) => {
|
||||
const travelTime = await getTravelTime(
|
||||
node.context.location,
|
||||
location,
|
||||
);
|
||||
return {
|
||||
...node,
|
||||
type: 'travel' as const,
|
||||
context: {
|
||||
...node.context,
|
||||
location,
|
||||
},
|
||||
planable: undefined,
|
||||
location,
|
||||
exploreId: 0,
|
||||
score: node.score - 10,
|
||||
time: node.time + node.duration,
|
||||
duration: travelTime,
|
||||
parent: node.id,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const travelNodes = await Promise.all(
|
||||
locations.map<Promise<GraphNode<TransportAttributes>>>(
|
||||
async (location) => {
|
||||
const travelTime = await getTravelTime(node.location, location);
|
||||
return {
|
||||
...node,
|
||||
type: 'travel',
|
||||
planable: undefined,
|
||||
location,
|
||||
exploreId: 0,
|
||||
score: node.score - 20,
|
||||
time: node.time + node.duration,
|
||||
duration: travelTime,
|
||||
parent: node.id,
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return travelNodes;
|
||||
},
|
||||
isPlanable: (node, planable) => {
|
||||
if (
|
||||
planable.attributes?.locations &&
|
||||
!planable.attributes?.locations.includes(node.location)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
return travelNodes;
|
||||
},
|
||||
isPlanable: (node, planable) => {
|
||||
if (
|
||||
planable.attributes?.locations &&
|
||||
!planable.attributes?.locations.includes(node.context.location)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export { transport };
|
||||
|
||||
@@ -2,11 +2,14 @@ import type { Planable } from './planable';
|
||||
|
||||
type Attributes = any;
|
||||
|
||||
type GraphNode<TAttributes extends Attributes = Attributes> = {
|
||||
type GraphNode<
|
||||
TAttributes extends Attributes = Attributes,
|
||||
TContext extends Attributes = Attributes,
|
||||
> = {
|
||||
id: string;
|
||||
type: 'root' | 'planable' | 'travel';
|
||||
score: number;
|
||||
location: string;
|
||||
context: TContext;
|
||||
parent: string | null;
|
||||
time: number;
|
||||
exploreId: number;
|
||||
|
||||
@@ -11,7 +11,6 @@ type Planable<TAttributes extends Attributes = Attributes> = {
|
||||
};
|
||||
attributes: TAttributes;
|
||||
required?: boolean;
|
||||
locations?: string[];
|
||||
};
|
||||
|
||||
export type { Planable };
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import { Attributes, GraphNode } from './node';
|
||||
import { GraphNode } from './node';
|
||||
import { Planable } from './planable';
|
||||
|
||||
type Plugin<TAttributes extends Attributes = Attributes> = {
|
||||
type Plugin<TAttributes = any, TContext = any> = {
|
||||
context: any;
|
||||
attributes: any;
|
||||
getMetaNodes?: (
|
||||
node: GraphNode<TAttributes>,
|
||||
) => Promise<GraphNode<TAttributes>[]>;
|
||||
isImpossible?: (node: GraphNode<TAttributes>) => Promise<boolean>;
|
||||
node: GraphNode<TAttributes, TContext>,
|
||||
) => Promise<GraphNode<TAttributes, TContext>[]>;
|
||||
isImpossible?: (node: GraphNode<TAttributes, TContext>) => Promise<boolean>;
|
||||
isPlanable?: (
|
||||
node: GraphNode<TAttributes>,
|
||||
node: GraphNode<TAttributes, TContext>,
|
||||
planable: Planable<TAttributes>,
|
||||
) => boolean;
|
||||
};
|
||||
|
||||
type Plugins = Plugin[];
|
||||
type Plugins = Record<string, Plugin>;
|
||||
|
||||
type PluginAttributes<TPlugins extends Plugins> = {
|
||||
[K in keyof TPlugins]: TPlugins[K] extends Plugin<infer TAttributes>
|
||||
? TAttributes extends Attributes
|
||||
[K in keyof TPlugins]: TPlugins[K] extends Plugin<infer TAttributes, any>
|
||||
? TAttributes
|
||||
: never
|
||||
: never;
|
||||
}[number];
|
||||
}[keyof TPlugins];
|
||||
|
||||
export type { Plugin, Plugins, PluginAttributes };
|
||||
type PluginContext<TPlugins extends Plugins> = {
|
||||
[K in keyof TPlugins]: TPlugins[K] extends Plugin<any, infer TContext>
|
||||
? TContext
|
||||
: never;
|
||||
}[keyof TPlugins];
|
||||
|
||||
export type { Plugin, Plugins, PluginAttributes, PluginContext };
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM"
|
||||
],
|
||||
"target": "ES2022",
|
||||
"module": "ESNext"
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
|
||||
@@ -14,7 +14,7 @@ type BlockProps = {
|
||||
presenter?: React.FC<any>;
|
||||
};
|
||||
|
||||
const id = (function* () {
|
||||
const id = (function*() {
|
||||
let i = 0;
|
||||
while (true) {
|
||||
yield i++;
|
||||
@@ -57,6 +57,7 @@ const Block: React.FC<BlockProps> = ({
|
||||
});
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
setRunning(false);
|
||||
@@ -75,6 +76,7 @@ const Block: React.FC<BlockProps> = ({
|
||||
}
|
||||
if (type === 'error') {
|
||||
setError(payload);
|
||||
console.error(payload);
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', listener);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { calulation, plugins } from '@bob-the-algorithm/core';
|
||||
import { Bob, plugins } from '@bob-the-algorithm/core';
|
||||
import { createWorker } from '../../features/runner/worker';
|
||||
import { convertResult } from '../../utils/graph';
|
||||
|
||||
@@ -6,141 +6,149 @@ const MIN = 1000 * 60;
|
||||
const HOUR = 1000 * 60 * 60;
|
||||
|
||||
const getTravelTime = async () => 30 * MIN;
|
||||
const transport = plugins.transport({
|
||||
getTravelTime,
|
||||
});
|
||||
|
||||
const realistic = async () => {
|
||||
const result = await calulation({
|
||||
location: 'home',
|
||||
time: 0,
|
||||
heuristic: ({ completed }) => completed.length >= 3,
|
||||
plugins: [
|
||||
plugins.transport({
|
||||
getTravelTime,
|
||||
}),
|
||||
],
|
||||
planables: [
|
||||
{
|
||||
id: `Brush teeth`,
|
||||
duration: 2 * MIN,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 8 * HOUR,
|
||||
},
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
},
|
||||
score: 1,
|
||||
try {
|
||||
const bob = new Bob({
|
||||
plugins: { transport },
|
||||
});
|
||||
const result = await bob.run({
|
||||
context: {
|
||||
location: 'home',
|
||||
},
|
||||
{
|
||||
id: 'Drop off kids',
|
||||
duration: 30 * MIN,
|
||||
attributes: {
|
||||
locations: ['daycare'],
|
||||
start: 0,
|
||||
heuristic: ({ completed }) => completed.length >= 3,
|
||||
planables: [
|
||||
{
|
||||
id: `Brush teeth`,
|
||||
duration: 2 * MIN,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 8 * HOUR,
|
||||
},
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
},
|
||||
score: 1,
|
||||
},
|
||||
score: 1,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 9 * HOUR,
|
||||
{
|
||||
id: 'Drop off kids',
|
||||
duration: 30 * MIN,
|
||||
attributes: {
|
||||
locations: ['daycare'],
|
||||
},
|
||||
score: 1,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 9 * HOUR,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Pickup the kids',
|
||||
duration: 30 * MIN,
|
||||
attributes: {
|
||||
locations: ['daycare'],
|
||||
{
|
||||
id: 'Pickup the kids',
|
||||
duration: 30 * MIN,
|
||||
attributes: {
|
||||
locations: ['daycare'],
|
||||
},
|
||||
score: 1,
|
||||
start: {
|
||||
min: 15 * HOUR,
|
||||
max: 15.5 * HOUR,
|
||||
},
|
||||
},
|
||||
score: 1,
|
||||
start: {
|
||||
min: 15 * HOUR,
|
||||
max: 15.5 * HOUR,
|
||||
{
|
||||
id: `Eat breakfast`,
|
||||
duration: 15 * MIN,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 9 * HOUR,
|
||||
},
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
},
|
||||
score: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: `Eat breakfast`,
|
||||
duration: 15 * MIN,
|
||||
start: {
|
||||
min: 7 * HOUR,
|
||||
max: 9 * HOUR,
|
||||
{
|
||||
id: 'Do work',
|
||||
duration: 1 * HOUR,
|
||||
count: 5,
|
||||
attributes: {
|
||||
locations: ['work'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 18 * HOUR,
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
{
|
||||
id: 'Read book',
|
||||
duration: 0.5 * HOUR,
|
||||
attributes: {
|
||||
locations: ['home', 'work'],
|
||||
},
|
||||
score: 3,
|
||||
count: 2,
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 22 * HOUR,
|
||||
},
|
||||
},
|
||||
score: 1,
|
||||
},
|
||||
{
|
||||
id: 'Do work',
|
||||
duration: 1 * HOUR,
|
||||
count: 5,
|
||||
attributes: {
|
||||
locations: ['work'],
|
||||
{
|
||||
id: 'Meditate',
|
||||
duration: 10 * MIN,
|
||||
score: 1,
|
||||
attributes: {},
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 22 * HOUR,
|
||||
},
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 18 * HOUR,
|
||||
{
|
||||
id: 'Meeting 1',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['work', 'work'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 10 * HOUR,
|
||||
max: 10 * HOUR,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Read book',
|
||||
duration: 0.5 * HOUR,
|
||||
attributes: {
|
||||
locations: ['home', 'work'],
|
||||
{
|
||||
id: 'Meeting 2',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['work', 'work'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 12 * HOUR,
|
||||
max: 12 * HOUR,
|
||||
},
|
||||
},
|
||||
score: 3,
|
||||
count: 2,
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 22 * HOUR,
|
||||
{
|
||||
id: 'Play playstation',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 16 * HOUR,
|
||||
max: 24 * HOUR,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Meditate',
|
||||
duration: 10 * MIN,
|
||||
score: 1,
|
||||
attributes: {},
|
||||
start: {
|
||||
min: 8 * HOUR,
|
||||
max: 22 * HOUR,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Meeting 1',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['work', 'work'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 10 * HOUR,
|
||||
max: 10 * HOUR,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Meeting 2',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['work', 'work'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 12 * HOUR,
|
||||
max: 12 * HOUR,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Play playstation',
|
||||
duration: 1 * HOUR,
|
||||
attributes: {
|
||||
locations: ['home'],
|
||||
},
|
||||
score: 10,
|
||||
start: {
|
||||
min: 16 * HOUR,
|
||||
max: 24 * HOUR,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
return convertResult(result);
|
||||
],
|
||||
});
|
||||
return convertResult(result);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
createWorker({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import './script.ts';
|
||||
const worker = new Worker(new URL('./script.ts', import.meta.url), {
|
||||
type: 'module',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user