@owlmeans/server-planning 0.1.18-rc.0
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 +150 -0
- package/agent-meta/manifest.json +16 -0
- package/agent-meta/skills/server-planning/SKILL.md +203 -0
- package/build/actions/cards.d.ts +16 -0
- package/build/actions/cards.d.ts.map +1 -0
- package/build/actions/cards.js +32 -0
- package/build/actions/cards.js.map +1 -0
- package/build/actions/commit.d.ts +14 -0
- package/build/actions/commit.d.ts.map +1 -0
- package/build/actions/commit.js +48 -0
- package/build/actions/commit.js.map +1 -0
- package/build/actions/execute.d.ts +13 -0
- package/build/actions/execute.d.ts.map +1 -0
- package/build/actions/execute.js +35 -0
- package/build/actions/execute.js.map +1 -0
- package/build/actions/index.d.ts +9 -0
- package/build/actions/index.d.ts.map +1 -0
- package/build/actions/index.js +9 -0
- package/build/actions/index.js.map +1 -0
- package/build/actions/links.d.ts +5 -0
- package/build/actions/links.d.ts.map +1 -0
- package/build/actions/links.js +8 -0
- package/build/actions/links.js.map +1 -0
- package/build/actions/schemas.d.ts +6 -0
- package/build/actions/schemas.d.ts.map +1 -0
- package/build/actions/schemas.js +9 -0
- package/build/actions/schemas.js.map +1 -0
- package/build/actions/specs.d.ts +10 -0
- package/build/actions/specs.d.ts.map +1 -0
- package/build/actions/specs.js +14 -0
- package/build/actions/specs.js.map +1 -0
- package/build/actions/transitions.d.ts +6 -0
- package/build/actions/transitions.d.ts.map +1 -0
- package/build/actions/transitions.js +8 -0
- package/build/actions/transitions.js.map +1 -0
- package/build/actions/watch.d.ts +12 -0
- package/build/actions/watch.d.ts.map +1 -0
- package/build/actions/watch.js +45 -0
- package/build/actions/watch.js.map +1 -0
- package/build/consts.d.ts +18 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +19 -0
- package/build/consts.js.map +1 -0
- package/build/executor/changes.d.ts +31 -0
- package/build/executor/changes.d.ts.map +1 -0
- package/build/executor/changes.js +55 -0
- package/build/executor/changes.js.map +1 -0
- package/build/executor/code.d.ts +15 -0
- package/build/executor/code.d.ts.map +1 -0
- package/build/executor/code.js +59 -0
- package/build/executor/code.js.map +1 -0
- package/build/executor/receipt.d.ts +12 -0
- package/build/executor/receipt.d.ts.map +1 -0
- package/build/executor/receipt.js +56 -0
- package/build/executor/receipt.js.map +1 -0
- package/build/executor/resolve.d.ts +35 -0
- package/build/executor/resolve.d.ts.map +1 -0
- package/build/executor/resolve.js +136 -0
- package/build/executor/resolve.js.map +1 -0
- package/build/executor/validate.d.ts +13 -0
- package/build/executor/validate.d.ts.map +1 -0
- package/build/executor/validate.js +222 -0
- package/build/executor/validate.js.map +1 -0
- package/build/executor.d.ts +15 -0
- package/build/executor.d.ts.map +1 -0
- package/build/executor.js +113 -0
- package/build/executor.js.map +1 -0
- package/build/facade.d.ts +12 -0
- package/build/facade.d.ts.map +1 -0
- package/build/facade.js +131 -0
- package/build/facade.js.map +1 -0
- package/build/helper.d.ts +18 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +32 -0
- package/build/helper.js.map +1 -0
- package/build/index.d.ts +13 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -0
- package/build/projection.d.ts +28 -0
- package/build/projection.d.ts.map +1 -0
- package/build/projection.js +40 -0
- package/build/projection.js.map +1 -0
- package/build/registry.d.ts +30 -0
- package/build/registry.d.ts.map +1 -0
- package/build/registry.js +103 -0
- package/build/registry.js.map +1 -0
- package/build/service.d.ts +40 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +109 -0
- package/build/service.js.map +1 -0
- package/build/store/commits.d.ts +15 -0
- package/build/store/commits.d.ts.map +1 -0
- package/build/store/commits.js +147 -0
- package/build/store/commits.js.map +1 -0
- package/build/store/composite.d.ts +12 -0
- package/build/store/composite.d.ts.map +1 -0
- package/build/store/composite.js +150 -0
- package/build/store/composite.js.map +1 -0
- package/build/store/fold.d.ts +39 -0
- package/build/store/fold.d.ts.map +1 -0
- package/build/store/fold.js +220 -0
- package/build/store/fold.js.map +1 -0
- package/build/store/index.d.ts +6 -0
- package/build/store/index.d.ts.map +1 -0
- package/build/store/index.js +5 -0
- package/build/store/index.js.map +1 -0
- package/build/store/memory.d.ts +19 -0
- package/build/store/memory.d.ts.map +1 -0
- package/build/store/memory.js +0 -0
- package/build/store/memory.js.map +1 -0
- package/build/store/types.d.ts +82 -0
- package/build/store/types.d.ts.map +1 -0
- package/build/store/types.js +2 -0
- package/build/store/types.js.map +1 -0
- package/build/types.d.ts +46 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/guard.d.ts +19 -0
- package/build/utils/guard.d.ts.map +1 -0
- package/build/utils/guard.js +35 -0
- package/build/utils/guard.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/scope.d.ts +20 -0
- package/build/utils/scope.d.ts.map +1 -0
- package/build/utils/scope.js +36 -0
- package/build/utils/scope.js.map +1 -0
- package/package.json +66 -0
- package/src/actions/cards.ts +52 -0
- package/src/actions/commit.ts +55 -0
- package/src/actions/execute.ts +47 -0
- package/src/actions/index.ts +8 -0
- package/src/actions/links.ts +14 -0
- package/src/actions/schemas.ts +15 -0
- package/src/actions/specs.ts +23 -0
- package/src/actions/transitions.ts +14 -0
- package/src/actions/watch.ts +52 -0
- package/src/consts.ts +22 -0
- package/src/executor/changes.ts +78 -0
- package/src/executor/code.ts +83 -0
- package/src/executor/receipt.ts +66 -0
- package/src/executor/resolve.ts +174 -0
- package/src/executor/validate.ts +253 -0
- package/src/executor.ts +149 -0
- package/src/facade.ts +162 -0
- package/src/helper.ts +44 -0
- package/src/index.ts +13 -0
- package/src/projection.ts +65 -0
- package/src/registry.ts +143 -0
- package/src/service.ts +157 -0
- package/src/store/commits.ts +174 -0
- package/src/store/composite.ts +171 -0
- package/src/store/fold.ts +257 -0
- package/src/store/index.ts +6 -0
- package/src/store/memory.ts +0 -0
- package/src/store/types.ts +94 -0
- package/src/types.ts +48 -0
- package/src/utils/guard.ts +40 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/scope.ts +51 -0
- package/tests/commits.spec.ts +78 -0
- package/tests/context.ts +198 -0
- package/tests/entrypoints.spec.ts +72 -0
- package/tests/executor.spec.ts +69 -0
- package/tests/memory-store.spec.ts +84 -0
- package/tests/plugins.spec.ts +86 -0
- package/tests/specifications.spec.ts +61 -0
- package/tests/tsconfig.json +17 -0
- package/tests/validation.spec.ts +62 -0
- package/tsconfig.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @owlmeans/server-planning
|
|
2
|
+
|
|
3
|
+
The server half of `@owlmeans/planning`: the planning service and its plugin registry, the
|
|
4
|
+
transition executor every write goes through, the in-memory reference store, the commit hub and the
|
|
5
|
+
protocol handlers. A backend appends the service once, binds the handlers to the shared protocol
|
|
6
|
+
tree, and reads and writes through a scoped facade. A browser or Node client uses
|
|
7
|
+
`@owlmeans/client-planning` against the same tree; a durable store implements the ports and reuses
|
|
8
|
+
the `@owlmeans/server-planning/store` subpath.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
bun add @owlmeans/server-planning@^0.1.18-rc.0 @owlmeans/planning@^0.1.18-rc.0 ajv
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Concepts
|
|
17
|
+
|
|
18
|
+
- **Facade** — `context.planning().for(scope)`: every read and write of one organization entity,
|
|
19
|
+
with no scope argument on any method.
|
|
20
|
+
- **Executor** — the only write path: normalize → idempotency → resolve → plugins' `before` →
|
|
21
|
+
validate → id → code → changes → seq CAS → append → project → receipt. Nothing is appended by a
|
|
22
|
+
refusal.
|
|
23
|
+
- **Plugin** — contributes types and flows, may own types with its own store, mint codes, refuse or
|
|
24
|
+
rewrite executions (`before`) and react to commits (`after`).
|
|
25
|
+
- **Commit** — a transition becomes visible when a store folds it; the folding process runs the
|
|
26
|
+
`after` chain exactly once and publishes a commit event.
|
|
27
|
+
- **Store** — `makeMemoryPlanningStore()` for tests and single-process tools; a durable store in
|
|
28
|
+
anything with more than one process.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Declare the tree once in the shared package:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { makePlanningProtocols } from '@owlmeans/planning'
|
|
36
|
+
|
|
37
|
+
export const planningProtocols = makePlanningProtocols({
|
|
38
|
+
base: { alias: 'app:planning', path: '/planning' },
|
|
39
|
+
guards: DEFAULT_GUARD,
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Wire the service and bind the handlers:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { appendPlanningService, servePlanningEntrypoints } from '@owlmeans/server-planning'
|
|
47
|
+
|
|
48
|
+
appendPlanningService(context, { plugins: [appTypesPlugin] })
|
|
49
|
+
context.registerEntrypoints(servePlanningEntrypoints(planningProtocols, {
|
|
50
|
+
scope: req => ({ channel: 'web' }),
|
|
51
|
+
}))
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Write through the facade and wait for the commit:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { TransitionAction, WorkcardKind } from '@owlmeans/planning'
|
|
58
|
+
|
|
59
|
+
const planning = context.planning().for({ entityId, profileId, channel: 'agent' })
|
|
60
|
+
const { card } = await planning.execute({
|
|
61
|
+
card: { kind: WorkcardKind.Card, type: 'app:task', parent: projectId, title: 'Ship it' },
|
|
62
|
+
action: TransitionAction.Create,
|
|
63
|
+
key: `import:${sourceId}`,
|
|
64
|
+
}, { wait: true })
|
|
65
|
+
|
|
66
|
+
await planning.execute({ card: card!.id!, action: TransitionAction.Transit, transition: 'start' }, { wait: true })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A plugin with a rule and a reaction:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { ensurePlanningService } from '@owlmeans/server-planning'
|
|
73
|
+
|
|
74
|
+
ensurePlanningService(context).use({
|
|
75
|
+
name: 'app-rules',
|
|
76
|
+
order: 10,
|
|
77
|
+
before: async (exec, { card, facade }) => {
|
|
78
|
+
if (exec.transition === 'start' && await facade.cards.count({ parent: card?.parent, status: 'doing' }) > 0) {
|
|
79
|
+
throw new ProjectBusy(card!.parent!)
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
after: async (event, { transition }) => {
|
|
83
|
+
if (event.action === TransitionAction.Transit && transition?.actor.channel === 'web') {
|
|
84
|
+
await notifyWorker(event.card)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Hand-written handler and the memory store in a test:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { appendPlanningService, planningFor } from '@owlmeans/server-planning'
|
|
94
|
+
import { makeMemoryPlanningStore } from '@owlmeans/server-planning/store'
|
|
95
|
+
|
|
96
|
+
appendPlanningService(testContext, { store: makeMemoryPlanningStore({ sync: false }), plugins: [fixtures] })
|
|
97
|
+
const cards = await planningFor(ctx, req, { channel: 'web' }).cards.list({ parent: projectId })
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
- Service: `appendPlanningService`, `ensurePlanningService`, `makePlanningService`,
|
|
103
|
+
`planningServiceApi`, `makePluginRegistry`, `makeStoreFacade`, `executeTransition`,
|
|
104
|
+
`PlanningServiceOptions`, `PlanningHostService`, `PlanningRuntime`, `PluginRegistry`
|
|
105
|
+
- Handlers: `servePlanningEntrypoints`, `planningFor`, `listSchemas`, `listCards`,
|
|
106
|
+
`summarizeCards`, `getCard`, `listCardTransitions`, `listCardSpecifications`, `getSpecification`,
|
|
107
|
+
`listSpecificationRevisions`, `listLinks`, `getTransition`, `executePlanning`, `wireExecution`,
|
|
108
|
+
`executeOptionsOf`, `getCommit`, `watchCommits`, `PlanningHandlerOptions`, `PlanningScopeExtractor`
|
|
109
|
+
- Scope: `scopeOf`, `actorOf`, `handlerFacade`, `planningServiceOf`, `concealed`, `assertScope`,
|
|
110
|
+
`notFoundOf`, `clampSeconds`
|
|
111
|
+
- Projection: `makeProjectionProcessor`, `planningQueueHooks`, `ProjectionOptions`
|
|
112
|
+
- Constants: `DEFAULT_ALIAS`, `MEMORY_STORE_ALIAS`, `DEFAULT_COMMIT_MEMORY`, `COMMIT_POLL_LADDER`
|
|
113
|
+
- `@owlmeans/server-planning/store`: `makeMemoryPlanningStore`, `foldPending`, `failPending`,
|
|
114
|
+
`revisionsFromLog`, `commitEventOf`, `makeCommitHub`, `makeCompositeStore`, and the types
|
|
115
|
+
`BindablePlanningStore`, `CommitListener`, `CommitHub`, `CommitHubOptions`, `FoldOptions`,
|
|
116
|
+
`FoldResult`, `MemoryPlanningStore`, `MemoryPlanningStoreOptions`, `StoreRoute`
|
|
117
|
+
|
|
118
|
+
## Common pitfalls
|
|
119
|
+
|
|
120
|
+
- `after` hooks run where the transition is FOLDED, once per commit — register the same plugins in
|
|
121
|
+
every folding process, and never run hooks from a commit-bus subscription.
|
|
122
|
+
- The memory store is per-process heap: wrong for multiple processes or restarts.
|
|
123
|
+
- `actor` comes from the scope; a wire `actor` or `createdBy` is ignored.
|
|
124
|
+
- Another entity's record is `WorkcardNotFound`, never a permission error.
|
|
125
|
+
- Give retried writes a `key` — it is checked before validation, so a retry answers the first receipt.
|
|
126
|
+
- `expectSeq` compares against `head`; a stale one is `WorkcardConflict`, re-read and retry.
|
|
127
|
+
- Call `appendPlanningService` before any `ensurePlanningService`, or the appended host replaces
|
|
128
|
+
what was registered on the default one.
|
|
129
|
+
|
|
130
|
+
## Related packages
|
|
131
|
+
|
|
132
|
+
- `@owlmeans/planning` — records, flows, fold, query language, protocol tree, models
|
|
133
|
+
- `@owlmeans/client-planning` — the remote facade and state mirror
|
|
134
|
+
- `@owlmeans/server-job`, `@owlmeans/queue` — job feeds and the projection queue
|
|
135
|
+
|
|
136
|
+
<!-- owlmeans:agent-guidance:start -->
|
|
137
|
+
## Agent guidance
|
|
138
|
+
|
|
139
|
+
This package ships embedded agent skills under `agent-meta/`. After installing your
|
|
140
|
+
`@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
|
|
141
|
+
your project's skill store (`.agents/skills/`):
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.27
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The embedded files are version-matched to this package release. Do not edit them
|
|
148
|
+
directly — they are regenerated on each publish. To contribute guidance edits,
|
|
149
|
+
open a PR against the source monorepo.
|
|
150
|
+
<!-- owlmeans:agent-guidance:end -->
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"package": "@owlmeans/server-planning",
|
|
4
|
+
"version": "0.1.18-rc.0",
|
|
5
|
+
"generatedAt": "2026-09-16T15:11:05.003Z",
|
|
6
|
+
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
|
+
"entries": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "skill",
|
|
10
|
+
"name": "server-planning",
|
|
11
|
+
"category": "package-specific",
|
|
12
|
+
"file": "skills/server-planning/SKILL.md",
|
|
13
|
+
"canonicalPath": ".agents/skills/server-planning/SKILL.md"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: server-planning
|
|
3
|
+
description: How to use @owlmeans/server-planning — appendPlanningService and the plugin registry, the transition executor and its refusal order, the in-memory store, servePlanningEntrypoints and the commit socket, and the ports a durable or foreign provider implements. Auto-invoked when wiring planning into a backend, writing a planning plugin, or diagnosing a transition that was refused or never committed.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
7
|
+
|
|
8
|
+
# @owlmeans/server-planning
|
|
9
|
+
|
|
10
|
+
**Layer:** Server
|
|
11
|
+
**Install:** `"@owlmeans/server-planning": "^0.1.18-rc.0"` in `dependencies` (`ajv` is a peer)
|
|
12
|
+
|
|
13
|
+
The general implementation of `@owlmeans/planning`: the planning service (a plugin host), the
|
|
14
|
+
scoped facade, the executor every write goes through, the in-memory reference store, the commit
|
|
15
|
+
hub and the protocol handlers. It holds no database code — a durable store implements the ports and
|
|
16
|
+
reuses the `./store` subpath (`foldPending`, `makeCommitHub`) without pulling fastify in.
|
|
17
|
+
|
|
18
|
+
## Key exports
|
|
19
|
+
|
|
20
|
+
| Export | What it is |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `appendPlanningService(ctx, opts?, alias?)` | Registers the lazy host service and `ctx.planning()` |
|
|
23
|
+
| `ensurePlanningService(ctx, alias?)` | Idempotent — what a plugin package calls before `use` |
|
|
24
|
+
| `makePlanningService(opts?, alias?)`, `planningServiceApi(opts, self)` | The service, and its body for a specialised service |
|
|
25
|
+
| `PlanningServiceOptions` | `{ store?, plugins?, schemas?, hooks?, ids?, now? }` |
|
|
26
|
+
| `makePluginRegistry`, `makeStoreFacade`, `executeTransition` | The pieces the service is made of |
|
|
27
|
+
| `servePlanningEntrypoints(protocols, opts?)` | One binding per protocol of a `makePlanningProtocols` tree |
|
|
28
|
+
| `planningFor(ctx, req, extra?)` | The request-scoped facade for a hand-written handler |
|
|
29
|
+
| `PlanningHandlerOptions` | `{ service?, event?, maxPoll?, scope?(req, ctx) }` |
|
|
30
|
+
| `listCards` … `executePlanning`, `getCommit`, `watchCommits` | The handlers, to bind one by hand |
|
|
31
|
+
| `scopeOf(req, extra?)`, `actorOf(req)`, `concealed(run)`, `clampSeconds` | Scope and security helpers |
|
|
32
|
+
| `makeProjectionProcessor(ctx, opts?)`, `planningQueueHooks(ctx, opts?)` | A generic projection job body and its `onJobDead` |
|
|
33
|
+
| `./store`: `makeMemoryPlanningStore`, `foldPending`, `failPending`, `revisionsFromLog`, `commitEventOf`, `makeCommitHub`, `makeCompositeStore` | What stores are built from |
|
|
34
|
+
|
|
35
|
+
## Wiring
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { appendPlanningService, servePlanningEntrypoints } from '@owlmeans/server-planning'
|
|
39
|
+
import { makePlanningProtocols } from '@owlmeans/planning'
|
|
40
|
+
|
|
41
|
+
// shared package
|
|
42
|
+
export const planningProtocols = makePlanningProtocols({
|
|
43
|
+
base: { alias: 'app:planning', path: '/planning', parent: appProtocols.api.base },
|
|
44
|
+
guards: DEFAULT_GUARD,
|
|
45
|
+
socketBase: appProtocols.updates.base,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// backend context
|
|
49
|
+
appendPlanningService(context, { store: durableStore, plugins: [appTypesPlugin] })
|
|
50
|
+
|
|
51
|
+
// API process
|
|
52
|
+
context.registerEntrypoints(servePlanningEntrypoints(planningProtocols, {
|
|
53
|
+
scope: req => ({ channel: req.auth?.type === AuthroizationType.AuthToken ? 'connect' : 'web' }),
|
|
54
|
+
}))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Without `store` the service builds a memory store. The host is a LAZY service so
|
|
58
|
+
`ensurePlanningService(ctx).use(plugin)` works from `makeContext`, before `init()`.
|
|
59
|
+
- Call `appendPlanningService` BEFORE any `ensurePlanningService`: `ensure` registers a default
|
|
60
|
+
host when none exists, and a later `append` replaces it together with whatever was `use`d on it.
|
|
61
|
+
- In code: `ctx.planning().for({ entityId, profileId?, channel?, actor? })` → a `PlanningFacade`
|
|
62
|
+
with no scope argument on any method.
|
|
63
|
+
|
|
64
|
+
## The executor, step by step
|
|
65
|
+
|
|
66
|
+
Nothing is appended before step 11 — every refusal leaves the log untouched.
|
|
67
|
+
|
|
68
|
+
1. **Normalize** — deep copy, trimmed text, `parents ∋ parent` (first).
|
|
69
|
+
2. **Scope** — `entityId` must be present.
|
|
70
|
+
3. **Idempotency** — a `key` already in the entity's log answers ITS receipt, before any
|
|
71
|
+
validation or middleware.
|
|
72
|
+
4. **Resolve** — the card (`WorkcardNotFound`; another entity's card is `PlanningScopeMismatch`),
|
|
73
|
+
its type and flow, every draft parent (`ParentNotFound`) and the parent project's
|
|
74
|
+
`cardTypes`/`projectTypes` (`CardTypeNotAllowed`), the specification slot.
|
|
75
|
+
5. **`before` chain** — in plugin order; re-resolved once when it changed the card, type or parent.
|
|
76
|
+
6. **Validate** — shape (`planning:malformed:*`), the flow rule (`IllegalTransition`), immutables
|
|
77
|
+
(`planning:immutable:*`), merged `fields` (`FieldsInvalid`), labels (`LabelNotAllowed`), a moved
|
|
78
|
+
parent, the slot (`SpecificationSlotUnknown`, `SpecificationRevisionConflict`, JSON body schema
|
|
79
|
+
→ `FieldsInvalid`), relationships (`RelationshipRefused`: undeclared type, `from` not the card,
|
|
80
|
+
missing target, type constraints, `single`, unlinking an absent edge).
|
|
81
|
+
7. **Card id** — `store.newId?.() ?? options.ids?.() ?? uuid()`. Ids are the store's.
|
|
82
|
+
8. **Code** — a supplied code is checked; else the plugins' `mintCode` (first answer wins, checked);
|
|
83
|
+
else the type's `CodePolicy` (a slug policy derives from the title) → `CodeTaken`.
|
|
84
|
+
9. **Changes** — `computeChanges`. An `update` that changes nothing answers the card's latest
|
|
85
|
+
receipt and appends nothing.
|
|
86
|
+
10. **Seq** — `transitions.nextSeq(card, expectSeq)`, a CAS against `head` (`WorkcardConflict`).
|
|
87
|
+
11. **Append** — `commit: pending`. A lost race on the key answers the winner's receipt.
|
|
88
|
+
12. **Project** — `store.cards.project(card)`: a sync store folds now, a queued one enqueues.
|
|
89
|
+
13. **Receipt** — `{ transition, card? (once committed), committed(opts) }`.
|
|
90
|
+
14. **`wait: true`** — `committed()` before returning (`CommitTimeout`, `CommitFailed`).
|
|
91
|
+
|
|
92
|
+
`actor` is the SCOPE's: `profileId`/`userId`/`service`/`channel` come from the scope only; an
|
|
93
|
+
in-process caller may add `agent`/`runId` on the execution. A handler never passes a wire `actor`.
|
|
94
|
+
|
|
95
|
+
## Idempotency and `expectSeq`
|
|
96
|
+
|
|
97
|
+
- Give every retried or replayed write a `key` (`import:<source>:<n>`). The key is unique per entity
|
|
98
|
+
and checked first, so a retry never re-validates against a card its first attempt already moved.
|
|
99
|
+
- `expectSeq` compares against `head`, not `seq`: two writers racing while a fold is pending both
|
|
100
|
+
see the same `seq` but only one wins the head. `null` or omitted opts out; a model fills it from
|
|
101
|
+
its record.
|
|
102
|
+
|
|
103
|
+
## The plugin seam
|
|
104
|
+
|
|
105
|
+
| Concern | Rule |
|
|
106
|
+
|---|---|
|
|
107
|
+
| Registration | `use(plugin)` replaces by `name`; schemas are contributed at `use` time (later type/flow id wins) |
|
|
108
|
+
| Order | `order ?? 50` ascending, registration order among equals |
|
|
109
|
+
| Store routing | `store(type)` = the first plugin whose `owns(type)` is true AND that supplies a `store`; else the default store. A store factory is resolved once |
|
|
110
|
+
| Reads | By id: the default store, then each owned store. A list naming one owned type (or several owned by one store) goes there; any other list — a cross-type query — is the default store's alone |
|
|
111
|
+
| `mintCode` | First non-`undefined` answer wins |
|
|
112
|
+
| `before` | Every plugin in order; may return a replaced execution; a `ResilientError` passes through, anything else becomes `PlanningRefused('<plugin>:<message>')` |
|
|
113
|
+
| `after` | Every plugin in order, once per COMMITTED transition; errors logged, never rethrown |
|
|
114
|
+
|
|
115
|
+
**Where `after` runs.** The service's `committed(event)` runs the chain, and it is called by the
|
|
116
|
+
process that FOLDS the transition — the memory store inside `project()`, a queued store's projection
|
|
117
|
+
job after marking the commit. Never subscribe a process to the commit bus to run hooks: that fires
|
|
118
|
+
once per subscribed process. Every process that folds registers the same plugins; each commit is
|
|
119
|
+
folded by one of them, so each hook fires once. `hooks: false` makes a process fold without running
|
|
120
|
+
them. A hook sees `ctx.transition` (read back, so `actor.channel` is available) and a facade scoped
|
|
121
|
+
to the event's entity acting as this process's `cfg.service`.
|
|
122
|
+
|
|
123
|
+
The service hands `committed` to a store through `store.bind?.(listener)` the first time it resolves
|
|
124
|
+
the store; `bind` replaces the listener, so a store has exactly one.
|
|
125
|
+
|
|
126
|
+
## The memory store
|
|
127
|
+
|
|
128
|
+
`makeMemoryPlanningStore({ sync?, ids?, now?, seed?, onCommitted?, alias? })`
|
|
129
|
+
|
|
130
|
+
- Cards and projects in one map, specifications in their own, relationships in a third; every read
|
|
131
|
+
goes through the `@owlmeans/resource` query engine. A card list includes specifications only when
|
|
132
|
+
the criteria asks for `kind: specification` or a `category`.
|
|
133
|
+
- `sync` (default) folds, publishes and runs `committed` inside `project()`, serialized per card.
|
|
134
|
+
`sync: false` folds nothing until `flush(card?)` — a queued store's shape, for tests that need a
|
|
135
|
+
commit to stay pending.
|
|
136
|
+
- A project `delete` purges the project, everything whose `parents` reach it, their specifications,
|
|
137
|
+
links and log; the commit hub remembers the settled event so the waiter still gets its answer.
|
|
138
|
+
- **It is wrong for more than one process or a restart** — the log, the cards and the commit feed
|
|
139
|
+
are this process's heap. Use it for tests, tools and single-process demos.
|
|
140
|
+
|
|
141
|
+
## Writing a durable store
|
|
142
|
+
|
|
143
|
+
- Implement `PlanningStore` (`transitions`, `cards`, `specs`, `links`, `commits`, `newId`). `newId`
|
|
144
|
+
answers the id format the host's references need (ObjectId hex for Mongo).
|
|
145
|
+
- `transitions.list` treats `sinceSeq` as `seq > sinceSeq`; `nextSeq` is the CAS against `head` and
|
|
146
|
+
answers 1 for a card that has no log.
|
|
147
|
+
- The projection body is `foldPending(store, cardId, entityId, { onCommitted, publish, touch, limit })`:
|
|
148
|
+
it applies pending transitions with `applyTransition` only, marks each committed or failed,
|
|
149
|
+
publishes, then runs `onCommitted`. The caller owns single flight per card. Strip `record` from
|
|
150
|
+
the event before a cross-process bus.
|
|
151
|
+
- **A failed transition never blocks the card.** The fold marks it `failed`, publishes a failed
|
|
152
|
+
event, and goes PAST it: the card's `seq` advances to that transition with every other value as
|
|
153
|
+
it was, and the next transition folds normally. Without that, every later transition fails as
|
|
154
|
+
out of order and the card is stuck for good. Only two cases stop short: a failed `create` leaves
|
|
155
|
+
no card to advance, and a store that refuses the advancing write stops the fold with `followUp`,
|
|
156
|
+
leaving the rest pending for a retry. `revisionsFromLog` replays past failed transitions the same
|
|
157
|
+
way.
|
|
158
|
+
- `failPending` is what a dead projection job must do, or waiters hang to their timeout.
|
|
159
|
+
- `makeCommitHub({ status, remember?, ladder? })` is the `CommitSource`: feed `publish` from the bus,
|
|
160
|
+
answer `status` from transition rows. `wait` subscribes first, polls once, then climbs the ladder.
|
|
161
|
+
- `revisionsFromLog(transitions, limit?)` answers `specs.revisions`.
|
|
162
|
+
- `makeProjectionProcessor(ctx)` + `planningQueueHooks(ctx)` are the store-agnostic job body and
|
|
163
|
+
`onJobDead`; a store with an admission CAS wraps its own around them.
|
|
164
|
+
|
|
165
|
+
## A foreign provider
|
|
166
|
+
|
|
167
|
+
Only `cards` is required. A plugin that `owns` its types and supplies a store without
|
|
168
|
+
`transitions` can be read through the facade; writes to it refuse with `PlanningUnsupported` —
|
|
169
|
+
mapping external records (`mappers`) belongs to that plugin's own store adapter.
|
|
170
|
+
|
|
171
|
+
## The commit feed
|
|
172
|
+
|
|
173
|
+
- **Poll** — `commit.get` answers the status; with `?wait=<s>` (clamped to `maxPoll`, default 55)
|
|
174
|
+
it subscribes, re-polls, and holds until the commit settles or the time is up, answering `pending`
|
|
175
|
+
then — never an error.
|
|
176
|
+
- **Notify** — `commit.events` pushes `planning-commit` frames (or `opts.event`) filtered by the
|
|
177
|
+
authenticated entity and optional `project`/`card` query values; a frame without `record` gets the
|
|
178
|
+
card as it reads now. The subscription is released on the socket's `close` frame.
|
|
179
|
+
- `execute` with `wait: true` holds at most `maxPoll` seconds whatever `timeout` the body asked for.
|
|
180
|
+
|
|
181
|
+
## Scope and security
|
|
182
|
+
|
|
183
|
+
- `entityId` is `requireEntityKey(req)`; `opts.scope(req, ctx)` adds a `channel` or `service` and
|
|
184
|
+
can never replace the entity. `actor` and a create's `createdBy` come from the request, never the
|
|
185
|
+
body.
|
|
186
|
+
- Another entity's card, specification or transition answers `WorkcardNotFound` — the same as an
|
|
187
|
+
absent id — on reads and writes alike (`concealed`).
|
|
188
|
+
- Queries arrive in their wire form and are decoded with `decode*Query` before `criteriaOf`, which
|
|
189
|
+
always adds the scope's `entityId`.
|
|
190
|
+
|
|
191
|
+
## Testing
|
|
192
|
+
|
|
193
|
+
Category A. Build one real context with `makeBasicContext` + `appendPlanningService({ store:
|
|
194
|
+
makeMemoryPlanningStore({ now }), plugins: [fixtures] })`, and run a handler through
|
|
195
|
+
`handler.bind({ ref: { ctx } })(req, res)` with a request carrying `auth` and `entity`. A monotonic
|
|
196
|
+
`now` keeps log order deterministic; `sync: false` + `flush()` pins pending and hook-count cases.
|
|
197
|
+
|
|
198
|
+
## Related
|
|
199
|
+
|
|
200
|
+
- `planning` — records, flows, the fold, the query language, the protocol tree, the models
|
|
201
|
+
- `client-planning` — the remote facade and the state mirror that talk to these handlers
|
|
202
|
+
- `server-job`, `queue` — the declare/serve/feed pattern and the projection queue
|
|
203
|
+
- `resource` — the criteria engine every memory read goes through
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning';
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js';
|
|
4
|
+
type RequestHandler = ReturnType<ReturnType<typeof handlers<Context>>['request']>;
|
|
5
|
+
/** Cards of the caller's entity; the query arrives in its wire shape and is decoded here. */
|
|
6
|
+
export declare const listCards: (protocol: PlanningProtocols['card']['list'], opts?: PlanningHandlerOptions) => RequestHandler;
|
|
7
|
+
/** Intrinsic counts of DIRECT children per parent. */
|
|
8
|
+
export declare const summarizeCards: (protocol: PlanningProtocols['card']['summary'], opts?: PlanningHandlerOptions) => RequestHandler;
|
|
9
|
+
/** @throws {WorkcardNotFound} for an absent card and for another entity's alike */
|
|
10
|
+
export declare const getCard: (protocol: PlanningProtocols['card']['get'], opts?: PlanningHandlerOptions) => RequestHandler;
|
|
11
|
+
/** One card's log. */
|
|
12
|
+
export declare const listCardTransitions: (protocol: PlanningProtocols['card']['transitions'], opts?: PlanningHandlerOptions) => RequestHandler;
|
|
13
|
+
/** One card's specifications — the current document per category unless `all`. */
|
|
14
|
+
export declare const listCardSpecifications: (protocol: PlanningProtocols['card']['specifications'], opts?: PlanningHandlerOptions) => RequestHandler;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=cards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../../src/actions/cards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAI/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGlE,KAAK,cAAc,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;AAEjF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,aAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,sBAAsB,KAAG,cAKlG,CAAA;AAEL,sDAAsD;AACtD,eAAO,MAAM,cAAc,aAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,sBAAsB,KAAG,cAM1G,CAAA;AAEL,mFAAmF;AACnF,eAAO,MAAM,OAAO,aAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,SAAS,sBAAsB,KAAG,cAK/F,CAAA;AAEL,sBAAsB;AACtB,eAAO,MAAM,mBAAmB,aAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,SAAS,sBAAsB,KAAG,cAMnH,CAAA;AAEL,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,aAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,SAAS,sBAAsB,KAAG,cAMzH,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import { decodeSpecificationQuery, decodeSummaryQuery, decodeTransitionQuery, decodeWorkcardQuery, } from '@owlmeans/planning';
|
|
3
|
+
import { concealed, handlerFacade } from '../utils/index.js';
|
|
4
|
+
/** Cards of the caller's entity; the query arrives in its wire shape and is decoded here. */
|
|
5
|
+
export const listCards = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
6
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
7
|
+
return await facade.cards.list(decodeWorkcardQuery(req.query));
|
|
8
|
+
}));
|
|
9
|
+
/** Intrinsic counts of DIRECT children per parent. */
|
|
10
|
+
export const summarizeCards = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
11
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
12
|
+
const { parents, ...query } = decodeSummaryQuery(req.query);
|
|
13
|
+
return await facade.cards.summary(parents, query);
|
|
14
|
+
}));
|
|
15
|
+
/** @throws {WorkcardNotFound} for an absent card and for another entity's alike */
|
|
16
|
+
export const getCard = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
17
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
18
|
+
return await facade.cards.get(`${req.params.id}`);
|
|
19
|
+
}));
|
|
20
|
+
/** One card's log. */
|
|
21
|
+
export const listCardTransitions = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
22
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
23
|
+
const card = await facade.cards.get(`${req.params.id}`);
|
|
24
|
+
return await facade.transitions.list({ ...decodeTransitionQuery(req.query), card: card.id });
|
|
25
|
+
}));
|
|
26
|
+
/** One card's specifications — the current document per category unless `all`. */
|
|
27
|
+
export const listCardSpecifications = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
28
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
29
|
+
const card = await facade.cards.get(`${req.params.id}`);
|
|
30
|
+
return await facade.specifications.list(card.id, decodeSpecificationQuery(req.query));
|
|
31
|
+
}));
|
|
32
|
+
//# sourceMappingURL=cards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.js","sourceRoot":"","sources":["../../src/actions/cards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning';
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The poll tool: a commit's status, held open up to `wait` seconds (clamped to `maxPoll`).
|
|
6
|
+
*
|
|
7
|
+
* status → settled or no wait: answer → subscribe → poll again (a commit landing between the two
|
|
8
|
+
* reads is caught by the subscription) → race the subscription against the timer → answer the
|
|
9
|
+
* status as it is then, still `pending` when nothing landed.
|
|
10
|
+
*
|
|
11
|
+
* @throws {WorkcardNotFound} for another entity's transition
|
|
12
|
+
*/
|
|
13
|
+
export declare const getCommit: (protocol: PlanningProtocols['commit']['get'], opts?: PlanningHandlerOptions) => ReturnType<ReturnType<typeof handlers<Context>>['request']>;
|
|
14
|
+
//# sourceMappingURL=commit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/actions/commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGlE;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,aACV,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,SAAS,sBAAsB,KAC1E,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAqCxD,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import { CommitState, MAX_COMMIT_POLL } from '@owlmeans/planning';
|
|
3
|
+
import { clampSeconds, concealed, handlerFacade } from '../utils/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* The poll tool: a commit's status, held open up to `wait` seconds (clamped to `maxPoll`).
|
|
6
|
+
*
|
|
7
|
+
* status → settled or no wait: answer → subscribe → poll again (a commit landing between the two
|
|
8
|
+
* reads is caught by the subscription) → race the subscription against the timer → answer the
|
|
9
|
+
* status as it is then, still `pending` when nothing landed.
|
|
10
|
+
*
|
|
11
|
+
* @throws {WorkcardNotFound} for another entity's transition
|
|
12
|
+
*/
|
|
13
|
+
export const getCommit = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
14
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
15
|
+
const transition = `${req.params.transition}`;
|
|
16
|
+
let status = await facade.commits.status(transition);
|
|
17
|
+
const wait = clampSeconds(req.query?.wait, opts?.maxPoll ?? MAX_COMMIT_POLL);
|
|
18
|
+
if (status.state !== CommitState.Pending || wait <= 0) {
|
|
19
|
+
return status;
|
|
20
|
+
}
|
|
21
|
+
let landed = false;
|
|
22
|
+
let wake = () => { landed = true; };
|
|
23
|
+
const unsubscribe = await facade.commits.subscribe(event => {
|
|
24
|
+
if (event.transition === transition && event.state !== CommitState.Pending) {
|
|
25
|
+
wake();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
try {
|
|
29
|
+
status = await facade.commits.status(transition);
|
|
30
|
+
if (status.state === CommitState.Pending) {
|
|
31
|
+
if (!landed) {
|
|
32
|
+
await new Promise(resolve => {
|
|
33
|
+
const timer = setTimeout(resolve, wait * 1000);
|
|
34
|
+
wake = () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
resolve();
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
status = await facade.commits.status(transition);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
unsubscribe();
|
|
45
|
+
}
|
|
46
|
+
return status;
|
|
47
|
+
}));
|
|
48
|
+
//# sourceMappingURL=commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../src/actions/commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGjE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE1E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,QAA4C,EAAE,IAA6B,EACd,EAAE,CAC/D,QAAQ,EAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,IAA2B,EAAE;IACpG,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;IAClD,MAAM,UAAU,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAA;IAE7C,IAAI,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACpD,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,eAAe,CAAC,CAAA;IAC5E,IAAI,MAAM,CAAC,KAAK,KAAK,WAAW,CAAC,OAAO,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACtD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,IAAI,IAAI,GAAe,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAA,CAAC,CAAC,CAAA;IAC9C,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;QACzD,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,CAAC,OAAO,EAAE,CAAC;YAC3E,IAAI,EAAE,CAAA;QACR,CAAC;IACH,CAAC,CAAC,CAAA;IACF,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAChD,IAAI,MAAM,CAAC,KAAK,KAAK,WAAW,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;oBAChC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC,CAAA;oBAC9C,IAAI,GAAG,GAAG,EAAE;wBACV,YAAY,CAAC,KAAK,CAAC,CAAA;wBACnB,OAAO,EAAE,CAAA;oBACX,CAAC,CAAA;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAClD,CAAC;IACH,CAAC;YAAS,CAAC;QACT,WAAW,EAAE,CAAA;IACf,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import type { ExecuteOptions, ExecuteRequest, PlanningProtocols, PlanningScope, TransitionExecution } from '@owlmeans/planning';
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a wire execution may say. `actor` is dropped — the scope names the writer — and a create's
|
|
6
|
+
* `createdBy` is the authenticated subject, whatever the body claimed.
|
|
7
|
+
*/
|
|
8
|
+
export declare const wireExecution: (body: ExecuteRequest, scope: PlanningScope) => TransitionExecution;
|
|
9
|
+
/** `wait`/`timeout` as a server grants them: the timeout never holds a request past `maxPoll`. */
|
|
10
|
+
export declare const executeOptionsOf: (body: Pick<ExecuteRequest, 'wait' | 'timeout'>, maxPoll: number) => ExecuteOptions;
|
|
11
|
+
/** The one write route. */
|
|
12
|
+
export declare const executePlanning: (protocol: PlanningProtocols['execute'], opts?: PlanningHandlerOptions) => ReturnType<ReturnType<typeof handlers<Context>>['request']>;
|
|
13
|
+
//# sourceMappingURL=execute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/actions/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,KAAK,EACV,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EACtF,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGlE;;;GAGG;AACH,eAAO,MAAM,aAAa,SAAU,cAAc,SAAS,aAAa,KAAG,mBAQ1E,CAAA;AAED,kGAAkG;AAClG,eAAO,MAAM,gBAAgB,SAAU,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC,WAAW,MAAM,KAAG,cAOlG,CAAA;AAED,2BAA2B;AAC3B,eAAO,MAAM,eAAe,aAChB,iBAAiB,CAAC,SAAS,CAAC,SAAS,sBAAsB,KACpE,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAWxD,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import { DEFAULT_COMMIT_TIMEOUT, MAX_COMMIT_POLL, TransitionAction } from '@owlmeans/planning';
|
|
3
|
+
import { concealed, handlerFacade } from '../utils/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a wire execution may say. `actor` is dropped — the scope names the writer — and a create's
|
|
6
|
+
* `createdBy` is the authenticated subject, whatever the body claimed.
|
|
7
|
+
*/
|
|
8
|
+
export const wireExecution = (body, scope) => {
|
|
9
|
+
const { wait: _wait, timeout: _timeout, actor: _actor, ...exec } = body;
|
|
10
|
+
if (exec.action === TransitionAction.Create && exec.card != null && typeof exec.card === 'object') {
|
|
11
|
+
const { createdBy: _createdBy, ...draft } = exec.card;
|
|
12
|
+
const subject = scope.profileId ?? scope.userId;
|
|
13
|
+
exec.card = subject != null ? { ...draft, createdBy: subject } : draft;
|
|
14
|
+
}
|
|
15
|
+
return exec;
|
|
16
|
+
};
|
|
17
|
+
/** `wait`/`timeout` as a server grants them: the timeout never holds a request past `maxPoll`. */
|
|
18
|
+
export const executeOptionsOf = (body, maxPoll) => {
|
|
19
|
+
const ceiling = maxPoll * 1000;
|
|
20
|
+
if (body.wait !== true) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const asked = Number(body.timeout ?? DEFAULT_COMMIT_TIMEOUT);
|
|
24
|
+
return { wait: true, timeout: Math.min(Number.isFinite(asked) && asked > 0 ? asked : DEFAULT_COMMIT_TIMEOUT, ceiling) };
|
|
25
|
+
};
|
|
26
|
+
/** The one write route. */
|
|
27
|
+
export const executePlanning = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
28
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
29
|
+
const body = (req.body ?? {});
|
|
30
|
+
const receipt = await facade.execute(wireExecution(body, facade.scope), executeOptionsOf(body, opts?.maxPoll ?? MAX_COMMIT_POLL));
|
|
31
|
+
return receipt.card !== undefined
|
|
32
|
+
? { transition: receipt.transition, card: receipt.card }
|
|
33
|
+
: { transition: receipt.transition };
|
|
34
|
+
}));
|
|
35
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/actions/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAK9F,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAoB,EAAE,KAAoB,EAAuB,EAAE;IAC/F,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IACvE,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAClG,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,IAAI,CAAA;QACrD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,CAAA;QAC/C,IAAI,CAAC,IAAI,GAAG,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IACxE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED,kGAAkG;AAClG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAA8C,EAAE,OAAe,EAAkB,EAAE;IAClH,MAAM,OAAO,GAAG,OAAO,GAAG,IAAI,CAAA;IAC9B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,sBAAsB,CAAC,CAAA;IAC5D,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,sBAAsB,EAAE,OAAO,CAAC,EAAE,CAAA;AACzH,CAAC,CAAA;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAsC,EAAE,IAA6B,EACR,EAAE,CAC/D,QAAQ,EAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,IAAoC,EAAE;IAC7G,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAmB,CAAA;IAC/C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAClC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,eAAe,CAAC,CAC5F,CAAA;IAED,OAAO,OAAO,CAAC,IAAI,KAAK,SAAS;QAC/B,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;QACxD,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAA;AACxC,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './cards.js';
|
|
2
|
+
export * from './commit.js';
|
|
3
|
+
export * from './execute.js';
|
|
4
|
+
export * from './links.js';
|
|
5
|
+
export * from './schemas.js';
|
|
6
|
+
export * from './specs.js';
|
|
7
|
+
export * from './transitions.js';
|
|
8
|
+
export * from './watch.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './cards.js';
|
|
2
|
+
export * from './commit.js';
|
|
3
|
+
export * from './execute.js';
|
|
4
|
+
export * from './links.js';
|
|
5
|
+
export * from './schemas.js';
|
|
6
|
+
export * from './specs.js';
|
|
7
|
+
export * from './transitions.js';
|
|
8
|
+
export * from './watch.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning';
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js';
|
|
4
|
+
export declare const listLinks: (protocol: PlanningProtocols['link']['list'], opts?: PlanningHandlerOptions) => ReturnType<ReturnType<typeof handlers<Context>>['request']>;
|
|
5
|
+
//# sourceMappingURL=links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/actions/links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGlE,eAAO,MAAM,SAAS,aACV,iBAAiB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,sBAAsB,KACzE,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAKxD,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import { decodeRelationshipQuery } from '@owlmeans/planning';
|
|
3
|
+
import { concealed, handlerFacade } from '../utils/index.js';
|
|
4
|
+
export const listLinks = (protocol, opts) => handlers().request(protocol, async (req, ctx) => concealed(async () => {
|
|
5
|
+
const facade = await handlerFacade(ctx, req, opts);
|
|
6
|
+
return await facade.relationships.list(decodeRelationshipQuery(req.query));
|
|
7
|
+
}));
|
|
8
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/actions/links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAG5D,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE5D,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,QAA2C,EAAE,IAA6B,EACb,EAAE,CAC/D,QAAQ,EAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE;IAC7E,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;IAElD,OAAO,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5E,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { handlers } from '@owlmeans/server-api';
|
|
2
|
+
import type { PlanningProtocols } from '@owlmeans/planning';
|
|
3
|
+
import type { Context, PlanningHandlerOptions } from '../types.js';
|
|
4
|
+
/** The schema bundle the service holds — what a client loads to answer `can()` with no round trip. */
|
|
5
|
+
export declare const listSchemas: (protocol: PlanningProtocols['schema']['list'], opts?: PlanningHandlerOptions) => ReturnType<ReturnType<typeof handlers<Context>>['request']>;
|
|
6
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/actions/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGlE,sGAAsG;AACtG,eAAO,MAAM,WAAW,aACZ,iBAAiB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,sBAAsB,KAC3E,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAMzD,CAAA"}
|