@owlmeans/client-planning 0.1.18-rc.3 → 0.1.18-rc.31

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.
Files changed (139) hide show
  1. package/README.md +94 -11
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client-planning/SKILL.md +152 -36
  4. package/build/client.d.ts.map +1 -1
  5. package/build/client.js +50 -8
  6. package/build/client.js.map +1 -1
  7. package/build/commits.d.ts +2 -13
  8. package/build/commits.d.ts.map +1 -1
  9. package/build/commits.js +190 -132
  10. package/build/commits.js.map +1 -1
  11. package/build/consts.d.ts +4 -0
  12. package/build/consts.d.ts.map +1 -1
  13. package/build/consts.js +10 -4
  14. package/build/consts.js.map +1 -1
  15. package/build/definitions.d.ts +13 -0
  16. package/build/definitions.d.ts.map +1 -0
  17. package/build/definitions.js +70 -0
  18. package/build/definitions.js.map +1 -0
  19. package/build/events/types.d.ts +34 -0
  20. package/build/events/types.d.ts.map +1 -0
  21. package/build/events/types.js +2 -0
  22. package/build/events/types.js.map +1 -0
  23. package/build/events.d.ts +10 -21
  24. package/build/events.d.ts.map +1 -1
  25. package/build/events.js +104 -102
  26. package/build/events.js.map +1 -1
  27. package/build/facade.d.ts.map +1 -1
  28. package/build/facade.js +86 -49
  29. package/build/facade.js.map +1 -1
  30. package/build/feed.d.ts +4 -4
  31. package/build/feed.d.ts.map +1 -1
  32. package/build/feed.js +50 -27
  33. package/build/feed.js.map +1 -1
  34. package/build/helper/types.d.ts +15 -0
  35. package/build/helper/types.d.ts.map +1 -0
  36. package/build/helper/types.js +2 -0
  37. package/build/helper/types.js.map +1 -0
  38. package/build/helper.d.ts +7 -7
  39. package/build/helper.d.ts.map +1 -1
  40. package/build/helper.js +16 -8
  41. package/build/helper.js.map +1 -1
  42. package/build/index.d.ts +11 -0
  43. package/build/index.d.ts.map +1 -1
  44. package/build/index.js +6 -0
  45. package/build/index.js.map +1 -1
  46. package/build/lifecycle/types.d.ts +27 -0
  47. package/build/lifecycle/types.d.ts.map +1 -0
  48. package/build/lifecycle/types.js +2 -0
  49. package/build/lifecycle/types.js.map +1 -0
  50. package/build/lifecycle.d.ts +3 -0
  51. package/build/lifecycle.d.ts.map +1 -0
  52. package/build/lifecycle.js +157 -0
  53. package/build/lifecycle.js.map +1 -0
  54. package/build/resource-feed.d.ts +5 -0
  55. package/build/resource-feed.d.ts.map +1 -0
  56. package/build/resource-feed.js +97 -0
  57. package/build/resource-feed.js.map +1 -0
  58. package/build/resources.d.ts +6 -0
  59. package/build/resources.d.ts.map +1 -0
  60. package/build/resources.js +93 -0
  61. package/build/resources.js.map +1 -0
  62. package/build/stores.d.ts +4 -11
  63. package/build/stores.d.ts.map +1 -1
  64. package/build/stores.js +11 -35
  65. package/build/stores.js.map +1 -1
  66. package/build/sync/types.d.ts +18 -0
  67. package/build/sync/types.d.ts.map +1 -0
  68. package/build/sync/types.js +2 -0
  69. package/build/sync/types.js.map +1 -0
  70. package/build/sync.d.ts +4 -0
  71. package/build/sync.d.ts.map +1 -0
  72. package/build/sync.js +26 -0
  73. package/build/sync.js.map +1 -0
  74. package/build/types.d.ts +48 -3
  75. package/build/types.d.ts.map +1 -1
  76. package/build/types.local.d.ts +13 -0
  77. package/build/types.local.d.ts.map +1 -0
  78. package/build/types.local.js +2 -0
  79. package/build/types.local.js.map +1 -0
  80. package/build/utils/record/types.d.ts +28 -0
  81. package/build/utils/record/types.d.ts.map +1 -0
  82. package/build/utils/record/types.js +2 -0
  83. package/build/utils/record/types.js.map +1 -0
  84. package/build/utils/record.d.ts +3 -24
  85. package/build/utils/record.d.ts.map +1 -1
  86. package/build/utils/record.js +64 -70
  87. package/build/utils/record.js.map +1 -1
  88. package/build/validation/consts.d.ts +16 -0
  89. package/build/validation/consts.d.ts.map +1 -0
  90. package/build/validation/consts.js +42 -0
  91. package/build/validation/consts.js.map +1 -0
  92. package/build/validation/types.d.ts +17 -0
  93. package/build/validation/types.d.ts.map +1 -0
  94. package/build/validation/types.js +2 -0
  95. package/build/validation/types.js.map +1 -0
  96. package/build/validation.d.ts +10 -0
  97. package/build/validation.d.ts.map +1 -0
  98. package/build/validation.js +172 -0
  99. package/build/validation.js.map +1 -0
  100. package/package.json +18 -16
  101. package/src/client.ts +42 -8
  102. package/src/commits.ts +152 -184
  103. package/src/consts.ts +12 -4
  104. package/src/definitions.ts +93 -0
  105. package/src/events/types.ts +35 -0
  106. package/src/events.ts +118 -108
  107. package/src/facade.ts +87 -55
  108. package/src/feed.ts +47 -28
  109. package/src/helper/types.ts +15 -0
  110. package/src/helper.ts +27 -10
  111. package/src/index.ts +12 -0
  112. package/src/lifecycle/types.ts +28 -0
  113. package/src/lifecycle.ts +123 -0
  114. package/src/resource-feed.ts +79 -0
  115. package/src/resources.ts +84 -0
  116. package/src/stores.ts +11 -40
  117. package/src/sync/types.ts +18 -0
  118. package/src/sync.ts +40 -0
  119. package/src/types.local.ts +14 -0
  120. package/src/types.ts +53 -5
  121. package/src/utils/record/types.ts +27 -0
  122. package/src/utils/record.ts +68 -71
  123. package/src/validation/consts.ts +41 -0
  124. package/src/validation/types.ts +18 -0
  125. package/src/validation.ts +141 -0
  126. package/tests/context.ts +46 -15
  127. package/tests/definitions.spec.ts +74 -0
  128. package/tests/events.spec.ts +13 -9
  129. package/tests/facade.spec.ts +2 -1
  130. package/tests/lifecycle/context.ts +29 -0
  131. package/tests/lifecycle-close.spec.ts +103 -0
  132. package/tests/lifecycle-commits.spec.ts +92 -0
  133. package/tests/lifecycle-definitions.spec.ts +28 -0
  134. package/tests/lifecycle-feeds.spec.ts +92 -0
  135. package/tests/lifecycle-sockets.spec.ts +76 -0
  136. package/tests/replies.spec.ts +81 -0
  137. package/tests/resources.spec.ts +116 -0
  138. package/tests/stores.spec.ts +13 -11
  139. package/tests/validation.spec.ts +127 -0
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @owlmeans/client-planning
2
2
 
