@owlmeans/planning 0.1.18-rc.29 → 0.1.18-rc.30

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 (48) hide show
  1. package/README.md +1 -1
  2. package/agent-meta/manifest.json +9 -4
  3. package/agent-meta/skills/planning/SKILL.md +7 -4
  4. package/agent-meta/skills/planning/parts/mounting.md +3 -0
  5. package/agent-meta/skills/planning/parts/types.md +55 -11
  6. package/build/helpers/app/consts.local.d.ts +5 -0
  7. package/build/helpers/app/consts.local.d.ts.map +1 -0
  8. package/build/helpers/app/consts.local.js +5 -0
  9. package/build/helpers/app/consts.local.js.map +1 -0
  10. package/build/helpers/app/types.d.ts +51 -0
  11. package/build/helpers/app/types.d.ts.map +1 -0
  12. package/build/helpers/app/types.js +2 -0
  13. package/build/helpers/app/types.js.map +1 -0
  14. package/build/helpers/app.d.ts +4 -0
  15. package/build/helpers/app.d.ts.map +1 -0
  16. package/build/helpers/app.js +134 -0
  17. package/build/helpers/app.js.map +1 -0
  18. package/build/helpers/index.d.ts +2 -0
  19. package/build/helpers/index.d.ts.map +1 -1
  20. package/build/helpers/index.js +1 -0
  21. package/build/helpers/index.js.map +1 -1
  22. package/build/helpers/scoped/types.d.ts +19 -7
  23. package/build/helpers/scoped/types.d.ts.map +1 -1
  24. package/build/helpers/scoped.d.ts +3 -3
  25. package/build/helpers/scoped.d.ts.map +1 -1
  26. package/build/helpers/scoped.js +10 -5
  27. package/build/helpers/scoped.js.map +1 -1
  28. package/build/registry.d.ts +3 -2
  29. package/build/registry.d.ts.map +1 -1
  30. package/build/registry.js +25 -3
  31. package/build/registry.js.map +1 -1
  32. package/build/schemas.d.ts +6 -1
  33. package/build/schemas.d.ts.map +1 -1
  34. package/build/schemas.js +16 -0
  35. package/build/schemas.js.map +1 -1
  36. package/build/types.d.ts +64 -1
  37. package/build/types.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/src/helpers/app/consts.local.ts +5 -0
  40. package/src/helpers/app/types.ts +52 -0
  41. package/src/helpers/app.ts +152 -0
  42. package/src/helpers/index.ts +2 -0
  43. package/src/helpers/scoped/types.ts +19 -7
  44. package/src/helpers/scoped.ts +12 -7
  45. package/src/registry.ts +29 -4
  46. package/src/schemas.ts +18 -1
  47. package/src/types.ts +66 -2
  48. package/tests/app.spec.ts +188 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@ the executor with `@owlmeans/server-planning` and reads remotely with `@owlmeans
9
9
  ## Installation
10
10
 
11
11
  ```sh
