@owlmeans/client-planning 0.1.18-rc.21 → 0.1.18-rc.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/client-planning/SKILL.md +24 -23
- package/build/client.js +2 -2
- package/build/client.js.map +1 -1
- package/build/commits.js +4 -4
- package/build/commits.js.map +1 -1
- package/build/consts.d.ts +12 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +15 -0
- package/build/consts.js.map +1 -1
- package/build/definitions.js +2 -2
- package/build/definitions.js.map +1 -1
- package/build/events/types.d.ts +29 -0
- package/build/events/types.d.ts.map +1 -0
- package/build/events/types.js.map +1 -0
- package/build/events.d.ts +9 -20
- package/build/events.d.ts.map +1 -1
- package/build/events.js +97 -103
- package/build/events.js.map +1 -1
- package/build/facade.d.ts.map +1 -1
- package/build/facade.js +10 -10
- package/build/facade.js.map +1 -1
- package/build/feed.d.ts +4 -4
- package/build/feed.d.ts.map +1 -1
- package/build/feed.js +17 -15
- package/build/feed.js.map +1 -1
- package/build/helper/types.d.ts +15 -0
- package/build/helper/types.d.ts.map +1 -0
- package/build/helper/types.js +2 -0
- package/build/helper/types.js.map +1 -0
- package/build/helper.d.ts +7 -7
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +16 -8
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/stores.d.ts +3 -10
- package/build/stores.d.ts.map +1 -1
- package/build/stores.js +9 -36
- package/build/stores.js.map +1 -1
- package/build/sync/types.d.ts +18 -0
- package/build/sync/types.d.ts.map +1 -0
- package/build/sync/types.js +2 -0
- package/build/{utils → sync}/types.js.map +1 -1
- package/build/sync.d.ts +4 -0
- package/build/sync.d.ts.map +1 -0
- package/build/sync.js +26 -0
- package/build/sync.js.map +1 -0
- package/build/utils/record/types.d.ts +28 -0
- package/build/utils/record/types.d.ts.map +1 -0
- package/build/utils/record/types.js +2 -0
- package/build/utils/record/types.js.map +1 -0
- package/build/utils/record.d.ts +3 -43
- package/build/utils/record.d.ts.map +1 -1
- package/build/utils/record.js +64 -72
- package/build/utils/record.js.map +1 -1
- package/package.json +15 -15
- package/src/client.ts +2 -2
- package/src/commits.ts +4 -4
- package/src/consts.ts +23 -0
- package/src/definitions.ts +2 -2
- package/src/events/types.ts +29 -0
- package/src/events.ts +105 -103
- package/src/facade.ts +11 -12
- package/src/feed.ts +17 -15
- package/src/helper/types.ts +15 -0
- package/src/helper.ts +27 -10
- package/src/index.ts +4 -1
- package/src/stores.ts +9 -39
- package/src/sync/types.ts +18 -0
- package/src/sync.ts +40 -0
- package/src/utils/record/types.ts +27 -0
- package/src/utils/record.ts +68 -75
- package/tests/events.spec.ts +12 -8
- package/tests/stores.spec.ts +13 -11
- package/build/utils/types.d.ts +0 -3
- package/build/utils/types.d.ts.map +0 -1
- package/build/utils.d.ts +0 -13
- package/build/utils.d.ts.map +0 -1
- package/build/utils.js +0 -15
- package/build/utils.js.map +0 -1
- package/src/utils/types.ts +0 -3
- package/src/utils.ts +0 -20
- /package/build/{utils → events}/types.js +0 -0
package/build/stores.d.ts
CHANGED
|
@@ -12,17 +12,10 @@ import type { PlanningStoreAliases, PlanningStores, SyncOptions, WithPlanningSto
|
|
|
12
12
|
* homes the moment its kind is read wrong. Idempotent, like every `append*`.
|
|
13
13
|
*/
|
|
14
14
|
export declare const appendPlanningStores: <C extends BasicConfig, T extends BasicContext<C>>(context: T, aliases?: Partial<PlanningStoreAliases>) => T & WithPlanningStores & StateResourceAppend;
|
|
15
|
-
/**
|
|
15
|
+
/** @deprecated compat:factory-refactor — use `planningContextOf(context).stores()` */
|
|
16
16
|
export declare const planningStoresOf: (context: BasicContext<any>) => PlanningStores | null;
|
|
17
|
-
/**
|
|
18
|
-
* Make the store agree with an authoritative list WITHIN a scope: every card given is written
|
|
19
|
-
* (unless the store holds a newer fold of it), and every card matching `where` that the list does
|
|
20
|
-
* not name is dropped. Cards outside `where` are left alone — which is why this, and never
|
|
21
|
-
* `replace()`, is how a list reaches the one shared card store.
|
|
22
|
-
*
|
|
23
|
-
* Unchanged cards are not rewritten, so a periodic re-seed wakes no subscriber.
|
|
24
|
-
*/
|
|
17
|
+
/** @deprecated compat:factory-refactor — use `syncHelper.syncCards(…)` */
|
|
25
18
|
export declare const syncCards: (store: PlanningStores['cards'], items: Workcard[], where?: Criteria<Workcard>, opts?: SyncOptions) => Promise<void>;
|
|
26
|
-
/**
|
|
19
|
+
/** @deprecated compat:factory-refactor — use `syncHelper.syncLinks(…)` */
|
|
27
20
|
export declare const syncLinks: (store: PlanningStores['links'], items: Relationship[], where?: Criteria<Relationship>, opts?: SyncOptions) => Promise<void>;
|
|
28
21
|
//# sourceMappingURL=stores.d.ts.map
|
package/build/stores.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAI/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEvG;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC1E,CAAC,YAAY,OAAO,CAAC,oBAAoB,CAAC,KAClD,CAAC,GAAG,kBAAkB,GAAG,mBAgB3B,CAAA;AAGD,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,YAAa,YAAY,CAAC,GAAG,CAAC,KAAG,cAAc,GAAG,IAA2C,CAAA;AAE1H,0EAA0E;AAC1E,eAAO,MAAM,SAAS,UACb,cAAc,CAAC,OAAO,CAAC,SAAS,QAAQ,EAAE,UAAU,QAAQ,CAAC,QAAQ,CAAC,SAAS,WAAW,KAChG,OAAO,CAAC,IAAI,CAA0D,CAAA;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,SAAS,UACb,cAAc,CAAC,OAAO,CAAC,SAAS,YAAY,EAAE,UAAU,QAAQ,CAAC,YAAY,CAAC,SAAS,WAAW,KACxG,OAAO,CAAC,IAAI,CAA0D,CAAA"}
|
package/build/stores.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { appendStateResource } from '@owlmeans/state';
|
|
2
|
-
import { DEFAULT_STORE_ALIASES } from './
|
|
3
|
-
import {
|
|
2
|
+
import { DEFAULT_STORE_ALIASES } from './consts.js';
|
|
3
|
+
import { planningContextOf } from './helper.js';
|
|
4
|
+
import { syncHelper } from './sync.js';
|
|
4
5
|
/**
|
|
5
6
|
* Register the planning state mirror: ONE card store for every kind, a link store and a commit
|
|
6
7
|
* store, and `context.planningStores()`.
|
|
@@ -24,38 +25,10 @@ export const appendPlanningStores = (context, aliases) => {
|
|
|
24
25
|
}
|
|
25
26
|
return result;
|
|
26
27
|
};
|
|
27
|
-
/**
|
|
28
|
-
export const planningStoresOf = (context) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* Make the store agree with an authoritative list WITHIN a scope: every card given is written
|
|
34
|
-
* (unless the store holds a newer fold of it), and every card matching `where` that the list does
|
|
35
|
-
* not name is dropped. Cards outside `where` are left alone — which is why this, and never
|
|
36
|
-
* `replace()`, is how a list reaches the one shared card store.
|
|
37
|
-
*
|
|
38
|
-
* Unchanged cards are not rewritten, so a periodic re-seed wakes no subscriber.
|
|
39
|
-
*/
|
|
40
|
-
export const syncCards = async (store, items, where, opts) => {
|
|
41
|
-
await dropUnnamed(store, recordHelper.idsOf(items), where, opts);
|
|
42
|
-
for (const item of items) {
|
|
43
|
-
await recordHelper.putCard(store, item);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
/** {@link syncCards} for the link store. */
|
|
47
|
-
export const syncLinks = async (store, items, where, opts) => {
|
|
48
|
-
await dropUnnamed(store, recordHelper.idsOf(items), where, opts);
|
|
49
|
-
for (const item of items) {
|
|
50
|
-
await recordHelper.putLink(store, item);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const dropUnnamed = async (store, named, where, opts) => {
|
|
54
|
-
const keep = new Set([...named, ...(opts?.keep ?? [])]);
|
|
55
|
-
const scoped = await store.list(where);
|
|
56
|
-
const stale = recordHelper.idsOf(scoped.items).filter(id => !keep.has(id));
|
|
57
|
-
if (stale.length > 0) {
|
|
58
|
-
await store.purge({ id: { $in: stale } });
|
|
59
|
-
}
|
|
60
|
-
};
|
|
28
|
+
/** @deprecated compat:factory-refactor — use `planningContextOf(context).stores()` */
|
|
29
|
+
export const planningStoresOf = (context) => planningContextOf(context).stores();
|
|
30
|
+
/** @deprecated compat:factory-refactor — use `syncHelper.syncCards(…)` */
|
|
31
|
+
export const syncCards = async (store, items, where, opts) => await syncHelper.syncCards(store, items, where, opts);
|
|
32
|
+
/** @deprecated compat:factory-refactor — use `syncHelper.syncLinks(…)` */
|
|
33
|
+
export const syncLinks = async (store, items, where, opts) => await syncHelper.syncLinks(store, items, where, opts);
|
|
61
34
|
//# sourceMappingURL=stores.js.map
|
package/build/stores.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAA4B,MAAM,iBAAiB,CAAA;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAA4B,MAAM,iBAAiB,CAAA;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,OAAU,EAAE,OAAuC,EACL,EAAE;IAChD,MAAM,KAAK,GAAyB,EAAE,GAAG,qBAAqB,EAAE,GAAG,OAAO,EAAE,CAAA;IAC5E,IAAI,GAAG,GAAG,mBAAmB,CAAiB,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnE,GAAG,GAAG,mBAAmB,CAA8B,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACxE,GAAG,GAAG,mBAAmB,CAAgB,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAE5D,MAAM,MAAM,GAAG,GAAmD,CAAA;IAClE,IAAI,MAAM,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;QAClC,MAAM,CAAC,cAAc,GAAG,GAAG,EAAE,CAAC,CAAC;YAC7B,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;YAC3C,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;YAC3C,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC;SAChD,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAGD,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAA0B,EAAyB,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAA;AAE1H,0EAA0E;AAC1E,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,KAA8B,EAAE,KAAiB,EAAE,KAA0B,EAAE,IAAkB,EAClF,EAAE,CAAC,MAAM,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;AAEzE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,KAA8B,EAAE,KAAqB,EAAE,KAA8B,EAAE,IAAkB,EAC1F,EAAE,CAAC,MAAM,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Relationship, Workcard } from '@owlmeans/planning';
|
|
2
|
+
import type { Criteria } from '@owlmeans/resource';
|
|
3
|
+
import type { PlanningStores, SyncOptions } from '../types.js';
|
|
4
|
+
/** An authoritative list made true in one mirror store — within a scope, never beyond it. */
|
|
5
|
+
export interface SyncHelper {
|
|
6
|
+
/**
|
|
7
|
+
* Make the store agree with an authoritative list WITHIN a scope: every card given is written
|
|
8
|
+
* (unless the store holds a newer fold of it), and every card matching `where` that the list does
|
|
9
|
+
* not name is dropped. Cards outside `where` are left alone — which is why this, and never
|
|
10
|
+
* `replace()`, is how a list reaches the one shared card store.
|
|
11
|
+
*
|
|
12
|
+
* Unchanged cards are not rewritten, so a periodic re-seed wakes no subscriber.
|
|
13
|
+
*/
|
|
14
|
+
syncCards: (store: PlanningStores['cards'], items: Workcard[], where?: Criteria<Workcard>, opts?: SyncOptions) => Promise<void>;
|
|
15
|
+
/** {@link SyncHelper.syncCards} for the link store. */
|
|
16
|
+
syncLinks: (store: PlanningStores['links'], items: Relationship[], where?: Criteria<Relationship>, opts?: SyncOptions) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9D,6FAA6F;AAC7F,MAAM,WAAW,UAAU;IACzB;;;;;;;OAOG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/H,uDAAuD;IACvD,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACxI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":""}
|
package/build/sync.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAIjD,eAAO,MAAM,gBAAgB,QAAO,UA+BnC,CAAA;AAED,eAAO,MAAM,UAAU,YAAqB,CAAA"}
|
package/build/sync.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { recordUtils } from './utils/record.js';
|
|
2
|
+
export const createSyncHelper = () => {
|
|
3
|
+
const syncCards = async (store, items, where, opts) => {
|
|
4
|
+
await dropUnnamed(store, recordUtils.idsOf(items), where, opts);
|
|
5
|
+
for (const item of items) {
|
|
6
|
+
await recordUtils.putCard(store, item);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
const syncLinks = async (store, items, where, opts) => {
|
|
10
|
+
await dropUnnamed(store, recordUtils.idsOf(items), where, opts);
|
|
11
|
+
for (const item of items) {
|
|
12
|
+
await recordUtils.putLink(store, item);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const dropUnnamed = async (store, named, where, opts) => {
|
|
16
|
+
const keep = new Set([...named, ...(opts?.keep ?? [])]);
|
|
17
|
+
const scoped = await store.list(where);
|
|
18
|
+
const stale = recordUtils.idsOf(scoped.items).filter(id => !keep.has(id));
|
|
19
|
+
if (stale.length > 0) {
|
|
20
|
+
await store.purge({ id: { $in: stale } });
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
return { syncCards, syncLinks };
|
|
24
|
+
};
|
|
25
|
+
export const syncHelper = createSyncHelper();
|
|
26
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAe,EAAE;IAC/C,MAAM,SAAS,GAAG,KAAK,EACrB,KAA8B,EAAE,KAAiB,EAAE,KAA0B,EAAE,IAAkB,EAClF,EAAE;QACjB,MAAM,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;QAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QACxC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,SAAS,GAAG,KAAK,EACrB,KAA8B,EAAE,KAAqB,EAAE,KAA8B,EAAE,IAAkB,EAC1F,EAAE;QACjB,MAAM,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;QAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QACxC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,KAAK,EACvB,KAAwD,EAAE,KAAe,EAAE,KAAmB,EAAE,IAAkB,EACnG,EAAE;QACjB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAO,KAAiC,CAAC,IAAI,CAAC,KAA2B,CAAC,CAAA;QACzF,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;QACzE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC,CAAA;IAED,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAA;AACjC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CommitState, Relationship, Workcard } from '@owlmeans/planning';
|
|
2
|
+
import type { ResourceRecord } from '@owlmeans/resource';
|
|
3
|
+
import type { StateResource } from '@owlmeans/state';
|
|
4
|
+
import type { PlanningCommitRecord, PlanningStores } from '../../types.js';
|
|
5
|
+
/** How a record is written into the state mirror: only when it is newer, and only when it changed. */
|
|
6
|
+
export interface RecordUtils {
|
|
7
|
+
/** Same content, whatever order the keys arrived in. */
|
|
8
|
+
sameRecord: (left: unknown, right: unknown) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Write one card unless the store already holds a NEWER fold of it.
|
|
11
|
+
*
|
|
12
|
+
* `seq` is the last folded transition, so a lower one is an older picture — a list fetched before a
|
|
13
|
+
* commit landed must not undo that commit. The head only ever grows: a head this client allocated
|
|
14
|
+
* (`applyReceipt`) stays until a fold catches up with it.
|
|
15
|
+
*/
|
|
16
|
+
putCard: (store: StateResource<Workcard>, card: Workcard) => Promise<Workcard>;
|
|
17
|
+
putLink: (store: StateResource<Relationship>, link: Relationship) => Promise<void>;
|
|
18
|
+
/** Drop a card, whatever lives under it when it is a project, and every link touching any of them. */
|
|
19
|
+
dropCard: (stores: PlanningStores, id: string, project: boolean) => Promise<void>;
|
|
20
|
+
/** Record what is known about a transition, merged over what was known before. */
|
|
21
|
+
putCommit: (store: StateResource<PlanningCommitRecord>, record: Partial<PlanningCommitRecord> & {
|
|
22
|
+
id: string;
|
|
23
|
+
state: CommitState;
|
|
24
|
+
}) => Promise<PlanningCommitRecord>;
|
|
25
|
+
clean: <T extends object>(record: T) => T;
|
|
26
|
+
idsOf: <T extends ResourceRecord>(records: T[]) => string[];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/record/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE1E,sGAAsG;AACtG,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAA;IACtD;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9E,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClF,sGAAsG;IACtG,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjF,kFAAkF;IAClF,SAAS,EAAE,CACT,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,KACnH,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAClC,KAAK,EAAE,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAA;IACzC,KAAK,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,MAAM,EAAE,CAAA;CAC5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/utils/record/types.ts"],"names":[],"mappings":""}
|
package/build/utils/record.d.ts
CHANGED
|
@@ -1,44 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import type { StateResource } from '@owlmeans/state';
|
|
5
|
-
/** Same content, whatever order the keys arrived in. */
|
|
6
|
-
declare const sameRecord: (left: unknown, right: unknown) => boolean;
|
|
7
|
-
/**
|
|
8
|
-
* Write one card unless the store already holds a NEWER fold of it.
|
|
9
|
-
*
|
|
10
|
-
* `seq` is the last folded transition, so a lower one is an older picture — a list fetched before a
|
|
11
|
-
* commit landed must not undo that commit. The head only ever grows: a head this client allocated
|
|
12
|
-
* (`applyReceipt`) stays until a fold catches up with it.
|
|
13
|
-
*/
|
|
14
|
-
declare const putCard: (store: StateResource<Workcard>, card: Workcard) => Promise<Workcard>;
|
|
15
|
-
declare const putLink: (store: StateResource<Relationship>, link: Relationship) => Promise<void>;
|
|
16
|
-
/** Drop a card, whatever lives under it when it is a project, and every link touching any of them. */
|
|
17
|
-
declare const dropCard: (stores: PlanningStores, id: string, project: boolean) => Promise<void>;
|
|
18
|
-
/** Record what is known about a transition, merged over what was known before. */
|
|
19
|
-
declare const putCommit: (store: StateResource<PlanningCommitRecord>, record: Partial<PlanningCommitRecord> & {
|
|
20
|
-
id: string;
|
|
21
|
-
state: CommitState;
|
|
22
|
-
}) => Promise<PlanningCommitRecord>;
|
|
23
|
-
declare const clean: <T extends object>(record: T) => T;
|
|
24
|
-
declare const idsOf: <T extends ResourceRecord>(records: T[]) => string[];
|
|
25
|
-
export declare const createRecordHelper: () => {
|
|
26
|
-
sameRecord: typeof sameRecord;
|
|
27
|
-
putCard: typeof putCard;
|
|
28
|
-
putLink: typeof putLink;
|
|
29
|
-
dropCard: typeof dropCard;
|
|
30
|
-
putCommit: typeof putCommit;
|
|
31
|
-
clean: typeof clean;
|
|
32
|
-
idsOf: typeof idsOf;
|
|
33
|
-
};
|
|
34
|
-
export declare const recordHelper: {
|
|
35
|
-
sameRecord: typeof sameRecord;
|
|
36
|
-
putCard: typeof putCard;
|
|
37
|
-
putLink: typeof putLink;
|
|
38
|
-
dropCard: typeof dropCard;
|
|
39
|
-
putCommit: typeof putCommit;
|
|
40
|
-
clean: typeof clean;
|
|
41
|
-
idsOf: typeof idsOf;
|
|
42
|
-
};
|
|
43
|
-
export {};
|
|
1
|
+
import type { RecordUtils } from './record/types.js';
|
|
2
|
+
export declare const createRecordUtils: () => RecordUtils;
|
|
3
|
+
export declare const recordUtils: RecordUtils;
|
|
44
4
|
//# sourceMappingURL=record.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/utils/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/utils/record.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,eAAO,MAAM,iBAAiB,QAAO,WA+EpC,CAAA;AAED,eAAO,MAAM,WAAW,aAAsB,CAAA"}
|
package/build/utils/record.js
CHANGED
|
@@ -1,74 +1,66 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
export const createRecordUtils = () => {
|
|
2
|
+
const stable = (value) => {
|
|
3
|
+
if (Array.isArray(value)) {
|
|
4
|
+
return value.map(stable);
|
|
5
|
+
}
|
|
6
|
+
if (value != null && typeof value === 'object') {
|
|
7
|
+
return Object.fromEntries(Object.entries(value)
|
|
8
|
+
.filter(([, entry]) => entry !== undefined)
|
|
9
|
+
.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
10
|
+
.map(([key, entry]) => [key, stable(entry)]));
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
const sameRecord = (left, right) => JSON.stringify(stable(left)) === JSON.stringify(stable(right));
|
|
15
|
+
const putCard = async (store, card) => {
|
|
16
|
+
if (card.id == null) {
|
|
17
|
+
return card;
|
|
18
|
+
}
|
|
19
|
+
const known = await store.load(card.id);
|
|
20
|
+
if (known != null && (known.seq ?? 0) > (card.seq ?? 0)) {
|
|
21
|
+
return known;
|
|
22
|
+
}
|
|
23
|
+
const head = Math.max(card.head ?? card.seq ?? 0, known?.head ?? 0);
|
|
24
|
+
const next = head > (card.head ?? card.seq ?? 0) ? { ...card, head } : card;
|
|
25
|
+
if (known != null && sameRecord(known, next)) {
|
|
26
|
+
return known;
|
|
27
|
+
}
|
|
28
|
+
await store.save(next);
|
|
29
|
+
return next;
|
|
30
|
+
};
|
|
31
|
+
const putLink = async (store, link) => {
|
|
32
|
+
if (link.id == null) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const known = await store.load(link.id);
|
|
36
|
+
if (known != null && sameRecord(known, link)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await store.save(link);
|
|
40
|
+
};
|
|
41
|
+
const dropCard = async (stores, id, project) => {
|
|
42
|
+
const ids = [id];
|
|
43
|
+
if (project) {
|
|
44
|
+
const children = await stores.cards.list({ parents: { $contains: [id] } });
|
|
45
|
+
ids.push(...children.items.map(child => child.id).filter((child) => child != null));
|
|
46
|
+
}
|
|
47
|
+
await stores.cards.purge({ id: { $in: ids } });
|
|
48
|
+
if (await stores.links.count({ $or: [{ from: { $in: ids } }, { to: { $in: ids } }] }) > 0) {
|
|
49
|
+
await stores.links.purge({ $or: [{ from: { $in: ids } }, { to: { $in: ids } }] });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const putCommit = async (store, record) => {
|
|
53
|
+
const known = await store.load(record.id);
|
|
54
|
+
const next = { ...known, ...clean(record) };
|
|
55
|
+
if (known != null && sameRecord(known, next)) {
|
|
56
|
+
return known;
|
|
57
|
+
}
|
|
58
|
+
await store.save(next);
|
|
59
|
+
return next;
|
|
60
|
+
};
|
|
61
|
+
const clean = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
62
|
+
const idsOf = (records) => records.map(record => record.id).filter((id) => id != null);
|
|
63
|
+
return { sameRecord, putCard, putLink, dropCard, putCommit, clean, idsOf };
|
|
12
64
|
};
|
|
13
|
-
|
|
14
|
-
const sameRecord = (left, right) => JSON.stringify(stable(left)) === JSON.stringify(stable(right));
|
|
15
|
-
/**
|
|
16
|
-
* Write one card unless the store already holds a NEWER fold of it.
|
|
17
|
-
*
|
|
18
|
-
* `seq` is the last folded transition, so a lower one is an older picture — a list fetched before a
|
|
19
|
-
* commit landed must not undo that commit. The head only ever grows: a head this client allocated
|
|
20
|
-
* (`applyReceipt`) stays until a fold catches up with it.
|
|
21
|
-
*/
|
|
22
|
-
const putCard = async (store, card) => {
|
|
23
|
-
if (card.id == null) {
|
|
24
|
-
return card;
|
|
25
|
-
}
|
|
26
|
-
const known = await store.load(card.id);
|
|
27
|
-
if (known != null && (known.seq ?? 0) > (card.seq ?? 0)) {
|
|
28
|
-
return known;
|
|
29
|
-
}
|
|
30
|
-
const head = Math.max(card.head ?? card.seq ?? 0, known?.head ?? 0);
|
|
31
|
-
const next = head > (card.head ?? card.seq ?? 0) ? { ...card, head } : card;
|
|
32
|
-
if (known != null && sameRecord(known, next)) {
|
|
33
|
-
return known;
|
|
34
|
-
}
|
|
35
|
-
await store.save(next);
|
|
36
|
-
return next;
|
|
37
|
-
};
|
|
38
|
-
const putLink = async (store, link) => {
|
|
39
|
-
if (link.id == null) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const known = await store.load(link.id);
|
|
43
|
-
if (known != null && sameRecord(known, link)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
await store.save(link);
|
|
47
|
-
};
|
|
48
|
-
/** Drop a card, whatever lives under it when it is a project, and every link touching any of them. */
|
|
49
|
-
const dropCard = async (stores, id, project) => {
|
|
50
|
-
const ids = [id];
|
|
51
|
-
if (project) {
|
|
52
|
-
const children = await stores.cards.list({ parents: { $contains: [id] } });
|
|
53
|
-
ids.push(...children.items.map(child => child.id).filter((child) => child != null));
|
|
54
|
-
}
|
|
55
|
-
await stores.cards.purge({ id: { $in: ids } });
|
|
56
|
-
if (await stores.links.count({ $or: [{ from: { $in: ids } }, { to: { $in: ids } }] }) > 0) {
|
|
57
|
-
await stores.links.purge({ $or: [{ from: { $in: ids } }, { to: { $in: ids } }] });
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
/** Record what is known about a transition, merged over what was known before. */
|
|
61
|
-
const putCommit = async (store, record) => {
|
|
62
|
-
const known = await store.load(record.id);
|
|
63
|
-
const next = { ...known, ...clean(record) };
|
|
64
|
-
if (known != null && sameRecord(known, next)) {
|
|
65
|
-
return known;
|
|
66
|
-
}
|
|
67
|
-
await store.save(next);
|
|
68
|
-
return next;
|
|
69
|
-
};
|
|
70
|
-
const clean = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
71
|
-
const idsOf = (records) => records.map(record => record.id).filter((id) => id != null);
|
|
72
|
-
export const createRecordHelper = () => ({ sameRecord, putCard, putLink, dropCard, putCommit, clean, idsOf });
|
|
73
|
-
export const recordHelper = createRecordHelper();
|
|
65
|
+
export const recordUtils = createRecordUtils();
|
|
74
66
|
//# sourceMappingURL=record.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/utils/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/utils/record.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/client-planning",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.23",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "The client half of OwlMeans planning — the remote planning facade, the state mirror and its commit fold, and the subscribe-then-poll commit wait. React-free: it runs in a browser and in a Node client alike.",
|
|
6
6
|
"type": "module",
|
|
@@ -23,18 +23,18 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
27
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
31
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
32
|
-
"@owlmeans/planning": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/resource": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/log": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/socket": "^0.1.18-rc.
|
|
37
|
-
"@owlmeans/state": "^0.1.18-rc.
|
|
26
|
+
"@owlmeans/auth": "^0.1.18-rc.40",
|
|
27
|
+
"@owlmeans/client-context": "^0.1.18-rc.49",
|
|
28
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.48",
|
|
29
|
+
"@owlmeans/context": "^0.1.18-rc.34",
|
|
30
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.41",
|
|
31
|
+
"@owlmeans/error": "^0.1.18-rc.38",
|
|
32
|
+
"@owlmeans/planning": "^0.1.18-rc.20",
|
|
33
|
+
"@owlmeans/resource": "^0.1.18-rc.40",
|
|
34
|
+
"@owlmeans/log": "^0.1.18-rc.2",
|
|
35
|
+
"@owlmeans/route": "^0.1.18-rc.35",
|
|
36
|
+
"@owlmeans/socket": "^0.1.18-rc.41",
|
|
37
|
+
"@owlmeans/state": "^0.1.18-rc.39"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"ajv": "*",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@owlmeans/dep-config": "workspace:*",
|
|
45
|
-
"@owlmeans/server-planning": "^0.1.18-rc.
|
|
46
|
-
"@owlmeans/test": "^0.1.18-rc.
|
|
45
|
+
"@owlmeans/server-planning": "^0.1.18-rc.23",
|
|
46
|
+
"@owlmeans/test": "^0.1.18-rc.35",
|
|
47
47
|
"@types/bun": "^1.4.2",
|
|
48
48
|
"ajv": "^8.20.0",
|
|
49
49
|
"ajv-formats": "^3.0.1",
|
package/src/client.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { PlanningFacade, PlanningPlugin, PlanningSchemaRegistry } from '@ow
|
|
|
8
8
|
import { makeRemoteCommitSource } from './commits.js'
|
|
9
9
|
import { makeRemoteDefinitions } from './definitions.js'
|
|
10
10
|
import { makeRemoteFacade } from './facade.js'
|
|
11
|
-
import {
|
|
11
|
+
import { planningContextOf } from './helper.js'
|
|
12
12
|
import type {
|
|
13
13
|
Config, Context, PlanningClientOptions, PlanningClientService, WithPlanningClient,
|
|
14
14
|
} from './types.js'
|
|
@@ -27,7 +27,7 @@ export const makePlanningClientService = <C extends Config, T extends Context<C>
|
|
|
27
27
|
): PlanningClientService => {
|
|
28
28
|
const registry = makeSchemaRegistry()
|
|
29
29
|
const plugins: PlanningPlugin[] = []
|
|
30
|
-
const stores = () =>
|
|
30
|
+
const stores = () => planningContextOf(context).stores()
|
|
31
31
|
|
|
32
32
|
const commits = makeRemoteCommitSource(context, options.protocols, {
|
|
33
33
|
socket: options.socket, poll: options.poll, timeout: options.timeout, stores,
|
package/src/commits.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Connection } from '@owlmeans/socket'
|
|
|
5
5
|
import { EARLY_POLL_LADDER, EARLY_POLL_MS, LONG_POLL_GRACE } from './consts.js'
|
|
6
6
|
import type { RemoteCommitSource, RemoteCommitSourceOptions } from './types.js'
|
|
7
7
|
import type { Subscription } from './types.local.js'
|
|
8
|
-
import {
|
|
8
|
+
import { recordUtils } from './utils/record.js'
|
|
9
9
|
|
|
10
10
|
const log = logger('client-planning')
|
|
11
11
|
|
|
@@ -115,16 +115,16 @@ export const makeRemoteCommitSource = <C extends BasicConfig, T extends BasicCon
|
|
|
115
115
|
return
|
|
116
116
|
}
|
|
117
117
|
const known = await stores.commits.load(transition)
|
|
118
|
-
await
|
|
118
|
+
await recordUtils.putCommit(stores.commits, {
|
|
119
119
|
id: transition, state: settled.state, at: settled.at, error: settled.error,
|
|
120
120
|
})
|
|
121
121
|
if (settled.state !== CommitState.Committed) {
|
|
122
122
|
return
|
|
123
123
|
}
|
|
124
124
|
if (settled.card != null) {
|
|
125
|
-
await
|
|
125
|
+
await recordUtils.putCard(stores.cards, settled.card)
|
|
126
126
|
} else if (settled.card === null && known?.card != null) {
|
|
127
|
-
await
|
|
127
|
+
await recordUtils.dropCard(stores, known.card, known.kind === WorkcardKind.Project)
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
|
package/src/consts.ts
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import type { Relationship, Workcard } from '@owlmeans/planning'
|
|
2
|
+
import type { StateAlias } from '@owlmeans/state'
|
|
3
|
+
import type { PlanningCommitRecord, PlanningStoreAliases } from './types.js'
|
|
4
|
+
|
|
5
|
+
// A state alias is the string itself at runtime (`stateAlias()` only attaches the record type), so
|
|
6
|
+
// each handle is declared as the constant it is.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The ONE card store. Projects, cards and specifications share an id space on the server, so they
|
|
10
|
+
* share it here too — a store per kind would let a list reload of one kind drop another's rows.
|
|
11
|
+
*/
|
|
12
|
+
export const CARDS = 'planning-card-state' as StateAlias<Workcard>
|
|
13
|
+
|
|
14
|
+
export const LINKS = 'planning-link-state' as StateAlias<Relationship>
|
|
15
|
+
|
|
16
|
+
/** What the client has learned about each transition it wrote or saw, keyed by transition id. */
|
|
17
|
+
export const COMMITS = 'planning-commit-state' as StateAlias<PlanningCommitRecord>
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_STORE_ALIASES: Readonly<PlanningStoreAliases> = Object.freeze({
|
|
20
|
+
cards: CARDS,
|
|
21
|
+
links: LINKS,
|
|
22
|
+
commits: COMMITS,
|
|
23
|
+
})
|
|
1
24
|
|
|
2
25
|
/**
|
|
3
26
|
* How much longer than its own `wait` a long poll's HTTP deadline is, in milliseconds — a held
|
package/src/definitions.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
2
|
-
import { PlanningUnsupported, SchemaWriteMode,
|
|
2
|
+
import { PlanningUnsupported, SchemaWriteMode, scopedSchemaHelper } from '@owlmeans/planning'
|
|
3
3
|
import type {
|
|
4
4
|
PlanningProtocols, SchemaDefineReply, SchemaDefineRequest, ScopedSchemaRegistry,
|
|
5
5
|
} from '@owlmeans/planning'
|
|
@@ -34,7 +34,7 @@ export const makeRemoteDefinitions = <C extends BasicConfig, T extends BasicCont
|
|
|
34
34
|
const key = project ?? ''
|
|
35
35
|
let entry = cache.get(key)
|
|
36
36
|
if (entry == null) {
|
|
37
|
-
const loading = (async () => scopedRegistryOf(await context.entrypoint(protocols.schema.list).call({
|
|
37
|
+
const loading = (async () => scopedSchemaHelper.scopedRegistryOf(await context.entrypoint(protocols.schema.list).call({
|
|
38
38
|
query: project != null ? { project } : {}, timeout: opts.timeout,
|
|
39
39
|
})))()
|
|
40
40
|
entry = loading
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CommitEvent, PlanningFacade, TransitionReceiptView, Workcard } from '@owlmeans/planning'
|
|
2
|
+
|
|
3
|
+
/** What one set of mirror stores learns from the server: commit frames, receipts and cards read. */
|
|
4
|
+
export interface PlanningMirror {
|
|
5
|
+
/**
|
|
6
|
+
* Fold one commit frame into the mirror.
|
|
7
|
+
*
|
|
8
|
+
* - An OLDER `seq` never overwrites a newer record — frames and list answers race.
|
|
9
|
+
* - A committed `delete` REMOVES the row and every link touching it (and, for a project, the rows
|
|
10
|
+
* under it — the server purged them in the same fold).
|
|
11
|
+
* - A frame for an id the store never saw still writes a row: a card created in another tab
|
|
12
|
+
* appears here without waiting for the next list.
|
|
13
|
+
* - A `failed` commit leaves the card alone; the reason is recorded in the commit store.
|
|
14
|
+
*
|
|
15
|
+
* A frame from a cross-process bus carries ids only. When `record` is absent the card is re-read
|
|
16
|
+
* through `facade`; without one there is nothing to write but the commit itself.
|
|
17
|
+
*/
|
|
18
|
+
applyCommitEvent: (event: CommitEvent, facade?: PlanningFacade) => Promise<void>
|
|
19
|
+
/**
|
|
20
|
+
* Mark a receipt in the mirror the moment the server answers it.
|
|
21
|
+
*
|
|
22
|
+
* A pending transition raises the card's `head` to its `seq`, so `head > seq` — the model's
|
|
23
|
+
* `pending()` — is true before any frame arrives. A receipt that already carries the committed
|
|
24
|
+
* card writes it; a committed delete drops the row.
|
|
25
|
+
*/
|
|
26
|
+
applyReceipt: (receipt: TransitionReceiptView) => Promise<void>
|
|
27
|
+
/** Write cards the caller read on its own, under the same newer-fold-wins rule as a commit. */
|
|
28
|
+
applyCards: (cards: Workcard[]) => Promise<void>
|
|
29
|
+
}
|