3
3
  The client half of OwlMeans planning: a `PlanningFacade` over the protocol tree a server mounted
4
- with [`@owlmeans/server-planning`](../server-planning), an optional state mirror of cards, links and
5
- commits, and a commit wait that subscribes first and then long-polls. It is React-free, so the same
4
+ with [`@owlmeans/server-planning`](../server-planning), optional state mirrors of cards, links,
5
+ commits, assignees, teams, comments and mentions, and a commit wait that subscribes first and then long-polls. It is React-free, so the same
6
6
  code runs in a browser app and in a Node client. Use `@owlmeans/server-planning` instead wherever the
7
7
  planning service runs in-process.
8
8
 
9
9
  ## Installation
10
10
 
11
11
  ```bash
12
- bun add @owlmeans/client-planning@^0.1.18-rc.3
12
+ bun add @owlmeans/client-planning@^0.1.18-rc.31
13
13
  ```
14
14
 
15
15
  ## Concepts
@@ -17,14 +17,46 @@ bun add @owlmeans/client-planning@^0.1.18-rc.3
17
17
  - **Remote facade** — the same `PlanningFacade` interface the server answers; every method is one
18
18
  entrypoint call, and `modelOf(record, facade)` models behave identically on both sides.
19
19
  - **One id space** — projects, cards and specifications share ONE card store; a list reaches it
20
- through `syncCards(store, items, where)`, never `replace()`.
20
+ through `syncHelper.syncCards(store, items, where)`, never `replace()`.
21
21
  - **Commit** — `execute()` returns a receipt for a pending transition; `receipt.committed()` /
22
22
  `commits.wait()` resolve with the folded card, or throw `CommitTimeout` / `CommitFailed`.
23
23
  - **Socket seam** — the commit socket is opened by an injected `PlanningSocketOpener`; without one
24
24
  the client long-polls.
25
- - **`head > seq`** — a card with a transition allocated and not yet folded; `applyReceipt` marks it
25
+ - **`head > seq`** — a card with a transition allocated and not yet folded; the mirror's `applyReceipt` marks it
26
26
  the moment the server answers.
27
27
 
28
+ ## Forms under a strict Content Security Policy
29
+
30
+ Use the exact resolved card or assignee type's field schema:
31
+
32
+ ```typescript
33
+ import { makePlanningFieldValidator } from '@owlmeans/client-planning'
34
+
35
+ const form = makePlanningFieldValidator(resolvedType.fields)
36
+ const checked = form.validateFields(fields)
37
+ if (checked.valid) {
38
+ // Submit this same fields object through the stock planning facade.
39
+ }
40
+ ```
41
+
42
+ The runtime interpreter supports dynamic organization and project definitions without `eval` or
43
+ `new Function`. It preserves the schema and payload, nested objects, required fields, enum/const,
44
+ dates, references, compositions and AJV nullable semantics; string formats use native
45
+ `ajv-formats` full-mode predicates. Errors contain field `path`, `keyword`, `schemaPath` and a
46
+ bounded message without submitted values. The server's authoritative AJV validates every write.
47
+
48
+ The schema contract is Draft 7 plus `$defs` and `nullable`, with native AJV reference-sibling
49
+ semantics. References must resolve within the supplied schema. Malformed schemas, missing
50
+ references, same-instance reference cycles, other drafts/custom validation keywords, unknown or
51
+ numeric formats, format-limit extensions and `multipleOf` throw `SchemaInvalid` at preparation.
52
+ The interpreter's floating tolerance and native AJV's multiple semantics differ, so that keyword
53
+ is explicitly refused. JSON fields cannot contain dot/dollar keys or non-JSON values. Standard
54
+ annotations and `x-` metadata remain annotations. At most 64 validation errors are returned.
55
+
56
+ Surface preparation errors and keep save disabled. Do not remove constraints, choose another
57
+ schema or relax CSP. Registry `validator` / `assigneeValidator` compile with native AJV and need
58
+ code generation; browser forms under strict CSP use this API instead.
59
+
28
60
  ## Usage
29
61
 
30
62
  Wire the client into a context with the server's own tree:
@@ -36,9 +68,14 @@ import { appendPlanningClient, appendPlanningStores } from '@owlmeans/client-pla
36
68
  const planningProtocols = makePlanningProtocols({
37
69
  base: { alias: 'app:api:planning', path: '/planning', service: API },
38
70
  guards: DEFAULT_GUARD,
71
+ resources: true,
72
+ definitions: true,
39
73
  })
40
74
 
41
- appendPlanningClient(context, { protocols: planningProtocols, poll: 20 })
75
+ appendPlanningClient(context, {
76
+ protocols: planningProtocols, poll: 20,
77
+ scopeKey: () => `${currentEntitySlug}:${currentSessionId}`,
78
+ })
42
79
  appendPlanningStores(context)