12
- bun add @owlmeans/planning@^0.1.18-rc.29 ajv ajv-formats
12
+ bun add @owlmeans/planning@^0.1.18-rc.30 ajv ajv-formats
13
13
  ```
14
14
 
15
15
  ## Concepts
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 3,
3
3
  "package": "@owlmeans/planning",
4
- "version": "0.1.18-rc.29",
5
- "generatedAt": "2026-10-20T23:19:58.625Z",
4
+ "version": "0.1.18-rc.30",
5
+ "generatedAt": "2026-10-24T21:07:16.900Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -109,7 +109,7 @@
109
109
  {
110
110
  "name": "types",
111
111
  "file": "skills/planning/parts/types.md",
112
- "description": "Declaring card, project and specification types and status flows as closed data, the data-defined layers (code, entity, project) and retirement, and the rules every record schema keeps.",
112
+ "description": "Declaring card, project and specification types and status flows as closed data, the application's own definition properties and base card fields (`app`), the data-defined layers (code, entity, project) and retirement, and the rules every record schema keeps.",
113
113
  "purposes": [
114
114
  "data"
115
115
  ],
@@ -129,7 +129,12 @@
129
129
  "putFlow",
130
130
  "putAssigneeType",
131
131
  "scopedCardTypes",
132
- "overridable"
132
+ "overridable",
133
+ "appSchemaHelper",
134
+ "PlanningAppDeclarations",
135
+ "PlanningAppSchema",
136
+ "registerApp",
137
+ "fieldsSchema"
133
138
  ]
134
139
  }
135
140
  ]
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/planning
9
9
 
10
10
  **Layer:** Cross-cutting domain
11
- **Install:** `"@owlmeans/planning": "^0.1.18-rc.29"` in `dependencies` (`ajv` and `ajv-formats` are peers)
11
+ **Install:** `"@owlmeans/planning": "^0.1.18-rc.30"` in `dependencies` (`ajv` and `ajv-formats` are peers)
12
12
 
13
13
  The contracts of project planning: record shapes, schemas, refusals, the protocol tree, the pure
14
14
  fold and the models. No database, no fastify, no React. The executor, the plugin registry, the
@@ -46,6 +46,7 @@ forms generated code keeps writing are `server-planning` → Creating and readin
46
46
  |---|---|
47
47
  | `Workcard`, `Card`, `Project`, `Specification`, `Relationship`, `Transition` | The records |
48
48
  | `StatusFlowSchema`, `WorkcardTypeSchema`, `ProjectTypeSchema`, `SpecificationSlot`, `CodePolicy`, `RelationshipType`, `PlanningSchemaBundle` | Type declarations — data, not code |
49
+ | `PlanningAppDeclarations`, `PlanningAppSchema`, `PlanningAppBagKind` | The application's own type/flow properties (`app` bags) and base card fields, declared in code |
49
50
  | `TransitionExecution`, `WorkcardDraft`, `TransitionReceipt`, `CommitEvent`, `CommitSource` | The write path and its commit feed |
50
51
  | `TransitionStore`, `ProjectionStore`, `SpecificationStore`, `RelationshipStore`, `SchemaStore`, `PlanningStore` | Ports a store implements (only `cards` is required) |
51
52
  | `PlanningPlugin`, `PlanningScope`, `PlanningFacade`, `PlanningService`, `PlanningDefinitions` | The plugin seam and the facade, typed here so a plugin and a client can name them |
@@ -61,7 +62,8 @@ forms generated code keeps writing are `server-planning` → Creating and readin
61
62
  | `codeHelper` — `mintCode`, `codeScopeOf`, `slugOf` | Codes |
62
63
  | `specificationHelper` — `slotOf`, `currentSpecification`, `nextRevision`, `specificationTypeOf`, `bodyCharsOf` | Specifications |
63
64
  | `makeAjv`, `validateHelper` — `validateFields`, `validateCard`, `validateSpecificationBody`, `invalidFieldKeys`, `ajvErrorText` | Validation |
64
- | `makeSchemaRegistry(bundle?)` | Types, flows, cached field validators; `bundle()` / `load()` |
65
+ | `makeSchemaRegistry(bundle?)` | Types, flows, the app declarations (`registerApp`, `app`), `fieldsSchema(type)` and its cached validator; `bundle()` / `load()` |
66
+ | `appSchemaHelper` — `merge`, `assertDeclarations`, `bagSchemaOf`, `assertBag`, `fieldsSchemaOf` | The application's declarations: merge order, closed forms, a definition's `app` bag, a type's effective fields schema |
65
67
  | `scopedSchemaHelper` — `resolveScopedBundle`, `scopedRegistryOf`, `assertTypeSchema`, `assertFlowSchema`, `assertOverridable`, `flowInUse`, `schemaRecordKey`, `schemaKeyOf` | Data-defined layers: resolution, the read-only view, the closed-form checks |
66
68
  | `makePlanningProtocols(opts)` | The protocol tree |
67
69
  | `makeWorkcardModel`, `makeProjectModel`, `makeSpecificationModel`, `modelOf`, `executeFor` | Models |
@@ -71,7 +73,8 @@ forms generated code keeps writing are `server-planning` → Creating and readin
71
73
 
72
74
  - **Base fields at the top level, provider fields under `fields`.** `title`, `description`, `code`,
73
75
  `status`, `labels`, `order`, `parent` are generic and indexable; everything a type adds lives in
74
- `fields` and is validated by the type's `fields` JSON schema. Generic code reads only the top.
76
+ `fields` and is validated by the type's `fields` JSON schema — beside the application's base card
77
+ fields (`schemas.app.card`, the `types` part). Generic code reads only the top.
75
78
  A `fields` key never holds `.` or `$` at any depth (`validateHelper.invalidFieldKeys`).
76
79
  - **`entityId` is the organization's stable record id** — never the wire `entitySlug`, never a body
77
80
  value. Every read is scoped by it; where a request's comes from is `server-planning` → Scope and
@@ -150,7 +153,7 @@ Load a part on top of this core when its job is in play.
150
153
 
151
154
  | Part | Load it when |
152
155
  |---|---|
153
- | [types](parts/types.md) | Declaring a type or a status flow, data-defined types and flows (`scopedSchemaHelper`), or a new record schema |
156
+ | [types](parts/types.md) | Declaring a type or a status flow, the application's own properties and base card fields (`app`, `appSchemaHelper`), data-defined types and flows (`scopedSchemaHelper`), or a new record schema |
154
157
  | [fold](parts/fold.md) | Folding transitions (`applyHelper`, `changesHelper`) or translating a `WorkcardQuery` (`queryHelper`, `wireHelper`) |
155
158
  | [mounting](parts/mounting.md) | Declaring or mounting the protocol tree (`makePlanningProtocols`), or serving it in a generated target |
156
159
  | [errors](parts/errors.md) | Throwing, matching or phrasing a planning refusal (marker, HTTP status, i18n text) |
@@ -59,3 +59,6 @@ routes; domain rules are `before` plugins. This section is the hub; each package
59
59
  `overridable: true` with open `fields` (`additionalProperties: true`); the container project type
60
60
  (always sealed) carries `scopedCardTypes: true`, so the app's people override and extend them as
61
61
  data through `schema.define`.
62
+ - What the app itself keeps about every type, flow or card (a type's `description`, an origin's
63
+ attribute planning has no key for) is `PLANNING.schemas.app` — never an extra key on a type or a
64
+ flow (the `types` part, Application-declared properties).
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: types
3
- description: Declaring card, project and specification types and status flows as closed data, the data-defined layers (code, entity, project) and retirement, and the rules every record schema keeps.
3
+ description: Declaring card, project and specification types and status flows as closed data, the application's own definition properties and base card fields (`app`), the data-defined layers (code, entity, project) and retirement, and the rules every record schema keeps.
4
4
  metadata:
5
5
  purposes: data
6
- mentions: WorkcardTypeSchema, ProjectTypeSchema, StatusFlowSchema, SpecificationSlot, CodePolicy, makeSchemaRegistry, scopedSchemaHelper, resolveScopedBundle, ScopedSchemaRecord, ScopedSchemaBundle, SchemaStore, putType, putFlow, putAssigneeType, scopedCardTypes, overridable
6
+ mentions: WorkcardTypeSchema, ProjectTypeSchema, StatusFlowSchema, SpecificationSlot, CodePolicy, makeSchemaRegistry, scopedSchemaHelper, resolveScopedBundle, ScopedSchemaRecord, ScopedSchemaBundle, SchemaStore, putType, putFlow, putAssigneeType, scopedCardTypes, overridable, appSchemaHelper, PlanningAppDeclarations, PlanningAppSchema, registerApp, fieldsSchema
7
7
  ---
8
8
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
9
9
 
@@ -17,16 +17,18 @@ the keys:
17
17
 
18
18
  | Declaration | Required | Optional |
19
19
  |---|---|---|
20
- | card / specification type | `type` (its key), `kind`, `version`, `fields`, `flows` (at least one), `specifications` | `intrinsic`, `relationships`, `labels`, `code`, `label`, `tone`, `icon`, `overridable` |
20
+ | card / specification type | `type` (its key), `kind`, `version`, `fields`, `flows` (at least one), `specifications` | `intrinsic`, `relationships`, `labels`, `code`, `label`, `tone`, `icon`, `overridable`, `app` |
21
21
  | project type | the same, plus `cardTypes` | `projectTypes`, `scopedCardTypes` |
22
- | flow | `id` (its key), `version`, `statuses` (at least one), `transitions` | `label`, `tone`, `icon`, `overridable` |
22
+ | flow | `id` (its key), `version`, `statuses` (at least one), `transitions` | `label`, `tone`, `icon`, `overridable`, `app` |
23
23
  | status | `key`, `intrinsic` | `initial`, `terminal`, `label`, `tone` |
24
24
  | transition rule | `name`, `from` (status keys or `'*'`), `to` | `label`, `explicit` |
25
25
 
26
26
  The display name is `label` everywhere; a type or a flow has no `name`, `key` or `description`
27
- (`key` belongs to a status, `name` to a rule). As data, `definitions` refuses an extra key with
28
- `SchemaInvalid`; in code the registry does not validate a declaration, so only TypeScript's
29
- excess-property check catches it — keep the literal typed.
27
+ (`key` belongs to a status, `name` to a rule). Anything else the application keeps about a type or
28
+ a flow — a `description`, an origin's attribute — goes in its `app` bag, which the application
29
+ declares (below). As data, `definitions` refuses an extra key with `SchemaInvalid`; in code the
30
+ registry does not validate a declaration, so only TypeScript's excess-property check catches it —
31
+ keep the literal typed.
30
32
 
31
33
  - `fields` — the AJV schema of `fields`; the registry compiles and caches it (`validator(type)`).
32
34
  - `specifications` — the slots this type's cards carry as children. A slot may name its
@@ -49,6 +51,48 @@ excess-property check catches it — keep the literal typed.
49
51
  `colors`). An unknown value renders the picker's fallback (`slate`, `circle`); server code and
50
52
  seeds read the data React-free from `@owlmeans/web-panel/picker/vocabulary`.
51
53
 
54
+ ## Application-declared properties (`app`)
55
+
56
+ Two kinds of field exist: the custom ones a data-defined type gives its cards (its `fields`,
57
+ written by the app's people), and the ones the APPLICATION pregenerates in code for every type,
58
+ flow or card. The latter are `PlanningAppDeclarations { type?, flow?, card? }`, declared in
59
+ `PlanningPlugin.schemas.app` (and `PlanningServiceOptions.schemas.app`) and served in the bundle
60
+ (`bundle.app`, `registry.app()`):
61
+
62
+ ```ts
63
+ schemas: { app: {
64
+ type: { properties: { description: { type: 'string', maxLength: 2000 } } },
65
+ flow: { properties: { description: { type: 'string' } } },
66
+ card: { properties: { priority: { type: 'string', enum: ['low', 'high'] } } },
67
+ } }
68
+ // then a type: { type: 'wm:task', …, app: { description: 'Work items' } }
69
+ ```
70
+
71
+ - **Closed form** (`appSchemaHelper.assertDeclarations` → `SchemaInvalid('app:<kind>:…')`): each
72
+ kind is `{ type?: 'object', properties, required? }` and nothing else; property names hold no
73
+ `.` or `$`; every `required` name is declared in the same declaration; the schema compiles.
74
+ - **Merge** (`appSchemaHelper.merge`, run by the server's plugin registry on every `use` and by
75
+ the client over the served bundle): the service's own `schemas.app` first, then each plugin's in
76
+ plugin order (`order` ascending, registration order among equals). Per kind `properties` are
77
+ united — a later declaration of a name replaces the earlier one whole — and `required` is united.
78
+ A plugin replaced by name takes its declarations with it. The result is normalized
79
+ (`type: 'object'`); nothing declared → `undefined` and no `app` key in any bundle.
80
+ - **`type` / `flow` → the definition's `app` bag**, closed over the declared properties
81
+ (`additionalProperties: false`; an absent bag reads `{}`, so a `required` property makes the bag
82
+ mandatory). Checked on every data-defined write — `define`, `put*`, `seed`, the wire
83
+ `schema.define` — by `assertTypeSchema(type, flows, app)` / `assertFlowSchema(flow, app)`. A kind
84
+ the application does not declare refuses any bag (`<kind>:<key>:app:undeclared`). Code
85
+ declarations are not validated (as every code declaration); a record written under earlier
86
+ declarations still resolves.
87
+ - **`card` → base `fields` of every type of kind `card`**, code or data-defined
88
+ (`registry.fieldsSchema(type)`, compiled by `registry.validator(type)`). The type's schema stays
89
+ the root (its `#/…` references still resolve); each base name absent from its top-level
90
+ `properties` is added as `{}` — so its `additionalProperties: false` never refuses a base field —
91
+ and the base object schema is appended to its `allOf`. A type that declares a base name itself
92
+ narrows it (both apply). Projects and specifications are untouched. A required base field, like a
93
+ newly required type field, refuses every later write of a card that lacks it.
94
+ - Without declarations every behavior is the previous one: `fieldsSchema(type)` IS `type.fields`.
95
+
52
96
  ## Data-defined types and flows
