@owlmeans/planning 0.1.18-rc.2 → 0.1.18-rc.21
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 +69 -26
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/planning/SKILL.md +157 -45
- package/build/aliases/types.d.ts +49 -0
- package/build/aliases/types.d.ts.map +1 -0
- package/build/aliases/types.js +2 -0
- package/build/aliases/types.js.map +1 -0
- package/build/aliases.d.ts +4 -0
- package/build/aliases.d.ts.map +1 -0
- package/build/aliases.js +27 -0
- package/build/aliases.js.map +1 -0
- package/build/consts.d.ts +23 -34
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +26 -25
- package/build/consts.js.map +1 -1
- package/build/entrypoints/types.d.ts +96 -0
- package/build/entrypoints/types.d.ts.map +1 -0
- package/build/entrypoints/types.js +2 -0
- package/build/entrypoints/types.js.map +1 -0
- package/build/entrypoints.d.ts +5 -1
- package/build/entrypoints.d.ts.map +1 -1
- package/build/entrypoints.js +17 -6
- package/build/entrypoints.js.map +1 -1
- package/build/errors.d.ts +41 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +61 -0
- package/build/errors.js.map +1 -1
- package/build/helpers/apply/consts.local.d.ts +7 -0
- package/build/helpers/apply/consts.local.d.ts.map +1 -0
- package/build/helpers/apply/consts.local.js +9 -0
- package/build/helpers/apply/consts.local.js.map +1 -0
- package/build/helpers/apply/types.d.ts +32 -0
- package/build/helpers/apply/types.d.ts.map +1 -0
- package/build/helpers/apply/types.js +2 -0
- package/build/helpers/apply/types.js.map +1 -0
- package/build/helpers/apply.d.ts +5 -23
- package/build/helpers/apply.d.ts.map +1 -1
- package/build/helpers/apply.js +121 -143
- package/build/helpers/apply.js.map +1 -1
- package/build/helpers/card/types.d.ts +20 -0
- package/build/helpers/card/types.d.ts.map +1 -0
- package/build/helpers/card/types.js +2 -0
- package/build/helpers/card/types.js.map +1 -0
- package/build/helpers/card.d.ts +11 -7
- package/build/helpers/card.d.ts.map +1 -1
- package/build/helpers/card.js +41 -28
- package/build/helpers/card.js.map +1 -1
- package/build/helpers/changes/consts.local.d.ts +13 -0
- package/build/helpers/changes/consts.local.d.ts.map +1 -0
- package/build/helpers/changes/consts.local.js +13 -0
- package/build/helpers/changes/consts.local.js.map +1 -0
- package/build/helpers/changes/types.d.ts +58 -0
- package/build/helpers/changes/types.d.ts.map +1 -0
- package/build/helpers/changes/types.js +2 -0
- package/build/helpers/changes/types.js.map +1 -0
- package/build/helpers/changes.d.ts +11 -42
- package/build/helpers/changes.d.ts.map +1 -1
- package/build/helpers/changes.js +234 -251
- package/build/helpers/changes.js.map +1 -1
- package/build/helpers/code/types.d.ts +27 -0
- package/build/helpers/code/types.d.ts.map +1 -0
- package/build/helpers/code/types.js +2 -0
- package/build/helpers/code/types.js.map +1 -0
- package/build/helpers/code.d.ts +6 -17
- package/build/helpers/code.d.ts.map +1 -1
- package/build/helpers/code.js +39 -40
- package/build/helpers/code.js.map +1 -1
- package/build/helpers/index.d.ts +12 -0
- package/build/helpers/index.d.ts.map +1 -1
- package/build/helpers/index.js +2 -0
- package/build/helpers/index.js.map +1 -1
- package/build/helpers/query/types.d.ts +23 -0
- package/build/helpers/query/types.d.ts.map +1 -0
- package/build/helpers/query/types.js +2 -0
- package/build/helpers/query/types.js.map +1 -0
- package/build/helpers/query.d.ts +25 -17
- package/build/helpers/query.d.ts.map +1 -1
- package/build/helpers/query.js +108 -280
- package/build/helpers/query.js.map +1 -1
- package/build/helpers/scoped/types.d.ts +59 -0
- package/build/helpers/scoped/types.d.ts.map +1 -0
- package/build/helpers/scoped/types.js +2 -0
- package/build/helpers/scoped/types.js.map +1 -0
- package/build/helpers/scoped.d.ts +24 -0
- package/build/helpers/scoped.d.ts.map +1 -0
- package/build/helpers/scoped.js +197 -0
- package/build/helpers/scoped.js.map +1 -0
- package/build/helpers/specification/types.d.ts +24 -0
- package/build/helpers/specification/types.d.ts.map +1 -0
- package/build/helpers/specification/types.js +2 -0
- package/build/helpers/specification/types.js.map +1 -0
- package/build/helpers/specification.d.ts +9 -13
- package/build/helpers/specification.d.ts.map +1 -1
- package/build/helpers/specification.js +47 -44
- package/build/helpers/specification.js.map +1 -1
- package/build/helpers/status/types.d.ts +48 -0
- package/build/helpers/status/types.d.ts.map +1 -0
- package/build/helpers/status/types.js +2 -0
- package/build/helpers/status/types.js.map +1 -0
- package/build/helpers/status.d.ts +16 -29
- package/build/helpers/status.d.ts.map +1 -1
- package/build/helpers/status.js +77 -73
- package/build/helpers/status.js.map +1 -1
- package/build/helpers/types.local.d.ts +17 -0
- package/build/helpers/types.local.d.ts.map +1 -0
- package/build/helpers/types.local.js +2 -0
- package/build/helpers/types.local.js.map +1 -0
- package/build/helpers/validate/types.d.ts +30 -0
- package/build/helpers/validate/types.d.ts.map +1 -0
- package/build/helpers/validate/types.js +2 -0
- package/build/helpers/validate/types.js.map +1 -0
- package/build/helpers/validate.d.ts +8 -19
- package/build/helpers/validate.d.ts.map +1 -1
- package/build/helpers/validate.js +74 -79
- package/build/helpers/validate.js.map +1 -1
- package/build/helpers/wire/types.d.ts +28 -0
- package/build/helpers/wire/types.d.ts.map +1 -0
- package/build/helpers/wire/types.js +2 -0
- package/build/helpers/wire/types.js.map +1 -0
- package/build/helpers/wire.d.ts +4 -0
- package/build/helpers/wire.d.ts.map +1 -0
- package/build/helpers/wire.js +215 -0
- package/build/helpers/wire.js.map +1 -0
- package/build/i18n/be.json +5 -0
- package/build/i18n/de.json +5 -0
- package/build/i18n/en.json +5 -0
- package/build/i18n/es.json +5 -0
- package/build/i18n/fr.json +56 -0
- package/build/i18n/pl.json +5 -0
- package/build/i18n/ru.json +5 -0
- package/build/i18n/uk.json +5 -0
- package/build/i18n.js +5 -4
- package/build/i18n.js.map +1 -1
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/models/index.d.ts +2 -3
- package/build/models/index.d.ts.map +1 -1
- package/build/models/index.js +1 -15
- package/build/models/index.js.map +1 -1
- package/build/models/model.d.ts +5 -0
- package/build/models/model.d.ts.map +1 -0
- package/build/models/model.js +16 -0
- package/build/models/model.js.map +1 -0
- package/build/models/project.d.ts +2 -1
- package/build/models/project.d.ts.map +1 -1
- package/build/models/project.js.map +1 -1
- package/build/models/specification.d.ts +2 -1
- package/build/models/specification.d.ts.map +1 -1
- package/build/models/specification.js.map +1 -1
- package/build/models/types.d.ts +69 -0
- package/build/models/types.d.ts.map +1 -0
- package/build/models/types.js +2 -0
- package/build/models/types.js.map +1 -0
- package/build/models/workcard.d.ts +2 -1
- package/build/models/workcard.d.ts.map +1 -1
- package/build/models/workcard.js +12 -12
- package/build/models/workcard.js.map +1 -1
- package/build/schemas.d.ts +10 -2
- package/build/schemas.d.ts.map +1 -1
- package/build/schemas.js +87 -1
- package/build/schemas.js.map +1 -1
- package/build/types.d.ts +199 -148
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +6 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +6 -0
- package/build/utils.js.map +1 -0
- package/package.json +11 -11
- package/src/aliases/types.ts +38 -0
- package/src/aliases.ts +31 -0
- package/src/consts.ts +26 -25
- package/src/entrypoints/types.ts +68 -0
- package/src/entrypoints.ts +34 -14
- package/src/errors.ts +71 -0
- package/src/helpers/apply/consts.local.ts +10 -0
- package/src/helpers/apply/types.ts +32 -0
- package/src/helpers/apply.ts +135 -152
- package/src/helpers/card/types.ts +20 -0
- package/src/helpers/card.ts +59 -33
- package/src/helpers/changes/consts.local.ts +17 -0
- package/src/helpers/changes/types.ts +66 -0
- package/src/helpers/changes.ts +267 -280
- package/src/helpers/code/types.ts +31 -0
- package/src/helpers/code.ts +52 -48
- package/src/helpers/index.ts +13 -0
- package/src/helpers/query/types.ts +26 -0
- package/src/helpers/query.ts +144 -314
- package/src/helpers/scoped/types.ts +64 -0
- package/src/helpers/scoped.ts +257 -0
- package/src/helpers/specification/types.ts +26 -0
- package/src/helpers/specification.ts +69 -51
- package/src/helpers/status/types.ts +46 -0
- package/src/helpers/status.ts +117 -93
- package/src/helpers/types.local.ts +18 -0
- package/src/helpers/validate/types.ts +30 -0
- package/src/helpers/validate.ts +84 -77
- package/src/helpers/wire/types.ts +31 -0
- package/src/helpers/wire.ts +251 -0
- package/src/i18n/be.json +5 -0
- package/src/i18n/de.json +5 -0
- package/src/i18n/en.json +5 -0
- package/src/i18n/es.json +5 -0
- package/src/i18n/fr.json +56 -0
- package/src/i18n/pl.json +5 -0
- package/src/i18n/ru.json +5 -0
- package/src/i18n/uk.json +5 -0
- package/src/i18n.ts +6 -4
- package/src/index.ts +4 -0
- package/src/models/index.ts +2 -17
- package/src/models/model.ts +18 -0
- package/src/models/project.ts +2 -1
- package/src/models/specification.ts +2 -1
- package/src/models/types.ts +70 -0
- package/src/models/workcard.ts +14 -16
- package/src/schemas.ts +98 -2
- package/src/types.ts +207 -126
- package/src/utils.ts +9 -0
- package/tests/apply.spec.ts +95 -31
- package/tests/code.spec.ts +9 -9
- package/tests/entrypoints.spec.ts +18 -2
- package/tests/error-status.spec.ts +6 -1
- package/tests/models.spec.ts +16 -15
- package/tests/query.spec.ts +13 -15
- package/tests/schemas.spec.ts +8 -8
- package/tests/scoped.spec.ts +154 -0
- package/tests/status.spec.ts +31 -22
package/src/aliases.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PlanningAliases, PlanningAliasHelper, PlanningDefinitionAliases } from './aliases/types.js'
|
|
2
|
+
|
|
3
|
+
export const createPlanningAliasHelper = (): PlanningAliasHelper => {
|
|
4
|
+
const planningAliases = (base: string): PlanningAliases => Object.freeze({
|
|
5
|
+
base,
|
|
6
|
+
schema: Object.freeze({ list: `${base}:schema:list` }),
|
|
7
|
+
card: Object.freeze({
|
|
8
|
+
list: `${base}:card:list`,
|
|
9
|
+
summary: `${base}:card:summary`,
|
|
10
|
+
get: `${base}:card:get`,
|
|
11
|
+
transitions: `${base}:card:transitions`,
|
|
12
|
+
specifications: `${base}:card:specifications`,
|
|
13
|
+
}),
|
|
14
|
+
spec: Object.freeze({
|
|
15
|
+
get: `${base}:specification:get`,
|
|
16
|
+
revisions: `${base}:specification:revisions`,
|
|
17
|
+
}),
|
|
18
|
+
link: Object.freeze({ list: `${base}:link:list` }),
|
|
19
|
+
transition: Object.freeze({ get: `${base}:transition:get` }),
|
|
20
|
+
execute: `${base}:execute`,
|
|
21
|
+
commit: Object.freeze({ get: `${base}:commit:get`, events: `${base}:commit:events` }),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const planningDefinitionAliases = (base: string): PlanningDefinitionAliases => Object.freeze({
|
|
25
|
+
define: `${base}:schema:define`,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return { planningAliases, planningDefinitionAliases }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const planningAliasHelper = createPlanningAliasHelper()
|
package/src/consts.ts
CHANGED
|
@@ -65,6 +65,32 @@ export enum CodeScope {
|
|
|
65
65
|
Entity = 'entity',
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** What a data-defined schema record declares: a card type or a status flow. */
|
|
69
|
+
export enum PlanningSchemaKind {
|
|
70
|
+
Type = 'type',
|
|
71
|
+
Flow = 'flow',
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The layer a resolved type or flow comes from. `code` is the in-process registry (plugins),
|
|
76
|
+
* `entity` an organization-wide record, `project` a record scoped to one project card.
|
|
77
|
+
*/
|
|
78
|
+
export enum SchemaOrigin {
|
|
79
|
+
Code = 'code',
|
|
80
|
+
Entity = 'entity',
|
|
81
|
+
Project = 'project',
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** How a `schema.define` request writes its declarations. */
|
|
85
|
+
export enum SchemaWriteMode {
|
|
86
|
+
/** Each declaration lands at its layer's next version. */
|
|
87
|
+
Define = 'define',
|
|
88
|
+
/** Compare-and-set: each declaration's `version` must be its layer's next version. */
|
|
89
|
+
Put = 'put',
|
|
90
|
+
/** Only keys the layer lacks are written, at version 1. */
|
|
91
|
+
Seed = 'seed',
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
/** Service alias of the planning facade host. Identical on the server and in a client. */
|
|
69
95
|
export const PLANNING_SERVICE = 'planning'
|
|
70
96
|
|
|
@@ -109,28 +135,3 @@ export const QUERY_TEXT_MAX = 256
|
|
|
109
135
|
/** The longest list a wire query carries in one value (ids, parents). */
|
|
110
136
|
export const MAX_QUERY_LIST = 500
|
|
111
137
|
|
|
112
|
-
/**
|
|
113
|
-
* The aliases one planning tree answers under, derived from the mount's base alias.
|
|
114
|
-
*
|
|
115
|
-
* Two mounts of the tree in one deployment are two base aliases, never two copies of this table —
|
|
116
|
-
* which is why nothing here is a global constant.
|
|
117
|
-
*/
|
|
118
|
-
export const planningAliases = (base: string) => Object.freeze({
|
|
119
|
-
base,
|
|
120
|
-
schema: Object.freeze({ list: `${base}:schema:list` }),
|
|
121
|
-
card: Object.freeze({
|
|
122
|
-
list: `${base}:card:list`,
|
|
123
|
-
summary: `${base}:card:summary`,
|
|
124
|
-
get: `${base}:card:get`,
|
|
125
|
-
transitions: `${base}:card:transitions`,
|
|
126
|
-
specifications: `${base}:card:specifications`,
|
|
127
|
-
}),
|
|
128
|
-
spec: Object.freeze({
|
|
129
|
-
get: `${base}:specification:get`,
|
|
130
|
-
revisions: `${base}:specification:revisions`,
|
|
131
|
-
}),
|
|
132
|
-
link: Object.freeze({ list: `${base}:link:list` }),
|
|
133
|
-
transition: Object.freeze({ get: `${base}:transition:get` }),
|
|
134
|
-
execute: `${base}:execute`,
|
|
135
|
-
commit: Object.freeze({ get: `${base}:commit:get`, events: `${base}:commit:events` }),
|
|
136
|
-
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { EntrypointProtocol, OpenRequest, OpenValue } from '@owlmeans/entrypoint'
|
|
2
|
+
import type { ListResult } from '@owlmeans/resource'
|
|
3
|
+
import type { RouteParent } from '@owlmeans/route'
|
|
4
|
+
import type {
|
|
5
|
+
CommitEvent, CommitFeedQuery, CommitQuery, CommitStatus, ExecuteRequest, Relationship, RelationshipQueryWire,
|
|
6
|
+
RevisionsQuery, SchemaDefineReply, SchemaDefineRequest, SchemaListQuery, ScopedSchemaBundle, Specification,
|
|
7
|
+
SpecificationQueryWire, SpecificationRevisionList, SummaryQueryWire, SummaryView, Transition, TransitionParams,
|
|
8
|
+
TransitionQueryWire, TransitionReceiptView, Workcard, WorkcardParams, WorkcardQueryWire,
|
|
9
|
+
} from '../types.js'
|
|
10
|
+
|
|
11
|
+
export interface PlanningBaseOptions {
|
|
12
|
+
/** The base alias every leaf alias derives from (`planningAliases`). */
|
|
13
|
+
alias: string
|
|
14
|
+
/** Defaults to `PLANNING_PATH`. */
|
|
15
|
+
path?: string
|
|
16
|
+
parent?: RouteParent
|
|
17
|
+
service?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PlanningProtocolOptions {
|
|
21
|
+
base: PlanningBaseOptions
|
|
22
|
+
/** The guard(s) the base carries; every HTTP leaf inherits them. */
|
|
23
|
+
guards: string | readonly string[]
|
|
24
|
+
gate?: { alias: string, params?: string | readonly string[] }
|
|
25
|
+
/**
|
|
26
|
+
* The socket base the commit feed hangs under. The feed inherits THAT base's guards; the
|
|
27
|
+
* planning base when omitted.
|
|
28
|
+
*/
|
|
29
|
+
socketBase?: RouteParent
|
|
30
|
+
/**
|
|
31
|
+
* Declare the data-defined schema surface: `schema.list` takes a `project` query and answers
|
|
32
|
+
* that layer's scoped bundle, and `schema.define` writes. Off by default — a tree without it
|
|
33
|
+
* declares exactly the other leaves.
|
|
34
|
+
*/
|
|
35
|
+
definitions?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PlanningProtocols {
|
|
39
|
+
base: EntrypointProtocol<OpenRequest, OpenValue>
|
|
40
|
+
schema: {
|
|
41
|
+
/** The plain bundle — or, on a tree declared with `definitions`, a layer's scoped one. */
|
|
42
|
+
list: EntrypointProtocol<{ query?: SchemaListQuery }, ScopedSchemaBundle>
|
|
43
|
+
/** Present on a tree declared with `definitions` only. */
|
|
44
|
+
define?: EntrypointProtocol<{ body: SchemaDefineRequest }, SchemaDefineReply>
|
|
45
|
+
}
|
|
46
|
+
card: {
|
|
47
|
+
list: EntrypointProtocol<{ query: WorkcardQueryWire }, ListResult<Workcard>>
|
|
48
|
+
summary: EntrypointProtocol<{ query: SummaryQueryWire }, SummaryView>
|
|
49
|
+
get: EntrypointProtocol<{ params: WorkcardParams }, Workcard>
|
|
50
|
+
transitions: EntrypointProtocol<{ params: WorkcardParams, query: TransitionQueryWire }, ListResult<Transition>>
|
|
51
|
+
specifications: EntrypointProtocol<{ params: WorkcardParams, query: SpecificationQueryWire }, ListResult<Specification>>
|
|
52
|
+
}
|
|
53
|
+
spec: {
|
|
54
|
+
get: EntrypointProtocol<{ params: WorkcardParams }, Specification>
|
|
55
|
+
revisions: EntrypointProtocol<{ params: WorkcardParams, query: RevisionsQuery }, SpecificationRevisionList>
|
|
56
|
+
}
|
|
57
|
+
link: {
|
|
58
|
+
list: EntrypointProtocol<{ query: RelationshipQueryWire }, ListResult<Relationship>>
|
|
59
|
+
}
|
|
60
|
+
transition: {
|
|
61
|
+
get: EntrypointProtocol<{ params: TransitionParams }, Transition>
|
|
62
|
+
}
|
|
63
|
+
execute: EntrypointProtocol<{ body: ExecuteRequest }, TransitionReceiptView>
|
|
64
|
+
commit: {
|
|
65
|
+
get: EntrypointProtocol<{ params: TransitionParams, query: CommitQuery }, CommitStatus>
|
|
66
|
+
events: EntrypointProtocol<{ query: CommitFeedQuery }, CommitEvent>
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/entrypoints.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
|
|
2
2
|
import type { ListResult } from '@owlmeans/resource'
|
|
3
|
-
import { backend, route, RouteMethod, socket } from '@owlmeans/route'
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
import { backend, route, RouteMethod, socket, type RouteOptions } from '@owlmeans/route'
|
|
4
|
+
import { PLANNING_PATH } from './consts.js'
|
|
5
|
+
import { planningAliasHelper } from './aliases.js'
|
|
6
6
|
import {
|
|
7
7
|
CommitQuerySchema, ExecuteRequestSchema, RelationshipQuerySchema, RevisionsQuerySchema,
|
|
8
|
-
|
|
9
|
-
WorkcardParamsSchema, WorkcardQuerySchema,
|
|
8
|
+
SchemaDefineRequestSchema, SchemaListQuerySchema, SpecificationQuerySchema, SummaryQuerySchema,
|
|
9
|
+
TransitionParamsSchema, TransitionQuerySchema, WorkcardParamsSchema, WorkcardQuerySchema,
|
|
10
10
|
} from './schemas.js'
|
|
11
|
+
import type { PlanningProtocolOptions, PlanningProtocols } from './entrypoints/types.js'
|
|
11
12
|
import type {
|
|
12
|
-
CommitEvent, CommitFeedQuery, CommitQuery, CommitStatus, ExecuteRequest,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
WorkcardQueryWire,
|
|
13
|
+
CommitEvent, CommitFeedQuery, CommitQuery, CommitStatus, ExecuteRequest,
|
|
14
|
+
PlanningSchemaBundle, Relationship, RelationshipQueryWire, RevisionsQuery,
|
|
15
|
+
SchemaDefineReply, SchemaDefineRequest, SchemaListQuery, ScopedSchemaBundle, Specification, SpecificationQueryWire,
|
|
16
|
+
SpecificationRevisionList, SummaryQueryWire, SummaryView, Transition, TransitionParams,
|
|
17
|
+
TransitionQueryWire, TransitionReceiptView, Workcard, WorkcardParams, WorkcardQueryWire,
|
|
17
18
|
} from './types.js'
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -26,9 +27,13 @@ import type {
|
|
|
26
27
|
*
|
|
27
28
|
* Queries travel as their `*Wire` shapes: encode with `encode*Query` before a call, decode with
|
|
28
29
|
* `decode*Query` in a handler.
|
|
30
|
+
*
|
|
31
|
+
* `definitions: true` adds the data-defined schema surface: `schema.list` takes a `project` query
|
|
32
|
+
* and answers that layer's scoped bundle, and `schema.define` (POST `/schemas`) writes. Without it
|
|
33
|
+
* the tree declares exactly the other leaves.
|
|
29
34
|
*/
|
|
30
35
|
export const makePlanningProtocols = (opts: PlanningProtocolOptions): PlanningProtocols => {
|
|
31
|
-
const aliases = planningAliases(opts.base.alias)
|
|
36
|
+
const aliases = planningAliasHelper.planningAliases(opts.base.alias)
|
|
32
37
|
const mount: Partial<RouteOptions> = {
|
|
33
38
|
...(opts.base.parent != null ? { parent: opts.base.parent } : {}),
|
|
34
39
|
...(opts.base.service != null ? { service: opts.base.service } : {}),
|
|
@@ -40,12 +45,27 @@ export const makePlanningProtocols = (opts: PlanningProtocolOptions): PlanningPr
|
|
|
40
45
|
})
|
|
41
46
|
const get = (alias: string, path: string) => route(alias, path, backend({ parent: base }, RouteMethod.GET))
|
|
42
47
|
|
|
48
|
+
const schema: PlanningProtocols['schema'] = opts.definitions === true
|
|
49
|
+
? Object.freeze({
|
|
50
|
+
list: protocol(
|
|
51
|
+
get(aliases.schema.list, '/schemas'),
|
|
52
|
+
contract.request({ query: typed<SchemaListQuery>(SchemaListQuerySchema) }, typed<ScopedSchemaBundle>())
|
|
53
|
+
) as unknown as PlanningProtocols['schema']['list'],
|
|
54
|
+
define: protocol(
|
|
55
|
+
route(planningAliasHelper.planningDefinitionAliases(opts.base.alias).define, '/schemas', backend({ parent: base }, RouteMethod.POST)),
|
|
56
|
+
contract.request({ body: typed<SchemaDefineRequest>(SchemaDefineRequestSchema) }, typed<SchemaDefineReply>())
|
|
57
|
+
),
|
|
58
|
+
})
|
|
59
|
+
: Object.freeze({
|
|
60
|
+
list: protocol(
|
|
61
|
+
get(aliases.schema.list, '/schemas'), contract(typed<PlanningSchemaBundle>())
|
|
62
|
+
) as unknown as PlanningProtocols['schema']['list'],
|
|
63
|
+
})
|
|
64
|
+
|
|
43
65
|
return Object.freeze({
|
|
44
66
|
base,
|
|
45
67
|
|
|
46
|
-
schema
|
|
47
|
-
list: protocol(get(aliases.schema.list, '/schemas'), contract(typed<PlanningSchemaBundle>())),
|
|
48
|
-
}),
|
|
68
|
+
schema,
|
|
49
69
|
|
|
50
70
|
card: Object.freeze({
|
|
51
71
|
list: protocol(
|
package/src/errors.ts
CHANGED
|
@@ -206,6 +206,72 @@ export class PlanningScopeMismatch extends PlanningError {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
/**
|
|
210
|
+
* A data-defined schema write lost its compare-and-set: the layer's record is not at the version
|
|
211
|
+
* the write replaces. Re-read the layer and retry.
|
|
212
|
+
*/
|
|
213
|
+
export class SchemaConflict extends PlanningError {
|
|
214
|
+
public static override typeName: string = `${PlanningError.typeName}SchemaConflict`
|
|
215
|
+
/** The stored record moved past the expected version: answered 409. */
|
|
216
|
+
public static httpStatus = 409
|
|
217
|
+
|
|
218
|
+
constructor(message: string = 'error') {
|
|
219
|
+
super(`schema-conflict:${message}`)
|
|
220
|
+
this.type = SchemaConflict.typeName
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** A flow cannot be retired while a live type of an affected layer still resolves it. */
|
|
225
|
+
export class SchemaInUse extends PlanningError {
|
|
226
|
+
public static override typeName: string = `${PlanningError.typeName}SchemaInUse`
|
|
227
|
+
/** Other declarations still depend on the key: answered 409. */
|
|
228
|
+
public static httpStatus = 409
|
|
229
|
+
|
|
230
|
+
constructor(message: string = 'error') {
|
|
231
|
+
super(`schema-in-use:${message}`)
|
|
232
|
+
this.type = SchemaInUse.typeName
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The key is registered in code and does not declare itself `overridable` — or it is a project
|
|
238
|
+
* or specification type, which only code declares.
|
|
239
|
+
*/
|
|
240
|
+
export class SchemaSealed extends PlanningError {
|
|
241
|
+
public static override typeName: string = `${PlanningError.typeName}SchemaSealed`
|
|
242
|
+
/** The key cannot be declared as data: answered 422. */
|
|
243
|
+
public static httpStatus = 422
|
|
244
|
+
|
|
245
|
+
constructor(message: string = 'error') {
|
|
246
|
+
super(`schema-sealed:${message}`)
|
|
247
|
+
this.type = SchemaSealed.typeName
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** A data-defined declaration fails its closed-form checks. */
|
|
252
|
+
export class SchemaInvalid extends PlanningError {
|
|
253
|
+
public static override typeName: string = `${PlanningError.typeName}SchemaInvalid`
|
|
254
|
+
/** The declaration's content is refused: answered 422. */
|
|
255
|
+
public static httpStatus = 422
|
|
256
|
+
|
|
257
|
+
constructor(message: string = 'error') {
|
|
258
|
+
super(`schema-invalid:${message}`)
|
|
259
|
+
this.type = SchemaInvalid.typeName
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** The request's access does not grant the write (creating or deleting a project, defining schemas). */
|
|
264
|
+
export class PlanningForbidden extends PlanningError {
|
|
265
|
+
public static override typeName: string = `${PlanningError.typeName}Forbidden`
|
|
266
|
+
/** The caller lacks the grant the write needs: answered 403. */
|
|
267
|
+
public static httpStatus = 403
|
|
268
|
+
|
|
269
|
+
constructor(message: string = 'error') {
|
|
270
|
+
super(`forbidden:${message}`)
|
|
271
|
+
this.type = PlanningForbidden.typeName
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
209
275
|
/** The store behind this type cannot do what was asked (a foreign provider without a log). */
|
|
210
276
|
export class PlanningUnsupported extends PlanningError {
|
|
211
277
|
public static override typeName: string = `${PlanningError.typeName}Unsupported`
|
|
@@ -234,4 +300,9 @@ ResilientError.registerErrorClass(WorkcardConflict)
|
|
|
234
300
|
ResilientError.registerErrorClass(CommitTimeout)
|
|
235
301
|
ResilientError.registerErrorClass(CommitFailed)
|
|
236
302
|
ResilientError.registerErrorClass(PlanningScopeMismatch)
|
|
303
|
+
ResilientError.registerErrorClass(SchemaConflict)
|
|
304
|
+
ResilientError.registerErrorClass(SchemaInUse)
|
|
305
|
+
ResilientError.registerErrorClass(SchemaSealed)
|
|
306
|
+
ResilientError.registerErrorClass(SchemaInvalid)
|
|
307
|
+
ResilientError.registerErrorClass(PlanningForbidden)
|
|
237
308
|
ResilientError.registerErrorClass(PlanningUnsupported)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Never written by a transition's `changes`: the fold takes them from the transition itself. */
|
|
2
|
+
export const IDENTITY_KEYS = new Set(['id', 'kind', 'type', 'entityId', 'seq', 'head', 'createdAt'])
|
|
3
|
+
|
|
4
|
+
/** Cannot be cleared: a record without them is not a record. */
|
|
5
|
+
export const REQUIRED_KEYS = new Set([
|
|
6
|
+
...IDENTITY_KEYS, 'title', 'parents', 'status', 'intrinsic', 'flows', 'labels', 'fields',
|
|
7
|
+
])
|
|
8
|
+
|
|
9
|
+
/** Merged shallowly into the card's own map rather than replacing it. */
|
|
10
|
+
export const MERGED_KEYS = new Set(['fields', 'flows'])
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Relationship, Transition, Workcard } from '../../types.js'
|
|
2
|
+
|
|
3
|
+
/** The pure fold — the card and its edges after one transition. */
|
|
4
|
+
export interface ApplyHelper {
|
|
5
|
+
/**
|
|
6
|
+
* THE fold: the card after one transition. Pure, total, no I/O — every store (memory, Mongo, a
|
|
7
|
+
* client mirror) folds with this and nothing else, so one log always means one card.
|
|
8
|
+
*
|
|
9
|
+
* - An already-applied transition (`seq <= card.seq`) returns the card unchanged: re-folding is
|
|
10
|
+
* safe.
|
|
11
|
+
* - Anything but the next seq (`card.seq + 1`, or 1 for a create with no card) is refused with
|
|
12
|
+
* `PlanningError('fold:out-of-order')`.
|
|
13
|
+
* - The log is replayed as written: a transition the executor would refuse today (an update naming
|
|
14
|
+
* `createdBy`, appended before that refusal existed) still folds. Guards live at admission.
|
|
15
|
+
* - `create` builds the whole record from `changes` plus the transition's identity; `delete`
|
|
16
|
+
* answers `null`; `link`/`unlink` only move `seq`/`head`/`updatedAt` (the edges are
|
|
17
|
+
* {@link ApplyHelper.applyRelationship}'s); `update`/`transit` write `changes` — `fields` and
|
|
18
|
+
* `flows` merge, everything else replaces — and clear `unset`.
|
|
19
|
+
* - Every result has `seq = transition.seq`, `head = max(head, seq)`, `updatedAt = transition.at`.
|
|
20
|
+
*
|
|
21
|
+
* @throws {PlanningError} `planning:fold:out-of-order:…`
|
|
22
|
+
*/
|
|
23
|
+
applyTransition: <T extends Workcard = Workcard>(card: T | null | undefined, transition: Transition) => T | null
|
|
24
|
+
/**
|
|
25
|
+
* The relationships after one transition. Pure and total, like {@link ApplyHelper.applyTransition}.
|
|
26
|
+
*
|
|
27
|
+
* `create` adds its `links`, `link` adds its `link`, `unlink` removes the matching edge, and a
|
|
28
|
+
* `delete` removes every edge touching the card. An edge that already exists is kept as it is, so
|
|
29
|
+
* re-folding adds nothing. Every other action answers the list unchanged. Always a new array.
|
|
30
|
+
*/
|
|
31
|
+
applyRelationship: (links: Relationship[], transition: Transition) => Relationship[]
|
|
32
|
+
}
|