43
80
  ```
44
81
 
@@ -77,18 +114,61 @@ Give a browser its commit socket through the host's own opener:
77
114
  ```typescript
78
115
  appendPlanningClient(context, {
79
116
  protocols: planningProtocols,
80
- socket: async (protocol, request) => await ws(context.entrypoint(protocol), request),
117
+ socket: async (protocol, request) => await socketClientHelper.ws(context.entrypoint(protocol), request),
118
+ })
119
+ ```
120
+
121
+ ## Auxiliary resources and session lifecycle
122
+
123
+ The remote facade supports the same `assignees`, `teams`, `comments` and `mentions` methods as
124
+ the server, including compare-and-set writes, canonical team membership/project attachment,
125
+ effective project assignees and mention-cache rebuilding. `definitions` supports assignee types
126
+ when the server mounts writable schemas. Relationships include endpoint resource kinds and named
127
+ inverse views; models resolve schemas through their nearest primary project.
128
+
129
+ `appendPlanningStores` registers four auxiliary mirrors beside cards, links and commits. Reads
130
+ and writes through the facade update those mirrors without replacing newer versions. A resource
131
+ feed refreshes the requested subsets through the stock API:
132
+
133
+ ```ts
134
+ import { makePlanningResourceFeed } from '@owlmeans/client-planning'
135
+
136
+ const resources = makePlanningResourceFeed(context, {
137
+ assignees: { retired: false, size: 0 }, teams: { size: 0 },
138
+ comments: { card: cardId, size: 0 }, mentions: { card: cardId, size: 0 },
139
+ refresh: 5_000,
81
140
  })
141
+ await resources.ready
142
+ // Read context.planningStores().assignees, teams, comments and mentions.
143
+ await resources.stop()
82
144
  ```
83
145
 
146
+ `scopeKey` identifies the current organization slug and authenticated session. A changed key
147
+ cancels old requests and feeds, drains local mutations and clears mirrors; capture a new facade
148
+ and feed for the new session. `await context.planning().close()` releases feeds, subscriptions,
149
+ commit waits and sockets, including carriers that open late. Stopped requests cannot repopulate
150
+ cleared mirrors, even when a transport ignores cancellation.
151
+
152
+ The server derives authorization scope from credentials. Wire records omit native organization
153
+ `entityId`; the client hydrates empty advisory metadata for the shared record shape. Never use
154
+ that placeholder to authorize a request, and never send an organization record id on the wire.
155
+
84
156
  ## API
85
157
 
86
158
  - `appendPlanningClient(context, options)`, `makePlanningClientService(context, options)`
87
159
  - `makeRemoteFacade(context, protocols, scope, opts)`, `makeRemoteCommitSource(context, protocols, opts?)`
88
- - `appendPlanningStores(context, aliases?)`, `planningStoresOf(context)`, `syncCards`, `syncLinks`
89
- - `applyCommitEvent(stores, event, facade?)`, `applyReceipt(stores, view)`, `applyCards(stores, cards)`
160
+ - `appendPlanningStores(context, aliases?)`
161
+ - `syncHelper` — `.syncCards(store, items, where?, opts?)`, `.syncLinks(store, items, where?, opts?)`
162
+ - `planningMirrorOf(stores)` (`makePlanningMirror`) — `.applyCommitEvent(event, facade?)`,
163
+ `.applyReceipt(view)`, `.applyCards(cards)`
90
164
  - `makePlanningFeed(context, opts?)`
91
- - `planningOf(context, scope?)`, `planningModelOf(context, card, scope?)`
165
+ - `makePlanningResourceFeed(context, opts?)`, `PlanningResourceFeedOptions`
166
+ - `makePlanningFieldValidator(schema)`, `PlanningFieldValidator`, `PlanningFieldValidation`, `PlanningFieldError`
167
+ - `makePlanningClientLifecycle`, `PlanningClientLifecycle`; service `close()` and `scopeKey`
168
+ - `planningContextOf(context)` (`makePlanningContextHelper`) — `.facade(scope?)`, `.model(card, scope?)`,
169
+ `.stores()`
170
+ - The former plain functions (`syncCards`, `applyCards`, `planningOf`, …) remain as deprecated
171
+ delegates.
92
172
  - `CARDS`, `LINKS`, `COMMITS`, `DEFAULT_STORE_ALIASES`, `LONG_POLL_GRACE`, `EARLY_POLL_LADDER`, `EARLY_POLL_MS`
93
173
  - Types: `PlanningClientOptions`, `PlanningClientService`, `WithPlanningClient`,
94
174
  `PlanningSocketOpener`, `RemoteCommitSource`, `RemoteCommitSourceOptions`, `RemoteFacadeOptions`,
@@ -103,6 +183,9 @@ appendPlanningClient(context, {
103
183
  - `CommitTimeout` leaves the transition pending — re-wait or re-read, do not re-execute without a `key`.
104
184
  - A client refuses `store()`, `committed()` and plugins carrying hooks with `PlanningUnsupported`.
105
185
  - Mount one feed per screen; every other reader uses the store.
186
+ - Auxiliary updates/removals require `{ version: record.version }`. Their `fields` replace the
187
+ previous object; card `changes.fields` merge.
188
+ - After a scope/session change, acquire new facades and feeds. Old ones are deliberately stale.
106
189
 
107
190
  ## Related packages
108
191
 
@@ -119,7 +202,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
119
202
  your project's skill store (`.agents/skills/`):
120
203
 
121
204
  ```sh