53
97
 
54
98
  Card types, assignee types and status flows may also be DATA — `ScopedSchemaRecord`s a store keeps
@@ -64,9 +108,9 @@ assignee types layer **code → entity** and refuse a project-local declaration:
64
108
  - A record's `version` is its compare-and-set token and is written into the declaration's own
65
109
  `version`: a write lands only at its layer's next version, 1 for a new key (`SchemaConflict`).
66
110
  - The closed-form checks are shared by every store: `scopedSchemaHelper.assertFlowSchema` (the declaration's schema,
67
- unique status keys, every `to` declared, `from` `'*'` or declared statuses) and
68
- `scopedSchemaHelper.assertTypeSchema` (the `card` kind, a compiling `fields` schema, unique flows that resolve) —
69
- `SchemaInvalid`.
111
+ unique status keys, every `to` declared, `from` `'*'` or declared statuses, the `app` bag) and
112
+ `scopedSchemaHelper.assertTypeSchema` (the `card` kind, the `app` bag, a `fields` schema that
113
+ compiles with the base card fields, unique flows that resolve) — `SchemaInvalid`.
70
114
  - **Retired** means offered for nothing new, still resolved for what uses it: a retired record
71
115
  gives way to a live declaration below it, and a key retired in every layer resolves retired —
72
116
  present in the bundle's `types`/`flows`, listed in `retired`. A flow cannot be retired while a
@@ -74,7 +118,7 @@ assignee types layer **code → entity** and refuse a project-local declaration:
74
118
  - `scopedSchemaHelper.resolveScopedBundle(code, records, scope)` is the ONE resolution (pure);
75
119
  `scopedRegistryOf` is its read-only registry (`originOf`, `isRetired`; `register*`/`load` refuse). A
76
120
  `ScopedSchemaBundle` extends the plain bundle with `scope`, `revision`, `origins`, `retired`, so a
77
- reader of the plain bundle is unaffected.
121
+ reader of the plain bundle is unaffected; the code bundle's `app` passes through.
78
122
  - `SchemaStore`: `list(where)`, `put(record)` (the CAS), `purge({ entityId, project })`,
79
123
  `revision(entityId)` (monotonic per organization), optional `watch(listener)`.
80
124
  - `PlanningFacade.definitions` (present only where the store has the port): `bundle(project?)`,
@@ -0,0 +1,5 @@
1
+ /** The kinds an application declares properties for, in merge order. */
2
+ export declare const APP_KINDS: readonly ['type', 'flow', 'card'];
3
+ /** The only keywords a kind's declaration may carry — a merge by name honours nothing else. */
4
+ export declare const APP_SCHEMA_KEYS: Set<string>;
5
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../../../src/helpers/app/consts.local.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,eAAO,MAAM,SAAS,YAAI,MAAM,EAAE,MAAM,EAAE,MAAM,CAAU,CAAA;AAE1D,+FAA+F;AAC/F,eAAO,MAAM,eAAe,aAA8C,CAAA"}
@@ -0,0 +1,5 @@
1
+ /** The kinds an application declares properties for, in merge order. */
2
+ export const APP_KINDS = ['type', 'flow', 'card'];
3
+ /** The only keywords a kind's declaration may carry — a merge by name honours nothing else. */
4
+ export const APP_SCHEMA_KEYS = new Set(['type', 'properties', 'required']);
5
+ //# sourceMappingURL=consts.local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../../../src/helpers/app/consts.local.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAU,CAAA;AAE1D,+FAA+F;AAC/F,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAA"}
@@ -0,0 +1,51 @@
1
+ import type { AnySchema } from 'ajv';
2
+ import type { PlanningSchemaKind } from '../../consts.js';
3
+ import type { AnyTypeSchema, PlanningAppDeclarations } from '../../types.js';
4
+ /** The declarations whose properties a definition carries in its `app` bag. */
5
+ export type PlanningAppBagKind = PlanningSchemaKind.Type | PlanningSchemaKind.Flow;
6
+ /**
7
+ * The application's pregenerated planning properties: the `app` bag of every type and flow
8
+ * declaration, and the base `fields` of every card — declared in code, beside the custom fields a
9
+ * data-defined type gives its cards.
10
+ */
11
+ export interface AppSchemaHelper {
12
+ /**
13
+ * Merge declarations in the order given — a planning service passes its own `schemas.app` first,
14
+ * then every plugin's in plugin order (`order` ascending, registration order among equals). Per
15
+ * kind (`type`, `flow`, `card`): `properties` are united, a later declaration of a name replacing
16
+ * the earlier one whole; `required` is the union. `undefined` when no source declares anything.
17
+ *
18
+ * @throws {SchemaInvalid} a malformed source
19
+ */
20
+ merge: (sources: ReadonlyArray<PlanningAppDeclarations | undefined>) => PlanningAppDeclarations | undefined;
21
+ /**
22
+ * The closed form of declarations: per kind only `type` (`'object'`), `properties` (names without
23
+ * `.` or `$`) and `required` (each name declared), and a schema that compiles.
24
+ *
25
+ * @throws {SchemaInvalid} `app:<kind>:…`
26
+ */
27
+ assertDeclarations: (app: PlanningAppDeclarations) => void;
28
+ /**
29
+ * The schema a definition's `app` bag must match: the declared kind closed over its properties
30
+ * (`additionalProperties: false`). `undefined` when the kind is not declared.
31
+ */
32
+ bagSchemaOf: (app: PlanningAppDeclarations | undefined, kind: PlanningAppBagKind) => AnySchema | undefined;
33
+ /**
34
+ * Check a declaration's `app` bag: refused when the kind is undeclared and the bag is present;
35
+ * matched against {@link bagSchemaOf} otherwise (an absent bag is `{}`, so a required property
36
+ * makes the bag mandatory).
37
+ *
38
+ * @throws {SchemaInvalid} `<kind>:<key>:app:…`
39
+ */
40
+ assertBag: (kind: PlanningAppBagKind, key: string, bag: unknown, app?: PlanningAppDeclarations) => void;
41
+ /**
42
+ * The schema a type's `fields` validate against. Without base card fields, or for a project or
43
+ * specification type, it is the type's own `fields` object itself. Otherwise the type's schema
44
+ * stays the root (so its `#/…` references still resolve) and gains: each base name in its
45
+ * top-level `properties` as `{}` when it does not declare it (its `additionalProperties` never
46
+ * refuses a base field; its own declaration of the name still applies and may narrow it), and the
47
+ * base object schema appended to its `allOf`.
48
+ */
49
+ fieldsSchemaOf: (type: AnyTypeSchema, app?: PlanningAppDeclarations) => AnySchema;
50
+ }
51
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/helpers/app/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAE5E,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAA;AAElF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,KAAK,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,uBAAuB,GAAG,SAAS,CAAC,KAAK,uBAAuB,GAAG,SAAS,CAAA;IAC3G;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC1D;;;OAGG;IACH,WAAW,EAAE,CAAC,GAAG,EAAE,uBAAuB,GAAG,SAAS,EAAE,IAAI,EAAE,kBAAkB,KAAK,SAAS,GAAG,SAAS,CAAA;IAC1G;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAA;IACvG;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,uBAAuB,KAAK,SAAS,CAAA;CAClF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/helpers/app/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { AppSchemaHelper } from './app/types.js';
2
+ export declare const createAppSchemaHelper: () => AppSchemaHelper;
3
+ export declare const appSchemaHelper: AppSchemaHelper;
4
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/helpers/app.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,gBAAgB,CAAA;AASzE,eAAO,MAAM,qBAAqB,QAAO,eAwIxC,CAAA;AAED,eAAO,MAAM,eAAe,iBAA0B,CAAA"}
@@ -0,0 +1,134 @@
1
+ import { PlanningSchemaKind, WorkcardKind } from '../consts.js';
2
+ import { SchemaInvalid } from '../errors.js';
3
+ import { APP_KINDS, APP_SCHEMA_KEYS } from './app/consts.local.js';
4
+ import { makeAjv, validateHelper } from './validate.js';
5
+ const messageOf = (error) => error instanceof Error ? error.message : `${error}`;
6
+ const isObject = (value) => value != null && typeof value === 'object' && !Array.isArray(value);
7
+ export const createAppSchemaHelper = () => {
8
+ // Keyed by the declared kind object a registry keeps — a fresh ajv per entry, so a replaced
9
+ // declaration leaves nothing compiled behind.
10
+ const bags = new WeakMap();
11
+ const objectSchemaOf = (declared) => ({
12
+ type: 'object',
13
+ properties: declared.properties,
14
+ ...((declared.required?.length ?? 0) > 0 ? { required: [...declared.required] } : {}),
15
+ });
16
+ const declaredOf = (app, kind) => kind === PlanningSchemaKind.Type ? app?.type : app?.flow;
17
+ const assertDeclarations = app => {
18
+ if (!isObject(app)) {
19
+ throw new SchemaInvalid('app:declarations');
20
+ }
21
+ const kinds = APP_KINDS;
22
+ const stranger = Object.keys(app).find(key => !kinds.includes(key));
23
+ if (stranger != null) {
24
+ throw new SchemaInvalid(`app:kind:${stranger}`);
25
+ }
26
+ for (const kind of APP_KINDS) {
27
+ const declared = app[kind];
28
+ if (declared == null) {
29
+ continue;
30
+ }
31
+ if (!isObject(declared)) {
32
+ throw new SchemaInvalid(`app:${kind}:schema`);
33
+ }
34
+ const keyword = Object.keys(declared).find(key => !APP_SCHEMA_KEYS.has(key));
35
+ if (keyword != null) {
36
+ throw new SchemaInvalid(`app:${kind}:keyword:${keyword}`);
37
+ }
38
+ if (declared.type != null && declared.type !== 'object') {
39
+ throw new SchemaInvalid(`app:${kind}:type`);
40
+ }
41
+ if (!isObject(declared.properties)) {
42
+ throw new SchemaInvalid(`app:${kind}:properties`);
43
+ }
44
+ const names = Object.keys(declared.properties);
45
+ const badName = names.find(name => name === '' || name.includes('.') || name.includes('$'));
46
+ if (badName != null) {
47
+ throw new SchemaInvalid(`app:${kind}:name:${badName}`);
48
+ }
49
+ const required = declared.required;
50
+ if (required != null && (!Array.isArray(required) || required.some(name => typeof name !== 'string'))) {
51
+ throw new SchemaInvalid(`app:${kind}:required`);
52
+ }
53
+ const missing = required?.find(name => !names.includes(name));
54
+ if (missing != null) {
55
+ throw new SchemaInvalid(`app:${kind}:required:${missing}`);
56
+ }
57
+ try {
58
+ makeAjv().compile(objectSchemaOf(declared));
59
+ }
60
+ catch (error) {
61
+ throw new SchemaInvalid(`app:${kind}:${messageOf(error)}`);
62
+ }
63
+ }
64
+ };
65
+ const merge = sources => {
66
+ const merged = {};
67
+ for (const source of sources) {
68
+ if (source == null) {
69
+ continue;
70
+ }
71
+ assertDeclarations(source);
72
+ for (const kind of APP_KINDS) {
73
+ const next = source[kind];
74
+ if (next == null) {
75
+ continue;
76
+ }
77
+ const current = merged[kind];
78
+ const required = [...new Set([...(current?.required ?? []), ...(next.required ?? [])])];
79
+ merged[kind] = {
80
+ type: 'object',
81
+ properties: { ...current?.properties, ...structuredClone(next.properties) },
82
+ ...(required.length > 0 ? { required } : {}),
83
+ };
84
+ }
85
+ }
86
+ return Object.keys(merged).length > 0 ? merged : undefined;
87
+ };
88
+ const bagSchemaOf = (app, kind) => {
89
+ const declared = declaredOf(app, kind);
90
+ return declared == null ? undefined : { ...objectSchemaOf(declared), additionalProperties: false };
91
+ };
92
+ const assertBag = (kind, key, bag, app) => {
93
+ const declared = declaredOf(app, kind);
94
+ if (declared == null) {
95
+ if (bag != null) {
96
+ throw new SchemaInvalid(`${kind}:${key}:app:undeclared`);
97
+ }
98
+ return;
99
+ }
100
+ const keys = validateHelper.invalidFieldKeys(bag);
101
+ if (keys.length > 0) {
102
+ throw new SchemaInvalid(`${kind}:${key}:app:keys:${keys.join(',')}`);
103
+ }
104
+ let validate = bags.get(declared);
105
+ if (validate == null) {
106
+ validate = makeAjv().compile(bagSchemaOf(app, kind));
107
+ bags.set(declared, validate);
108
+ }
109
+ if (!validate(bag ?? {})) {
110
+ throw new SchemaInvalid(`${kind}:${key}:app:${validateHelper.ajvErrorText(validate.errors)}`);
111
+ }
112
+ };
113
+ const fieldsSchemaOf = (type, app) => {
114
+ const base = app?.card;
115
+ const own = type.fields;
116
+ if (base == null || type.kind !== WorkcardKind.Card) {
117
+ return own;
118
+ }
119
+ const declared = objectSchemaOf(base);
120
+ if (!isObject(own)) {
121
+ return own === false ? false : declared;
122
+ }
123
+ // The type's schema stays the root, so its own `#/…` references keep resolving.
124
+ const admitted = Object.fromEntries(Object.keys(base.properties).map(name => [name, {}]));
125
+ return {
126
+ ...own,
127
+ properties: { ...admitted, ...(isObject(own.properties) ? own.properties : {}) },
128
+ allOf: [...(Array.isArray(own.allOf) ? own.allOf : []), declared],
129
+ };
130
+ };
131
+ return { merge, assertDeclarations, bagSchemaOf, assertBag, fieldsSchemaOf };
132
+ };
133
+ export const appSchemaHelper = createAppSchemaHelper();
134
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/helpers/app.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ export type * from './app/types.js';
1
2
  export type * from './apply/types.js';