122
- npx @owlmeans/agent-skills@^0.1.18-rc.31
205
+ npx @owlmeans/agent-skills@^0.1.18-rc.53
123
206
  ```
124
207
 
125
208
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/client-planning",
4
- "version": "0.1.18-rc.3",
5
- "generatedAt": "2026-09-21T21:54:22.962Z",
4
+ "version": "0.1.18-rc.31",
5
+ "generatedAt": "2026-10-10T22:28:00.510Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: client-planning
3
- description: How to use @owlmeans/client-planning — appendPlanningClient for the remote planning facade, appendPlanningStores and applyCommitEvent for the state mirror, makePlanningFeed, commits.wait (subscribe then long-poll), and the models that are the same objects the server uses. Auto-invoked when reading planning data in a browser or a Node client, mounting the commit feed, or wiring optimistic transitions.
3
+ description: How to use @owlmeans/client-planning — appendPlanningClient for the remote planning facade, appendPlanningStores and planningMirrorOf for the state mirror, makePlanningFeed, commits.wait (subscribe then long-poll), and the models that are the same objects the server uses. Auto-invoked when reading planning data in a browser or a Node client, mounting the commit feed, or wiring optimistic transitions.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/client-planning
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client-planning": "^0.1.18-rc.3"` in `dependencies`
11
+ **Install:** `"@owlmeans/client-planning": "^0.1.18-rc.31"` in `dependencies`
12
12
 
13
13
  The client half of OwlMeans planning. It answers the `PlanningFacade` interface of
14
14
  `@owlmeans/planning` over the protocol tree a server mounted with `@owlmeans/server-planning`, keeps
@@ -24,13 +24,15 @@ CLI). React hooks over the mirror are `useStoreModel` / `useStoreList` from `@ow
24
24
  | `makePlanningClientService(context, options)` | The service itself, for a host that registers it on its own |
25
25
  | `makeRemoteFacade(context, protocols, scope, opts)` | One facade — every method is one entrypoint call |
26
26
  | `makeRemoteCommitSource(context, protocols, opts?)` | `status` / `subscribe` / `wait` over `commit.get` and `commit.events` |
27
- | `appendPlanningStores(context, aliases?)` / `planningStoresOf(context)` | The state mirror, and a lookup that answers `null` without one |
28
- | `syncCards(store, items, where?, opts?)` / `syncLinks(...)` | Make the mirror agree with a list WITHIN a scope |
29
- | `applyCommitEvent(stores, event, facade?)` / `applyReceipt(stores, view)` / `applyCards(stores, cards)` | The folds |
27
+ | `makeRemoteDefinitions(context, protocols, opts?)` | Data-defined types and flows over a tree declared with `definitions: true` |
28
+ | `appendPlanningStores(context, aliases?)` / `planningContextOf(context).stores()` | The state mirror, and a lookup that answers `null` without one |
29
+ | `syncHelper.syncCards(store, items, where?, opts?)` / `syncHelper.syncLinks(...)` | Make the mirror agree with a list WITHIN a scope |
30
+ | `planningMirrorOf(stores).applyCommitEvent(event, facade?)` / `.applyReceipt(view)` / `.applyCards(cards)` | The folds |
30
31
  | `makePlanningFeed(context, opts?)` | Subscribe, seed, fold, refresh — `{ connected, seeded, error, ready, refresh, stop }` |
31
- | `planningOf(context, scope?)` / `planningModelOf(context, card, scope?)` | The facade / a model with the schemas loaded |
32
+ | `planningContextOf(context).facade(scope?)` / `.model(card, scope?)` | The facade / a model with the schemas loaded |
33
+ | `makePlanningFieldValidator(schema)` | CSP-safe validation of an exact served field schema; `{ validateFields }` |
32
34
  | `CARDS`, `LINKS`, `COMMITS` | Store aliases (`planning-card-state`, `planning-link-state`, `planning-commit-state`) |
33
- | Types | `PlanningClientOptions`, `PlanningClientService`, `WithPlanningClient`, `PlanningSocketOpener`, `RemoteCommitSource`, `PlanningStores`, `PlanningStoreAliases`, `WithPlanningStores`, `PlanningCommitRecord`, `PlanningFeed`, `PlanningFeedOptions`, `PlanningFeedState`, `SyncOptions` |
35
+ | Types | `PlanningClientOptions`, `PlanningClientService`, `WithPlanningClient`, `PlanningSocketOpener`, `RemoteCommitSource`, `RemoteDefinitions`, `RemoteDefinitionsOptions`, `PlanningStores`, `PlanningStoreAliases`, `WithPlanningStores`, `PlanningCommitRecord`, `PlanningFeed`, `PlanningFeedOptions`, `PlanningFeedState`, `SyncOptions`, `PlanningContextHelper`, `PlanningMirror`, `SyncHelper` |
34
36
 
35
37
  ## Wiring
36
38
 
@@ -59,8 +61,8 @@ appendPlanningStores(context) // a browser mirror; a Node client usually
59
61
  - `bind: false` when the host already bound the tree.
60
62
  - The service answers under `PLANNING_SERVICE`, the alias the server's service uses, so
61
63
  `context.planning().for(scope)` is the same call in a handler and in a screen. The scope is
62
- **advisory**: the server takes the entity and the actor from the credential. The execution's own
63
- `actor` is never sent.
64
+ **advisory** — it changes nothing the server decides: the server takes the entity and the actor
65
+ from the credential. The execution's own `actor` is never sent.
64
66
  - A client runs no middleware and folds nothing: `store()`, `committed()` and a plugin carrying
65
67
  `before`/`after`/`store`/`mintCode` answer `PlanningUnsupported`. `use()` accepts a schemas-only
66
68
  plugin, layered over the server's bundle.
@@ -75,20 +77,78 @@ there is swallowed — a browser that is not signed in yet cannot read it, and a
75
77
  fail or wait over it. `model()` and `service.loadSchemas()` load it on first use regardless, and a
76
78
  failed load is retried by the next caller. `schemas: false` skips the background load.
77
79
 
80
+ ### Data-defined types and flows
81
+
82
+ A tree declared with `definitions: true` (the server's store holds data-defined schemas) gives the
83
+ service and every facade `definitions`: `registry(project?)` / `bundle(project?)` read
84
+ `schema.list?project=` and are cached per project; `putType`/`putFlow` (compare-and-set on the
85
+ declaration's `version`), `define`, `seed` and `retire` go through `schema.define` and drop the
86
+ whole cache — an organization-wide write reaches every project's layer — and make the next
87
+ `model()` reload the service's own bundle. `records` is the server's alone (`PlanningUnsupported`).
88
+ `model(card)` of a card resolves its type in its project's layer (a project's own id, a card's
89
+ nearest primary project ancestor); a specification's type is always code's. A tree without `definitions` has none of this.
90
+
91
+ ## Browser field validation under CSP
92
+
93
+ Bind `makePlanningFieldValidator(type.fields)` to the exact definition returned by the resolved
94
+ organization or project bundle. Its `validateFields(fields)` returns `{ valid, errors }`; errors
95
+ carry decoded `path`, `keyword`, `schemaPath` and a bounded value-free `message`. Construct one JSON
96
+ fields object, validate it and send that same object. No coercion, defaults or property removal
97
+ occurs, and the schema is left unchanged. Preserve nested money objects and calendar date strings.
98
+ The native server validates every write again.
99
+
100
+ The validator interprets schemas with `@cfworker/json-schema`, with no `eval`, `new Function` or
101
+ network reference loading. Do not call `makeSchemaRegistry(...).validator()` or
102
+ `.assigneeValidator()` in a browser with strict `script-src`: those remain native AJV compilers.
103
+ Static AJV standalone compilation is suitable only when the schema cannot change after building;
104
+ organization and project definitions require the runtime interpreter.
105
+
106
+ The supported contract is Draft 7's object/array/scalar, required, enum/const, ranges, string
107
+ length/pattern, dependencies, boolean, composition and conditional keywords; `$defs`, local/nested
108
+ `$id` references and AJV's `nullable` are also supported. `$ref` sibling constraints are enforced as
109
+ by native planning AJV. String formats use the same `ajv-formats` full-mode predicates, including
110
+ real calendar dates and timezone-aware times. `nullable` adds null to the declared type while
111
+ leaving enum and const constraints intact. All references must resolve inside the supplied field
112
+ schema; productive recursive schemas are supported, same-instance reference cycles are refused.
113
+ Plain JSON values and field names without dot or dollar characters are required.
114
+
115
+ Preparation throws `SchemaInvalid` for malformed schemas, unresolved references, unsupported
116
+ drafts/keywords/formats, numeric formats, `formatMinimum`/`formatMaximum` extensions and
117
+ `multipleOf`. The latter is refused because the interpreter's epsilon and native AJV's quotient
118
+ semantics disagree for fractional and very large numbers. Standard annotation keywords and `x-`
119
+ annotations do not add validation. Never discard a refused constraint, invent a substitute schema,
120
+ enable `unsafe-eval` or proceed with a ready form after preparation fails. Show the error and retain
121
+ disabled save controls. Validation reports at most 64 errors, with paths bounded to 512 characters.
122
+
123
+ ## Mounting in a target
124
+
125
+ The web half of the target mount (`planning` → Mounting in a target).
126
+
127
+ - `appendPlanningClient(context, { protocols, bind: false, schemas: false })` — the target binds
128
+ its whole api tree itself, and a signed-out visitor may read nothing, so the bundle loads on
129
+ first use — plus `appendPlanningStores(context)`. No `socket`: the target serves no commit socket,
130
+ so `commits.wait` long-polls.
131
+ - Screens read the mirror with `useStoreList` / `useStoreModel` (`@owlmeans/client`), never a
132
+ fetched array kept in component state; lists reach the store through `syncHelper.syncCards` or
133
+ `makePlanningFeed` (with `refresh`), NEVER `replace()` — the one card store holds every kind.
134
+ - Every write is `facade.execute(...)` with a `key` (a retried click or a double submit answers
135
+ the first receipt) and `{ wait: true }` where the screen shows the result.
136
+ - Never call a route with an empty id; a picker offers no archived parent (`intrinsic: closed`).
137
+
78
138
  ## Reading
79
139
 
80
140
  ```typescript