2
3
  export type * from './card/types.js';
3
4
  export type * from './changes/types.js';
@@ -9,6 +10,7 @@ export type * from './specification/types.js';
9
10
  export type * from './status/types.js';
10
11
  export type * from './validate/types.js';
11
12
  export type * from './wire/types.js';
13
+ export * from './app.js';
12
14
  export * from './apply.js';
13
15
  export * from './card.js';
14
16
  export * from './changes.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,iBAAiB,CAAA;AACpC,mBAAmB,oBAAoB,CAAA;AACvC,mBAAmB,iBAAiB,CAAA;AACpC,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,mBAAmB,CAAA;AACtC,mBAAmB,0BAA0B,CAAA;AAC7C,mBAAmB,mBAAmB,CAAA;AACtC,mBAAmB,qBAAqB,CAAA;AACxC,mBAAmB,iBAAiB,CAAA;AAEpC,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,gBAAgB,CAAA;AACnC,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,iBAAiB,CAAA;AACpC,mBAAmB,oBAAoB,CAAA;AACvC,mBAAmB,iBAAiB,CAAA;AACpC,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,kBAAkB,CAAA;AACrC,mBAAmB,mBAAmB,CAAA;AACtC,mBAAmB,0BAA0B,CAAA;AAC7C,mBAAmB,mBAAmB,CAAA;AACtC,mBAAmB,qBAAqB,CAAA;AACxC,mBAAmB,iBAAiB,CAAA;AAEpC,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA"}
@@ -1,3 +1,4 @@
1
+ export * from './app.js';
1
2
  export * from './apply.js';
2
3
  export * from './card.js';
3
4
  export * from './changes.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAYA,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import type { AssigneeTypeSchema } from '../../resources/types.js';
2
2
  import type { PlanningSchemaKind } from '../../consts.js';
3
- import type { PlanningSchemaBundle, PlanningSchemaRegistry, SchemaScope, ScopedSchemaBundle, ScopedSchemaRecord, ScopedSchemaRegistry, StatusFlowSchema, WorkcardTypeSchema } from '../../types.js';
3
+ import type { PlanningAppDeclarations, PlanningSchemaBundle, PlanningSchemaRegistry, SchemaScope, ScopedSchemaBundle, ScopedSchemaRecord, ScopedSchemaRegistry, StatusFlowSchema, WorkcardTypeSchema } from '../../types.js';
4
4
  /** Data-defined (scoped) types and flows: their keys, the checks a write passes, and the layer they resolve to. */
5
5
  export interface ScopedSchemaHelper {
6
6
  /** One layer's identity of a record: organization, project (or none), kind, key. */
@@ -15,21 +15,30 @@ export interface ScopedSchemaHelper {
15
15
  */
16
16
  assertOverridable: (code: Pick<PlanningSchemaRegistry, 'has' | 'type' | 'flows' | 'assigneeTypes'>, kind: PlanningSchemaKind, key: string) => void;
17
17
  /**
18
- * The closed-form checks of a data-defined flow: the declaration's own schema, at least one status,
19
- * unique status keys, every rule's `to` declared, and a `from` that is `'*'` or a list of declared
20
- * statuses.
18
+ * The closed-form checks of a data-defined assignee type: the declaration's own schema and a
19
+ * `fields` schema that compiles.
21
20
  *
22
21
  * @throws {SchemaInvalid}
23
22
  */
24
23
  assertAssigneeTypeSchema: (schema: AssigneeTypeSchema) => void;
25
- assertFlowSchema: (flow: StatusFlowSchema) => void;
24
+ /**
25
+ * The closed-form checks of a data-defined flow: the declaration's own schema, at least one status,
26
+ * unique status keys, every rule's `to` declared, a `from` that is `'*'` or a list of declared
27
+ * statuses, and its `app` bag against the application's `flow` declaration
28
+ * (`appSchemaHelper.assertBag` — refused when `app` declares none).
29
+ *
30
+ * @throws {SchemaInvalid}
31
+ */
32
+ assertFlowSchema: (flow: StatusFlowSchema, app?: PlanningAppDeclarations) => void;
26
33
  /**
27
34
  * The closed-form checks of a data-defined type: the declaration's own schema, the `card` kind,
28
- * a `fields` schema that compiles, and unique flow ids every one of which resolves.
35
+ * its `app` bag against the application's `type` declaration (refused when `app` declares none),
36
+ * a `fields` schema that compiles together with the application's base card fields, and unique
37
+ * flow ids every one of which resolves.
29
38
  *
30
39
  * @throws {SchemaInvalid}
31
40
  */
32
- assertTypeSchema: (type: WorkcardTypeSchema, flows: Pick<PlanningSchemaRegistry, 'flow'>) => void;
41
+ assertTypeSchema: (type: WorkcardTypeSchema, flows: Pick<PlanningSchemaRegistry, 'flow'>, app?: PlanningAppDeclarations) => void;
33
42
  /**
34
43
  * One layer resolved: the code bundle, overlaid by the organization-wide records, then by the
35
44
  * project's (when `scope.project` names one). Pure — every store and both sides of the wire resolve
@@ -42,6 +51,9 @@ export interface ScopedSchemaHelper {
42
51
  * - A retired record gives way to the nearest live declaration below it; a key retired in every
43
52
  * layer resolves retired — present in `types`/`flows`, listed in `retired`.
44
53
  * - A data-defined declaration carries its record's `version`.
54
+ * - The code bundle's `app` declarations pass through as they are; a record's `app` bag is not
55
+ * re-checked here (the write path checks it), so a record written under earlier declarations
56
+ * still resolves.
45
57
  */
46
58
  resolveScopedBundle: (code: PlanningSchemaBundle, records: readonly ScopedSchemaRecord[], scope: SchemaScope & {
47
59
  revision?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/helpers/scoped/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EACV,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,kBAAkB,EAAE,kBAAkB,EACjG,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAC3D,MAAM,gBAAgB,CAAA;AAEvB,mHAAmH;AACnH,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,eAAe,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,MAAM,CAAA;IACtG,6EAA6E;IAC7E,WAAW,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,KAAK,MAAM,CAAA;IACzH;;;;;OAKG;IACH,iBAAiB,EAAE,CACjB,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,KAClH,IAAI,CAAA;IACT;;;;;;OAMG;IACH,wBAAwB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC9D,gBAAgB,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAClD;;;;;OAKG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,KAAK,IAAI,CAAA;IACjG;;;;;;;;;;;;OAYG;IACH,mBAAmB,EAAE,CACnB,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,kBAAkB,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAC3G,kBAAkB,CAAA;IACvB;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,oBAAoB,CAAA;IACtE;;;OAGG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;CAC5E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/helpers/scoped/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EACV,uBAAuB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,kBAAkB,EAAE,kBAAkB,EAC1H,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAC3D,MAAM,gBAAgB,CAAA;AAEvB,mHAAmH;AACnH,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,eAAe,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,MAAM,CAAA;IACtG,6EAA6E;IAC7E,WAAW,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,KAAK,MAAM,CAAA;IACzH;;;;;OAKG;IACH,iBAAiB,EAAE,CACjB,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,KAClH,IAAI,CAAA;IACT;;;;;OAKG;IACH,wBAAwB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC9D;;;;;;;OAOG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAA;IACjF;;;;;;;OAOG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAChI;;;;;;;;;;;;;;;OAeG;IACH,mBAAmB,EAAE,CACnB,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,kBAAkB,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAC3G,kBAAkB,CAAA;IACvB;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,oBAAoB,CAAA;IACtE;;;OAGG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;CAC5E"}
@@ -1,6 +1,6 @@
1
1
  import type { AssigneeTypeSchema } from '../resources/types.js';
2
2
  import { PlanningSchemaKind } from '../consts.js';
3
- import type { PlanningSchemaBundle, PlanningSchemaRegistry, SchemaScope, ScopedSchemaBundle, ScopedSchemaRecord, ScopedSchemaRegistry, StatusFlowSchema, WorkcardTypeSchema } from '../types.js';
3
+ import type { PlanningAppDeclarations, PlanningSchemaBundle, PlanningSchemaRegistry, SchemaScope, ScopedSchemaBundle, ScopedSchemaRecord, ScopedSchemaRegistry, StatusFlowSchema, WorkcardTypeSchema } from '../types.js';
4
4
  import type { ScopedSchemaHelper } from './scoped/types.js';
5
5
  export declare const createScopedSchemaHelper: () => ScopedSchemaHelper;
6
6
  export declare const scopedSchemaHelper: ScopedSchemaHelper;
@@ -11,9 +11,9 @@ export declare const schemaKeyOf: (kind: PlanningSchemaKind, definition: Workcar
11
11
  /** @deprecated compat:factory-refactor — use `scopedSchemaHelper.assertOverridable(…)` */
12
12
  export declare const assertOverridable: (code: Pick<PlanningSchemaRegistry, 'has' | 'type' | 'flows' | 'assigneeTypes'>, kind: PlanningSchemaKind, key: string) => void;
13
13
  /** @deprecated compat:factory-refactor — use `scopedSchemaHelper.assertFlowSchema(…)` */
14
- export declare const assertFlowSchema: (flow: StatusFlowSchema) => void;
14
+ export declare const assertFlowSchema: (flow: StatusFlowSchema, app?: PlanningAppDeclarations) => void;
15
15
  /** @deprecated compat:factory-refactor — use `scopedSchemaHelper.assertTypeSchema(…)` */
16
- export declare const assertTypeSchema: (type: WorkcardTypeSchema, flows: Pick<PlanningSchemaRegistry, 'flow'>) => void;
16
+ export declare const assertTypeSchema: (type: WorkcardTypeSchema, flows: Pick<PlanningSchemaRegistry, 'flow'>, app?: PlanningAppDeclarations) => void;
17
17
  /** @deprecated compat:factory-refactor — use `scopedSchemaHelper.resolveScopedBundle(…)` */
18
18
  export declare const resolveScopedBundle: (code: PlanningSchemaBundle, records: readonly ScopedSchemaRecord[], scope: SchemaScope & {
19
19
  revision?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"scoped.d.ts","sourceRoot":"","sources":["../../src/helpers/scoped.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG/D,OAAO,EAAE,kBAAkB,EAA8B,MAAM,cAAc,CAAA;AAI7E,OAAO,KAAK,EACK,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,kBAAkB,EAC5F,kBAAkB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAC/E,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAM3D,eAAO,MAAM,wBAAwB,QAAO,kBAiO3C,CAAA;AAED,eAAO,MAAM,kBAAkB,oBAA6B,CAAA;AAE5D,wFAAwF;AACxF,eAAO,MAAM,eAAe,WAAY,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,KAAG,MAChE,CAAA;AAE5C,oFAAoF;AACpF,eAAO,MAAM,WAAW,SAAU,kBAAkB,cAAc,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,KAAG,MAC7E,CAAA;AAElD,0FAA0F;AAC1F,eAAO,MAAM,iBAAiB,SACtB,IAAI,CAAC,sBAAsB,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC,QAAQ,kBAAkB,OAAO,MAAM,KACpH,IAA6D,CAAA;AAEhE,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,SAAU,gBAAgB,KAAG,IAAiD,CAAA;AAE3G,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,SAAU,kBAAkB,SAAS,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,KAAG,IACvD,CAAA;AAElD,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB,SACxB,oBAAoB,WAAW,SAAS,kBAAkB,EAAE,SAAS,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7G,kBAAkF,CAAA;AAErF,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,WAAY,kBAAkB,KAAG,oBAAmE,CAAA;AAEjI,kFAAkF;AAClF,eAAO,MAAM,SAAS,WAAY,kBAAkB,QAAQ,MAAM,KAAG,MAAM,GAAG,SAAuD,CAAA"}
1
+ {"version":3,"file":"scoped.d.ts","sourceRoot":"","sources":["../../src/helpers/scoped.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG/D,OAAO,EAAE,kBAAkB,EAA8B,MAAM,cAAc,CAAA;AAI7E,OAAO,KAAK,EACK,uBAAuB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,kBAAkB,EACrH,kBAAkB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,kBAAkB,EAC/E,MAAM,aAAa,CAAA;AAEpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAM3D,eAAO,MAAM,wBAAwB,QAAO,kBAqO3C,CAAA;AAED,eAAO,MAAM,kBAAkB,oBAA6B,CAAA;AAE5D,wFAAwF;AACxF,eAAO,MAAM,eAAe,WAAY,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,KAAG,MAChE,CAAA;AAE5C,oFAAoF;AACpF,eAAO,MAAM,WAAW,SAAU,kBAAkB,cAAc,kBAAkB,GAAG,gBAAgB,GAAG,kBAAkB,KAAG,MAC7E,CAAA;AAElD,0FAA0F;AAC1F,eAAO,MAAM,iBAAiB,SACtB,IAAI,CAAC,sBAAsB,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC,QAAQ,kBAAkB,OAAO,MAAM,KACpH,IAA6D,CAAA;AAEhE,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,SAAU,gBAAgB,QAAQ,uBAAuB,KAAG,IAAsD,CAAA;AAE/I,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,SAAU,kBAAkB,SAAS,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,QAAQ,uBAAuB,KAAG,IACjF,CAAA;AAEvD,4FAA4F;AAC5F,eAAO,MAAM,mBAAmB,SACxB,oBAAoB,WAAW,SAAS,kBAAkB,EAAE,SAAS,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7G,kBAAkF,CAAA;AAErF,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,WAAY,kBAAkB,KAAG,oBAAmE,CAAA;AAEjI,kFAAkF;AAClF,eAAO,MAAM,SAAS,WAAY,kBAAkB,QAAQ,MAAM,KAAG,MAAM,GAAG,SAAuD,CAAA"}