81
141
  const planning = context.planning().for()
82
142
 
83
- const stories = await planning.cards.list({ parent: projectId, type: STORY, sort: ['order'], size: 0 })
84
- const board = await planning.cards.summary([projectId])
85
- const brief = await planning.specifications.current(projectId, 'specification')
86
- const story = await planning.model(storyId)
87
- story.available().map(rule => rule.name)
143
+ const tools = await planning.cards.list({ parent: shedId, type: 'shed:tool', sort: ['order'], size: 0 })
144
+ const board = await planning.cards.summary([shedId])
145
+ const rules = await planning.specifications.current(shedId, 'house-rules')
146
+ const drill = await planning.model(drillId)
147
+ drill.available().map(rule => rule.name)
88
148
  ```
89
149
 
90
150
  - The facade takes the rich `WorkcardQuery`; it encodes the scalar wire shape itself
91
- (`encodeWorkcardQuery` and its twins). Never pass a wire shape to the facade.
151
+ (`wireHelper.encodeWorkcardQuery` and its twins). Never pass a wire shape to the facade.
92
152
  - `cards.load(id)` answers `null` for a missing card — and for another entity's card, which the
93
153
  server deliberately reports the same way.
94
154
  - `cards.count(query)` is a one-row list read for its `total`; the tree declares no count route.
@@ -102,13 +162,14 @@ story.available().map(rule => rule.name)
102
162
  share an id space on the server, so they share one store here: a store per kind lets a card list
103
163
  that reloads drop the projects beside it.
104
164
 
105
- That is also why a list reaches the store through **`syncCards(store, items, where)`, never
106
- `replace()`**: every card given is written, every card matching `where` that the list does not name
107
- is dropped, and everything outside `where` is left alone. An unchanged card is not rewritten, so a
165
+ That is also why a list reaches the store through **`syncHelper.syncCards(store, items, where)`,
166
+ never `replace()`**: every card given is written, every card matching `where` that the list does not
167
+ name is dropped, and everything outside `where` is left alone. An unchanged card is not rewritten, so a
108
168
  periodic re-seed wakes no subscriber. `opts.keep` protects ids a commit wrote while the list was in
109
169
  flight.
110
170
 
111
- The fold rules, shared by `syncCards`, `applyCards`, `applyCommitEvent` and `commits.wait`:
171
+ The fold rules, shared by `syncHelper.syncCards`, the mirror's `applyCards` and `applyCommitEvent`, and
172
+ `commits.wait`:
112
173
 
113
174
  - an OLDER `seq` never overwrites a newer record — frames and list answers race;
114
175
  - a committed `delete` removes the row and every link touching it; a project's delete also drops
@@ -124,8 +185,8 @@ The fold rules, shared by `syncCards`, `applyCards`, `applyCommitEvent` and `com
124
185
 
125
186
  ```typescript
126
187
  const feed = makePlanningFeed(context, {
127
- query: { parent: projectId, type: STORY },
128
- filter: { project: projectId },
188
+ query: { parent: shedId, type: 'shed:tool' },
189
+ filter: { project: shedId },
129
190
  refresh: 15_000,
130
191
  onChange: state => setState(state),
131
192
  })
@@ -135,11 +196,11 @@ await feed.stop() // stops folding; the shared socket stays open
135
196
  ```
136
197
 
137
198
  1. Subscribes to commits FIRST (the socket, when an opener exists).
138
- 2. Seeds: `cards.list` (unpaged unless the query pages) → `syncCards` over `where`
139
- (`criteriaOf(query)` by default). A PAGED seed only writes — a page cannot say what does not
140
- exist.
141
- 3. Folds every frame with `applyCommitEvent`, and re-seeds every `refresh` ms — the authoritative
142
- backstop to a socket that can drop frames.
199
+ 2. Seeds: `cards.list` (unpaged unless the query pages) → `syncHelper.syncCards` over `where`
200
+ (`queryHelper.criteriaOf(query)` by default). A PAGED seed only writes — a page cannot say what
201
+ does not exist.
202
+ 3. Folds every frame with `planningMirrorOf(stores).applyCommitEvent`, and re-seeds every `refresh`
203
+ ms — the authoritative backstop to a socket that can drop frames.
143
204
 
144
205
  `connected` reports whether a socket was open when the feed subscribed; `seeded` tells "nothing
145
206
  there" from "not loaded yet". A React hook wraps the feed in an effect and stops it on unmount.
@@ -181,22 +242,18 @@ A Node client passes no opener and relies on the long poll.
181
242
 
182
243
  ## Optimistic writes and `head > seq`
183
244
 
184
- `seq` is the last transition folded into a card, `head` the highest allocated. `applyReceipt`
185
- (run by `execute` whenever stores are registered) raises the stored card's `head` to the new
186
- transition's `seq`, so `model.pending()` is true before any frame arrives; the commit's fold brings
187
- `seq` up to it. The mirror only ever grows `head`, so a list fetched before the append does not
188
- clear the marker. A model's writes default `expectSeq` to `record.head ?? record.seq`; a stale model
189
- gets `WorkcardConflict` — reload and retry.
245
+ `seq` is the last transition folded into a card, `head` the highest allocated. The mirror's
246
+ `applyReceipt` (run by `execute` whenever stores are registered) raises the stored card's `head` to
247
+ the new transition's `seq`, so `model.pending()` is true before any frame arrives; the commit's fold
248
+ brings `seq` up to it. The mirror only ever grows `head`, so a list fetched before the append does not
249
+ clear the marker. A model's `expectSeq` default and its `WorkcardConflict` are `planning` → Models.
190
250
 
191
251
  ## Gotchas
192
252
 
193
253
  - Mount a feed once per screen; read the store everywhere else.
194
- - Do not `replace()` the card store — it holds every kind. Use `syncCards` with a `where`.
195
254
  - A refusal crosses the hop as its class (`IllegalTransition`, `WorkcardConflict`,
196
255
  `FieldsInvalid`, a plugin's own class) — branch on `instanceof`, never on message text.
197
256
  - `CommitTimeout` is not a failure of the transition; re-wait or re-read the card.
198
- - The scope passed to `for()` changes nothing the server decides; it exists so code written against
199
- `PlanningFacade` reads the same on both sides.
200
257
 
201
258
  ## Depends On
202
259
 
@@ -204,9 +261,68 @@ gets `WorkcardConflict` — reload and retry.
204
261
  - `@owlmeans/client-entrypoint` — `bindAll`, the typed `call()`
205
262
  - `@owlmeans/state` — the mirror; `@owlmeans/socket` — `Connection`
206
263
 
264
+ ## Auxiliary mirrors and authenticated scope lifecycle
265
+
266
+ With `resources: true` in the shared protocol tree, every remote facade exposes the complete
267
+ assignee/team/comment/mention API, including versioned writes, team membership and project
268
+ attachment. `appendPlanningStores` registers separate stock typed mirrors for all four resources.
269
+ `makePlanningResourceFeed` seeds and polls selected resource queries; use it with the stock stores
270
+ and `useStoreList` / `useStoreModel`. Auxiliary writes update the mirror only after success.
271
+ An active-assignee query (`retired: false`) includes an absent or null retirement flag. Local
272
+ mirror queries and feed reconciliation use `{ retired: { $ne: true } }`, matching the native
273
+ facade rather than strict equality to false. A filtered poll removes only its unchanged matching
274
+ snapshot rows, preserving unrelated retired identities needed by existing references.
275
+ Comments use trusted server authors; mentions are read-only derived cache commands.
276
+
277
+ Supply `PlanningClientOptions.scopeKey` from authenticated organization entity slug, profile/user
278
+ id, session id and authorization revision; return undefined when signed out. Before switching
279
+ organization or signing out, **await** `service.close()` before adopting the credential. It cancels
280
+ requests and commit waits, invalidates receipts (including already-settled receipts), stops every
281
+ registered feed, removes socket subscriptions, closes the carrier and rejects late socket openers.
282
+ A stale operation answers `client:scope-changed`; cancellation never rolls back an accepted server
283
+ write. A new subscription opens a fresh carrier after close.
284
+
285
+ Close drains admitted **local** mirror writes before clearing every stock store and schema cache.
286
+ New calls during an explicit close are refused, including hosts without `scopeKey`. Concurrent
287
+ closes share the boundary. If clearing fails, close rejects and fresh work stays blocked; retain
288
+ the old credential and retry close explicitly. An observed identity change also cancels old work
289
+ and queues clearing before the new identity's mirror writes. Reload schemas and create fresh feeds
290
+ after credential adoption. Feed query scope alone is insufficient for this lifecycle.
291
+
292
+ The service's `lifecycle` coordinates stock facades, receipts, feeds and schema writes. A custom
293
+ mirror integration captures an operation with `lifecycle.run`, performs remote reads through
294
+ `operation.wait` with `operation.signal`, then uses `lifecycle.mutate(operation, write)` for local
295
+ writes; never hold or nest that queue over a request, socket opener or another queued mutation.
296
+ `planningMirrorOf(stores)` accepts `{ lifecycle, operation }` as its optional last argument and
297
+ fetches missing frame records/links before entering the queue. Direct mirror/sync helpers without
298
+ that option are local primitives: their caller owns isolation and cleanup.
299
+
300
+ Planning protocols return `PlanningReply<T>` without storage organization identifiers. Stock
301
+ facades hydrate records, receipts, schema scopes and socket frames with `entityId: ''` only to
302
+ preserve the shared facade shape. That value is an advisory placeholder, never an organization
303
+ identity or a credential. Use `scopeKey` and the authenticated organization entity slug for
304
+ identity changes and access isolation; native server records keep their verified stable scope.
305
+
306
+ Resource feed reconciliation snapshots mirror versions before requesting a list. Only unchanged
307
+ snapshot rows absent from that response are removed, so a new local write or a newer version
308
+ survives an older in-flight poll. Stopped feeds cannot publish. Track custom feeds through
309
+ `registerFeed` when their lifecycle must end with the planning service. Card feeds use the same
310
+ identity fence and stop registration; component cleanup awaits `stop()` when it must observe a
311
+ completed boundary. Stop cancels unresolved seeds and drains admitted local changes without waiting
312
+ for the network; neither late callbacks nor authoritative deletion passes may publish afterward.
313
+ A schema write from an old generation cannot invalidate a newer generation's cached registry.
314
+
315
+
207
316
  ## Related
208
317
 
209
318
  - `planning` — records, flows, the fold, the models
210
319
  - `server-planning` — the handlers this addresses, the executor, the commit hub
211
320
  - `client-job` — the same seed-then-fold shape for safe application-job views
212
- - `state` — `syncCards` is built on its criteria engine and `purge`
321
+ - `state` — `syncHelper.syncCards` is built on its criteria engine and `purge`
322
+
323
+ ## External docs
324
+
325
+ - https://ajv.js.org/security.html#content-security-policy — runtime AJV requires unsafe-eval; standalone build compilation preserves strict CSP for static schemas.
326
+ - https://www.npmjs.com/package/@cfworker/json-schema/v/4.1.1 — the interpreter supports dynamic schemas without code generation; select an explicit vocabulary and check its known limitations (4.1.1).
327
+ - https://github.com/ajv-validator/ajv-formats#full-and-fast-validation-modes — full-mode predicates validate formats completely and are reusable through the plugin's `get` API (3.0.1).
328
+ - https://ajv.js.org/json-schema.html#nullable-openapi — nullable expands the declared type without adding null to enum or const.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAClF,MAAM,YAAY,CAAA;AAEnB;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACrE,CAAC,WAAW,qBAAqB,KACzC,qBAoFF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,qBAAqB,KACzC,CAAC,GAAG,kBA0BN,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAClF,MAAM,YAAY,CAAA;AAEnB;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACrE,CAAC,WAAW,qBAAqB,KACzC,qBAoHF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,qBAAqB,KACzC,CAAC,GAAG,kBA0BN,CAAA"}
package/build/client.js CHANGED
@@ -2,8 +2,10 @@ import { bindAll } from '@owlmeans/client-entrypoint';
2
2
  import { createLazyService, MiddlewareStage, MiddlewareType } from '@owlmeans/context';
3
3
  import { DEFAULT_PLUGIN_ORDER, makeSchemaRegistry, PLANNING_SERVICE, PlanningUnsupported, } from '@owlmeans/planning';
4
4
  import { makeRemoteCommitSource } from './commits.js';
5
+ import { makeRemoteDefinitions } from './definitions.js';
5
6
  import { makeRemoteFacade } from './facade.js';
6
- import { planningStoresOf } from './stores.js';
7
+ import { planningContextOf } from './helper.js';
8
+ import { makePlanningClientLifecycle } from './lifecycle.js';
7
9
  /**
8
10
  * The client planning service: a remote facade factory under the SAME alias the server registers
9
11
  * its service under, so `context.planning().for(scope)` is one call on both sides.
@@ -14,30 +16,51 @@ import { planningStoresOf } from './stores.js';
14
16
  * bundle.
15
17
  */
16
18
  export const makePlanningClientService = (context, options) => {
19
+ const feeds = new Set();
17
20
  const registry = makeSchemaRegistry();
18
21
  const plugins = [];
19
- const stores = () => planningStoresOf(context);
22
+ const stores = () => planningContextOf(context).stores();
23
+ const lifecycle = makePlanningClientLifecycle({ scopeKey: options.scopeKey, clear: async () => {
24
+ const mirrors = stores();
25
+ if (mirrors != null)
26
+ for (const resource of Object.values(mirrors)) {
27
+ const rows = await resource.list({}, { size: 0 });
28
+ for (const row of rows.items)
29
+ await resource.delete(row.id);
30
+ }
31
+ } });
32
+ const scopeKey = lifecycle.key;
20
33
  const commits = makeRemoteCommitSource(context, options.protocols, {
21
- socket: options.socket, poll: options.poll, timeout: options.timeout, stores,
34
+ socket: options.socket, poll: options.poll, timeout: options.timeout, stores, lifecycle,
22
35
  });
23
36
  const layerPlugins = () => plugins.forEach(plugin => {
37
+ plugin.schemas?.assigneeTypes?.forEach(registry.registerAssigneeType);
24
38
  plugin.schemas?.flows?.forEach(registry.registerFlow);
25
39
  plugin.schemas?.types?.forEach(registry.registerType);
26
40
  });
41
+ let loadedKey = scopeKey();
27
42
  let loaded = false;
28
43
  let loading = null;
29
44
  const loadSchemas = async (opts) => {
45
+ const key = scopeKey();
46
+ if (loadedKey !== key) {
47
+ loaded = false;
48
+ loading = null;
49
+ definitions?.invalidate();
50
+ loadedKey = key;
51
+ }
30
52
  if (loaded && opts?.force !== true) {
31
53
  return registry;
32
54
  }
33
55
  if (loading == null || opts?.force === true) {
34
- const attempt = (async () => {
35
- const bundle = await context.entrypoint(options.protocols.schema.list).call({ timeout: options.timeout });
56
+ const attempt = lifecycle.run(async (operation) => {
57
+ const bundle = await operation.wait(context.entrypoint(options.protocols.schema.list).call({ timeout: options.timeout, signal: operation.signal }));
58
+ operation.check();
36
59
  registry.load(bundle);
37
60
  layerPlugins();
38
61
  loaded = true;
39
62
  return registry;
40
- })();
63
+ });
41
64
  loading = attempt;
42
65
  // A failed load is forgotten, so the next caller asks again instead of inheriting the error.
43
66
  attempt.catch(() => {
@@ -48,13 +71,31 @@ export const makePlanningClientService = (context, options) => {
48
71
  }
49
72
  return await loading;
50
73
  };
74
+ // A write of data-defined schemas moves the organization's bundle too: the next `model()` reloads it.
75
+ const definitions = options.protocols.schema.define == null
76
+ ? undefined
77
+ : makeRemoteDefinitions(context, options.protocols, { lifecycle, timeout: options.timeout, onWrite: () => { loaded = false; loading = null; } });
51
78
  const facadeFor = (scope) => makeRemoteFacade(context, options.protocols, { ...options.scope, ...scope }, {
52
- commits, schemas: registry, loadSchemas, timeout: options.timeout, stores,
79
+ commits, schemas: registry, loadSchemas, scopeKey, lifecycle, timeout: options.timeout, stores,
80
+ ...(definitions != null ? { definitions } : {}),
53
81
  });
54
82
  let fallback = null;
83
+ lifecycle.onInvalidate(async () => {
84
+ const stopping = [...feeds];
85
+ feeds.clear();
86
+ loaded = false;
87
+ loading = null;
88
+ definitions?.invalidate();
89
+ registry.load({ version: 1, types: [], flows: [] });
90
+ layerPlugins();
91
+ await Promise.all(stopping.map(async (feed) => await feed.stop()));
92
+ });
55
93
  // Lazy, like the server's host: `context.planning()` answers before the context initializes.
56
94
  return createLazyService(PLANNING_SERVICE, {
57
95
  schemas: registry,
96
+ lifecycle,
97
+ scopeKey,
98
+ registerFeed: feed => { feeds.add(feed); return () => { feeds.delete(feed); }; },
58
99
  commits,
59
100
  loadSchemas,
60
101
  use: plugin => {
@@ -77,7 +118,8 @@ export const makePlanningClientService = (context, options) => {
77
118
  committed: async () => {
78
119
  throw new PlanningUnsupported('client:committed');
79
120
  },
80
- close: async () => await commits.close(),
121
+ close: lifecycle.close,
122
+ ...(definitions != null ? { definitions } : {}),
81
123
  });
82
124
  };
83
125
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,6 @@
1
1
  import type { BasicConfig, BasicContext } from '@owlmeans/context';
2
- import type { PlanningProtocols } from '@owlmeans/planning';
2
+ import { type PlanningProtocols } from '@owlmeans/planning';
3
3
  import type { RemoteCommitSource, RemoteCommitSourceOptions } from './types.js';
4
- /**
5
- * The commit source of a remote facade: the `commit.get` long poll and, when the host injects a
6
- * socket opener, the `commit.events` feed.
7
- *
8
- * `wait` SUBSCRIBES FIRST and only then reads the status — a commit landing between a read and a
9
- * subscription would otherwise be missed until the deadline. It then long-polls in holds of
10
- * `min(poll, remaining)` seconds, so a client with no socket (a Node connector) still learns of a
11
- * commit within one round trip of it landing, and a client with one learns from whichever arrives
12
- * first. The deadline is a {@link CommitTimeout}; the transition stays pending — nothing is undone.
13
- *
14
- * ONE socket serves every subscription of the source; releasing a subscription never closes it.
15
- */
4
+ /** Subscribe before reading, then race the shared socket against bounded HTTP holds. */
16
5
  export declare const makeRemoteCommitSource: <C extends BasicConfig, T extends BasicContext<C>>(context: T, protocols: PlanningProtocols, opts?: RemoteCommitSourceOptions) => RemoteCommitSource;
17
6
  //# sourceMappingURL=commits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commits.d.ts","sourceRoot":"","sources":["../src/commits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKlE,OAAO,KAAK,EAC+B,iBAAiB,EAC3D,MAAM,oBAAoB,CAAA;AAG3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAuB/E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC5E,CAAC,aAAa,iBAAiB,SAAS,yBAAyB,KACzE,kBAuMF,CAAA"}
1
+ {"version":3,"file":"commits.d.ts","sourceRoot":"","sources":["../src/commits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAA2O,KAAK,iBAAiB,EAAmC,MAAM,oBAAoB,CAAA;AAKrU,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAM/E,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,WAC5E,CAAC,aAAa,iBAAiB,SAAS,yBAAyB,KACzE,kBAsMF,CAAA"}