@wildo-ai/platform-core-shared 1.1.1 → 1.1.2
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/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts +3 -3
- package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.js +39 -8
- package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts +4 -3
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.js +37 -6
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts +1 -0
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts.map +1 -1
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js +13 -0
- package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js.map +1 -1
- package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts +100 -0
- package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts.map +1 -0
- package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js +578 -0
- package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js.map +1 -0
- package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts +98 -0
- package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts.map +1 -0
- package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js +86 -0
- package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js.map +1 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts +157 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts.map +1 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js +258 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js.map +1 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts +221 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts.map +1 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js +292 -0
- package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js.map +1 -0
- package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.d.ts +48 -0
- package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.d.ts.map +1 -0
- package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.js +48 -0
- package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.js.map +1 -0
- package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts +142 -21
- package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts.map +1 -1
- package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js +252 -23
- package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js.map +1 -1
- package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts +280 -55
- package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js +187 -102
- package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts +227 -49
- package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts.map +1 -1
- package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js +464 -85
- package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -5
- package/dist/esm/.builder.pid +0 -9
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { CoreResourceOperation, CoreResourceType, IntrospectionTenancyStance, PersistenceAdapter, RelationshipNature, ResourceParentResourceRequirement, ResourceRelationshipAccessScopeStrategy, ResourceRelationshipCardinality, createResourceConfiguration_Initialization, createResourcesRelationship, } from '@wildo-ai/saas-models';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { initZodDecorators } from '@wildo-ai/zod-decorators';
|
|
4
|
+
// Decorator methods must exist before the module-load-time envelope builds below.
|
|
5
|
+
initZodDecorators();
|
|
6
|
+
import { WorkbenchShared_ResourceFieldIdentifier, WorkbenchShared_ResourceType, } from './workbench-resources-types.custom.shared.schemas.js';
|
|
7
|
+
import { createReadOnlyApiVariant, createReadOnlyListApiVariant, } from './workbench-resources.custom.shared.resources-config.schemas.js';
|
|
8
|
+
/**
|
|
9
|
+
* FAMILY ASPECT RESOURCES — the spine's schema-agnostic transcription of
|
|
10
|
+
* `FAMILY_ASPECTS_REGISTRY` (`@wildo-ai/saas-specifications`, `_core/family-aspects.*`).
|
|
11
|
+
*
|
|
12
|
+
* ## The split, restated where it is enforced
|
|
13
|
+
*
|
|
14
|
+
* The registry is AUTHORITATIVE: item schemas, business keys, edges, all declared beside the
|
|
15
|
+
* `_core` family schemas (decided 2026-09-01). The spine cannot import it — platform-core-shared
|
|
16
|
+
* deliberately never depends on saas-specifications — so this module carries plain DATA: which
|
|
17
|
+
* aspects exist, which envelope fields carry references, and which resource each reference
|
|
18
|
+
* resolves to. A companion-side parity test holds this table to the registry; drift between the
|
|
19
|
+
* two is a failing test, never a silent divergence.
|
|
20
|
+
*
|
|
21
|
+
* ## The envelope
|
|
22
|
+
*
|
|
23
|
+
* Aspect rows share ONE envelope shape (identity, family backlink, headline, the full item as a
|
|
24
|
+
* raw record) plus the aspect's own FLATTENED reference fields. Flattening is the projection's
|
|
25
|
+
* job and is what makes every edge a PLAIN foreign key the engine already resolves:
|
|
26
|
+
*
|
|
27
|
+
* - weighted edge objects flatten to key arrays (`jobs[].jobRef` → `jobIds`),
|
|
28
|
+
* - multi-target fields split per target (`deliversRequirementRefs` →
|
|
29
|
+
* `deliversFunctionalRequirementIds` + `deliversNonFunctionalRequirementIds`), because the
|
|
30
|
+
* frontend resolves an FK field name to ONE resource — a mixed-kind array has no honest single
|
|
31
|
+
* answer, and split fields read better anyway,
|
|
32
|
+
* - every field name below maps to exactly one target across ALL aspects (collision-audited at
|
|
33
|
+
* generation; the parity test re-checks it).
|
|
34
|
+
*
|
|
35
|
+
* The `family` field is the artifact BACKLINK for free: it is the specification-artifacts
|
|
36
|
+
* resource's own field identifier, so the global field-identifier map already resolves it.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* How an aspect row gets its identity — mirrors the registry's `businessKey` contract.
|
|
40
|
+
*
|
|
41
|
+
* The three are not interchangeable and the fallback order between them is the point: a collection
|
|
42
|
+
* whose schema declares a single-field key is addressed by it, a singleton has one row per family so
|
|
43
|
+
* the aspect key IS the identity, and a keyless collection falls back to a positional address that is
|
|
44
|
+
* stable per landed snapshot and honest about carrying no business meaning.
|
|
45
|
+
*/
|
|
46
|
+
export var WorkbenchAspectIdentityKind;
|
|
47
|
+
(function (WorkbenchAspectIdentityKind) {
|
|
48
|
+
/** The item's own business key (`ref`, `taskRef`, …), unique within the aspect. */
|
|
49
|
+
WorkbenchAspectIdentityKind["BUSINESS_KEY"] = "business-key";
|
|
50
|
+
/** One row per family — identity is the aspect key itself. */
|
|
51
|
+
WorkbenchAspectIdentityKind["SINGLETON"] = "singleton";
|
|
52
|
+
/** Keyless collection (locale × jurisdictions): `<aspectKey>[<index>]`, stable per snapshot. */
|
|
53
|
+
WorkbenchAspectIdentityKind["INDEXED"] = "indexed";
|
|
54
|
+
})(WorkbenchAspectIdentityKind || (WorkbenchAspectIdentityKind = {}));
|
|
55
|
+
export const WORKBENCH_ASPECT_DESCRIPTORS = [
|
|
56
|
+
{
|
|
57
|
+
aspectKey: 'vision.idea',
|
|
58
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_VISION_IDEA,
|
|
59
|
+
rowsPath: 'visionIdeas',
|
|
60
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
61
|
+
edges: [],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
aspectKey: 'vision.problem',
|
|
65
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_VISION_PROBLEM,
|
|
66
|
+
rowsPath: 'visionProblems',
|
|
67
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
68
|
+
edges: [],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
aspectKey: 'vision.mission',
|
|
72
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_VISION_MISSION,
|
|
73
|
+
rowsPath: 'visionMissions',
|
|
74
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
75
|
+
edges: [],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
aspectKey: 'vision.success-criterion',
|
|
79
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_VISION_SUCCESS_CRITERION,
|
|
80
|
+
rowsPath: 'visionSuccessCriteria',
|
|
81
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
82
|
+
edges: [],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
aspectKey: 'vision.constraint',
|
|
86
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_VISION_CONSTRAINT,
|
|
87
|
+
rowsPath: 'visionConstraints',
|
|
88
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
89
|
+
edges: [],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
aspectKey: 'market.category',
|
|
93
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_CATEGORY,
|
|
94
|
+
rowsPath: 'marketCategories',
|
|
95
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
96
|
+
edges: [],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
aspectKey: 'market.icp',
|
|
100
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_ICP,
|
|
101
|
+
rowsPath: 'marketIcps',
|
|
102
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
103
|
+
edges: [
|
|
104
|
+
{ field: 'personaIds', targetAspectKey: 'market.persona', many: true },
|
|
105
|
+
{ field: 'jobIds', targetAspectKey: 'market.job', many: true },
|
|
106
|
+
{ field: 'painIds', targetAspectKey: 'market.pain', many: true },
|
|
107
|
+
{ field: 'gainIds', targetAspectKey: 'market.gain', many: true },
|
|
108
|
+
{ field: 'channelIds', targetAspectKey: 'market.channel', many: true },
|
|
109
|
+
{ field: 'triggerIds', targetAspectKey: 'market.trigger', many: true },
|
|
110
|
+
{ field: 'alternativeIds', targetAspectKey: 'market.alternative', many: true },
|
|
111
|
+
{ field: 'proofIds', targetAspectKey: 'market.proof', many: true },
|
|
112
|
+
{ field: 'objectionIds', targetAspectKey: 'market.objection', many: true },
|
|
113
|
+
{ field: 'preferredVoiceId', targetAspectKey: 'market.tone-of-voice', many: false },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
aspectKey: 'market.persona',
|
|
118
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_PERSONA,
|
|
119
|
+
rowsPath: 'marketPersonas',
|
|
120
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
121
|
+
edges: [
|
|
122
|
+
{ field: 'belongsToIcpIds', targetAspectKey: 'market.icp', many: true },
|
|
123
|
+
{ field: 'jobIds', targetAspectKey: 'market.job', many: true },
|
|
124
|
+
{ field: 'painIds', targetAspectKey: 'market.pain', many: true },
|
|
125
|
+
{ field: 'gainIds', targetAspectKey: 'market.gain', many: true },
|
|
126
|
+
{ field: 'channelIds', targetAspectKey: 'market.channel', many: true },
|
|
127
|
+
{ field: 'preferredVoiceId', targetAspectKey: 'market.tone-of-voice', many: false },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
aspectKey: 'market.job',
|
|
132
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_JOB,
|
|
133
|
+
rowsPath: 'marketJobs',
|
|
134
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
135
|
+
edges: [],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
aspectKey: 'market.pain',
|
|
139
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_PAIN,
|
|
140
|
+
rowsPath: 'marketPains',
|
|
141
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
142
|
+
edges: [],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
aspectKey: 'market.gain',
|
|
146
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_GAIN,
|
|
147
|
+
rowsPath: 'marketGains',
|
|
148
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
149
|
+
edges: [],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
aspectKey: 'market.tone-of-voice',
|
|
153
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_TONE_OF_VOICE,
|
|
154
|
+
rowsPath: 'marketTonesOfVoice',
|
|
155
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
156
|
+
edges: [],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
aspectKey: 'market.benefit',
|
|
160
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_BENEFIT,
|
|
161
|
+
rowsPath: 'marketBenefits',
|
|
162
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
163
|
+
edges: [
|
|
164
|
+
{ field: 'deliveredByFeatureIds', targetAspectKey: 'feature-drafts.draft', many: true },
|
|
165
|
+
{ field: 'painIds', targetAspectKey: 'market.pain', many: true },
|
|
166
|
+
{ field: 'gainIds', targetAspectKey: 'market.gain', many: true },
|
|
167
|
+
{ field: 'jobIds', targetAspectKey: 'market.job', many: true },
|
|
168
|
+
{ field: 'icpIds', targetAspectKey: 'market.icp', many: true },
|
|
169
|
+
{ field: 'proofIds', targetAspectKey: 'market.proof', many: true },
|
|
170
|
+
{ field: 'addressesObjectionIds', targetAspectKey: 'market.objection', many: true },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
aspectKey: 'market.positioning',
|
|
175
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_POSITIONING,
|
|
176
|
+
rowsPath: 'marketPositionings',
|
|
177
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
178
|
+
edges: [
|
|
179
|
+
{ field: 'icpId', targetAspectKey: 'market.icp', many: false },
|
|
180
|
+
{ field: 'preferredVoiceId', targetAspectKey: 'market.tone-of-voice', many: false },
|
|
181
|
+
{ field: 'benefitIds', targetAspectKey: 'market.benefit', many: true },
|
|
182
|
+
{ field: 'alternativeIds', targetAspectKey: 'market.alternative', many: true },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
aspectKey: 'market.channel',
|
|
187
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_CHANNEL,
|
|
188
|
+
rowsPath: 'marketChannels',
|
|
189
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
190
|
+
edges: [],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
aspectKey: 'market.alternative',
|
|
194
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_ALTERNATIVE,
|
|
195
|
+
rowsPath: 'marketAlternatives',
|
|
196
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
197
|
+
edges: [],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
aspectKey: 'market.trigger',
|
|
201
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_TRIGGER,
|
|
202
|
+
rowsPath: 'marketTriggers',
|
|
203
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
204
|
+
edges: [
|
|
205
|
+
{ field: 'affectedIcpIds', targetAspectKey: 'market.icp', many: true },
|
|
206
|
+
{ field: 'recommendedChannelIds', targetAspectKey: 'market.channel', many: true },
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
aspectKey: 'market.buying-journey',
|
|
211
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_BUYING_JOURNEY,
|
|
212
|
+
rowsPath: 'marketBuyingJourneys',
|
|
213
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
214
|
+
edges: [
|
|
215
|
+
{ field: 'icpId', targetAspectKey: 'market.icp', many: false },
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
aspectKey: 'market.proof',
|
|
220
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_PROOF,
|
|
221
|
+
rowsPath: 'marketProofs',
|
|
222
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
223
|
+
edges: [
|
|
224
|
+
{ field: 'supportsBenefitIds', targetAspectKey: 'market.benefit', many: true },
|
|
225
|
+
{ field: 'supportsIcpIds', targetAspectKey: 'market.icp', many: true },
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
aspectKey: 'market.objection',
|
|
230
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_OBJECTION,
|
|
231
|
+
rowsPath: 'marketObjections',
|
|
232
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
233
|
+
edges: [
|
|
234
|
+
{ field: 'raisedByIcpIds', targetAspectKey: 'market.icp', many: true },
|
|
235
|
+
{ field: 'raisedByPersonaIds', targetAspectKey: 'market.persona', many: true },
|
|
236
|
+
{ field: 'proofIds', targetAspectKey: 'market.proof', many: true },
|
|
237
|
+
{ field: 'relatedBenefitIds', targetAspectKey: 'market.benefit', many: true },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
aspectKey: 'market.message-template',
|
|
242
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_MARKET_MESSAGE_TEMPLATE,
|
|
243
|
+
rowsPath: 'marketMessageTemplates',
|
|
244
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
245
|
+
edges: [
|
|
246
|
+
{ field: 'preferredVoiceId', targetAspectKey: 'market.tone-of-voice', many: false },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
aspectKey: 'requirements.functional',
|
|
251
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_REQUIREMENT_FUNCTIONAL,
|
|
252
|
+
rowsPath: 'functionalRequirements',
|
|
253
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
254
|
+
edges: [
|
|
255
|
+
{ field: 'icpIds', targetAspectKey: 'market.icp', many: true },
|
|
256
|
+
{ field: 'jobIds', targetAspectKey: 'market.job', many: true },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
aspectKey: 'requirements.non-functional',
|
|
261
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_REQUIREMENT_NON_FUNCTIONAL,
|
|
262
|
+
rowsPath: 'nonFunctionalRequirements',
|
|
263
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
264
|
+
edges: [
|
|
265
|
+
{ field: 'icpIds', targetAspectKey: 'market.icp', many: true },
|
|
266
|
+
{ field: 'jobIds', targetAspectKey: 'market.job', many: true },
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
aspectKey: 'feature-drafts.draft',
|
|
271
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_FEATURE_DRAFT,
|
|
272
|
+
rowsPath: 'featureDrafts',
|
|
273
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
274
|
+
edges: [],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
aspectKey: 'roadmap.phase',
|
|
278
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_ROADMAP_PHASE,
|
|
279
|
+
rowsPath: 'roadmapPhases',
|
|
280
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
281
|
+
edges: [
|
|
282
|
+
{ field: 'deliversFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
|
|
283
|
+
{ field: 'deliversNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
|
|
284
|
+
{ field: 'deliversSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
aspectKey: 'business-model.plan',
|
|
289
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_PRICING_PLAN,
|
|
290
|
+
rowsPath: 'pricingPlans',
|
|
291
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
292
|
+
edges: [
|
|
293
|
+
{ field: 'targetIcpIds', targetAspectKey: 'market.icp', many: true },
|
|
294
|
+
{ field: 'includesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
|
|
295
|
+
{ field: 'includesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
aspectKey: 'brand.name',
|
|
300
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_NAME,
|
|
301
|
+
rowsPath: 'brandNames',
|
|
302
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
303
|
+
edges: [],
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
aspectKey: 'brand.logo-direction',
|
|
307
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_LOGO_DIRECTION,
|
|
308
|
+
rowsPath: 'brandLogoDirections',
|
|
309
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
310
|
+
edges: [],
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
aspectKey: 'brand.visual-tone',
|
|
314
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_VISUAL_TONE,
|
|
315
|
+
rowsPath: 'brandVisualTones',
|
|
316
|
+
identity: WorkbenchAspectIdentityKind.SINGLETON,
|
|
317
|
+
edges: [],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
aspectKey: 'brand.asset',
|
|
321
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_ASSET,
|
|
322
|
+
rowsPath: 'brandAssets',
|
|
323
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
324
|
+
edges: [
|
|
325
|
+
{ field: 'derivedFromId', targetAspectKey: 'brand.asset', many: false },
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
aspectKey: 'domain-plan.module',
|
|
330
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_DOMAIN_PLAN_MODULE,
|
|
331
|
+
rowsPath: 'domainPlanModules',
|
|
332
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
333
|
+
edges: [],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
aspectKey: 'domain-plan.relationship',
|
|
337
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_DOMAIN_PLAN_RELATIONSHIP,
|
|
338
|
+
rowsPath: 'domainPlanRelationships',
|
|
339
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
340
|
+
edges: [],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
aspectKey: 'build-plan.task',
|
|
344
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_BUILD_TASK,
|
|
345
|
+
rowsPath: 'buildTasks',
|
|
346
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
347
|
+
edges: [
|
|
348
|
+
{ field: 'prerequisiteTaskIds', targetAspectKey: 'build-plan.task', many: true },
|
|
349
|
+
{ field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
|
|
350
|
+
{ field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
|
|
351
|
+
{ field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
aspectKey: 'placement.group',
|
|
356
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_PLACEMENT_GROUP,
|
|
357
|
+
rowsPath: 'placementGroups',
|
|
358
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
359
|
+
edges: [],
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
aspectKey: 'placement.decision',
|
|
363
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_PLACEMENT_DECISION,
|
|
364
|
+
rowsPath: 'placementDecisions',
|
|
365
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
366
|
+
edges: [
|
|
367
|
+
{ field: 'groupId', targetAspectKey: 'placement.group', many: false },
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
aspectKey: 'compliance-candidate.section',
|
|
372
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_CANDIDATE_SECTION,
|
|
373
|
+
rowsPath: 'complianceCandidateSections',
|
|
374
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
375
|
+
edges: [],
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
aspectKey: 'compliance-candidate.variant-selection',
|
|
379
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_CANDIDATE_VARIANT_SELECTION,
|
|
380
|
+
rowsPath: 'complianceCandidateVariantSelections',
|
|
381
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
382
|
+
edges: [],
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
aspectKey: 'document-facts.declaration',
|
|
386
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_DOCUMENT_FACT_DECLARATION,
|
|
387
|
+
rowsPath: 'documentFactDeclarations',
|
|
388
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
389
|
+
edges: [],
|
|
390
|
+
},
|
|
391
|
+
/*
|
|
392
|
+
* The same row shape as the descriptor above and a SEPARATE resource, because the two families it
|
|
393
|
+
* transcribes are separate (#192): one is regenerated by a producer, the other is only ever written
|
|
394
|
+
* by a person. Merging them into one browsable list would erase exactly the provenance the split
|
|
395
|
+
* exists to keep, and a reader could no longer tell which declarations survive the next producer run.
|
|
396
|
+
*/
|
|
397
|
+
{
|
|
398
|
+
aspectKey: 'document-facts-authored.declaration',
|
|
399
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_AUTHORED_DOCUMENT_FACT_DECLARATION,
|
|
400
|
+
rowsPath: 'authoredDocumentFactDeclarations',
|
|
401
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
402
|
+
edges: [],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
/*
|
|
406
|
+
* INDEXED, not BUSINESS_KEY, and that is the registry's own answer rather than a simplification
|
|
407
|
+
* here: the row is a union whose two arms key on different fields (`providerRef` vs
|
|
408
|
+
* `subprocessorRef`), so no single field identifies a row across it.
|
|
409
|
+
*/
|
|
410
|
+
aspectKey: 'processor-register.processor',
|
|
411
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_PROCESSOR_REGISTER_ENTRY,
|
|
412
|
+
rowsPath: 'processorRegisterEntries',
|
|
413
|
+
identity: WorkbenchAspectIdentityKind.INDEXED,
|
|
414
|
+
edges: [],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
aspectKey: 'release-policy.selection',
|
|
418
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_RELEASE_POLICY_SELECTION,
|
|
419
|
+
rowsPath: 'releasePolicySelections',
|
|
420
|
+
identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
|
|
421
|
+
edges: [],
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
aspectKey: 'document-publication.variant',
|
|
425
|
+
resourceType: WorkbenchShared_ResourceType.ASPECT_DOCUMENT_PUBLICATION_VARIANT,
|
|
426
|
+
rowsPath: 'documentPublicationVariants',
|
|
427
|
+
identity: WorkbenchAspectIdentityKind.INDEXED,
|
|
428
|
+
edges: [],
|
|
429
|
+
},
|
|
430
|
+
];
|
|
431
|
+
const DESCRIPTOR_BY_ASPECT_KEY = new Map(WORKBENCH_ASPECT_DESCRIPTORS.map((descriptor) => [descriptor.aspectKey, descriptor]));
|
|
432
|
+
export function getWorkbenchAspectDescriptor(aspectKey) {
|
|
433
|
+
return DESCRIPTOR_BY_ASPECT_KEY.get(aspectKey);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* The shared envelope, instantiated per aspect with its identity field and reference fields.
|
|
437
|
+
*
|
|
438
|
+
* `content` stays a RAW record for the same reason the artifact resource's does: the spine never
|
|
439
|
+
* sees the item schemas; the workbench frontend renders `content` through the REAL `_core` schema
|
|
440
|
+
* from the registry, so typed rendering happens where the type authority is a dependency.
|
|
441
|
+
*/
|
|
442
|
+
export function buildWorkbenchAspectEnvelopeSchema(descriptor) {
|
|
443
|
+
const identityField = WorkbenchShared_ResourceFieldIdentifier[descriptor.resourceType];
|
|
444
|
+
const shape = {
|
|
445
|
+
/**
|
|
446
|
+
* The composed address the projection emits, and the ONLY field a row click can resolve by.
|
|
447
|
+
*
|
|
448
|
+
* `.isSummaryField()` is not decoration here. A list renders SUMMARIES, and the frontend resolves
|
|
449
|
+
* a clicked row's target by reading this field off the item — so an id that does not survive the
|
|
450
|
+
* projection makes every row in every aspect list silently stop responding to a click, while READ
|
|
451
|
+
* still answers 200 (the adapter filters by `identityFields`, not by this name). The only symptom
|
|
452
|
+
* is one console line. Pinned for the whole population by the addressing-field contract test.
|
|
453
|
+
*/
|
|
454
|
+
[identityField]: z.string().min(1).isPrimaryKey().isSummaryField(),
|
|
455
|
+
/**
|
|
456
|
+
* The owning artifact family id — a LIVE reference: `family` is the artifacts resource's own
|
|
457
|
+
* field identifier, so the global map resolves it, and the artifacts READ is addressable —
|
|
458
|
+
* the one link out of any popup back to the document the aspect lives in.
|
|
459
|
+
*/
|
|
460
|
+
family: z.string().isForeignKey().isSummaryField(),
|
|
461
|
+
/** The registry aspect key (`market.icp`) — which KIND of aspect this row is. */
|
|
462
|
+
aspectKey: z.string().isSummaryField(),
|
|
463
|
+
/** The item's headline value (the registry's `headlineField`), projected for lists/summaries. */
|
|
464
|
+
title: z.string().isSummaryField(),
|
|
465
|
+
/**
|
|
466
|
+
* The full item as landed — rendered frontend-side through the family's own `_core` schema.
|
|
467
|
+
*
|
|
468
|
+
* Deliberately NOT a summary field: it is the whole item, and carrying it in every list row is
|
|
469
|
+
* what the summary projection exists to avoid.
|
|
470
|
+
*/
|
|
471
|
+
content: z.record(z.string(), z.unknown()),
|
|
472
|
+
};
|
|
473
|
+
for (const edge of descriptor.edges) {
|
|
474
|
+
// FK marking rides the ELEMENT for arrays (the decorator's home is the string type); the
|
|
475
|
+
// display pipeline accepts `string | string[]` values on a reference field either way.
|
|
476
|
+
shape[edge.field] = edge.many
|
|
477
|
+
? z.array(z.string().isForeignKey())
|
|
478
|
+
: z.string().isForeignKey().optional();
|
|
479
|
+
}
|
|
480
|
+
return z.object(shape);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* One introspection resource-configuration factory per aspect, all from the same mold as their
|
|
484
|
+
* two siblings: READ/LIST only, `APPLICATION_STRUCTURE` tenancy, rows served by the companion's
|
|
485
|
+
* family-aspects behavior at this aspect's `rowsPath`. Identity is always the COMPOSED field the
|
|
486
|
+
* projection emits (see the identifier-map note in the types module).
|
|
487
|
+
*/
|
|
488
|
+
export function buildWorkbenchAspectConfigurationFactory(descriptor) {
|
|
489
|
+
const envelopeSchema = buildWorkbenchAspectEnvelopeSchema(descriptor);
|
|
490
|
+
return (resourcesRelationships) => createResourceConfiguration_Initialization({
|
|
491
|
+
mainSchema: envelopeSchema,
|
|
492
|
+
resourceIdentifier: descriptor.resourceType,
|
|
493
|
+
resourceFieldIdentifier: WorkbenchShared_ResourceFieldIdentifier[descriptor.resourceType],
|
|
494
|
+
resourceRelationships: resourcesRelationships,
|
|
495
|
+
isSystemResource: false,
|
|
496
|
+
persistenceAdapter: PersistenceAdapter.INTROSPECTION,
|
|
497
|
+
introspectionBinding: {
|
|
498
|
+
behavior: 'family-aspects',
|
|
499
|
+
rowsPath: descriptor.rowsPath,
|
|
500
|
+
tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,
|
|
501
|
+
},
|
|
502
|
+
coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],
|
|
503
|
+
customOperation: undefined,
|
|
504
|
+
operationsConfiguration: {
|
|
505
|
+
[CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },
|
|
506
|
+
[CoreResourceOperation.LIST]: {
|
|
507
|
+
variants: [{
|
|
508
|
+
...createReadOnlyListApiVariant(),
|
|
509
|
+
/* The two slices every aspect list needs: which family, which kind. */
|
|
510
|
+
filterFields: {
|
|
511
|
+
family: z.string(),
|
|
512
|
+
aspectKey: z.string(),
|
|
513
|
+
},
|
|
514
|
+
}],
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Every aspect's relationships:
|
|
521
|
+
*
|
|
522
|
+
* - the STANDALONE application anchor each INTROSPECTION resource needs (primary scope derives
|
|
523
|
+
* from it; STANDALONE keeps the route flat — the established pattern of its two siblings), and
|
|
524
|
+
* - one REFERENCE relationship per edge, carrying `foreignKeyField`: that is what teaches the
|
|
525
|
+
* frontend's field-identifier map to resolve the envelope field to its target resource, which
|
|
526
|
+
* is the entire navigation story (labels, popups, links) with no bespoke lookup anywhere.
|
|
527
|
+
*/
|
|
528
|
+
export function buildWorkbenchAspectRelationships() {
|
|
529
|
+
const relationships = [];
|
|
530
|
+
for (const descriptor of WORKBENCH_ASPECT_DESCRIPTORS) {
|
|
531
|
+
relationships.push(createResourcesRelationship(CoreResourceType.APPLICATION, descriptor.resourceType, ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY, {
|
|
532
|
+
nature: RelationshipNature.COMPOSITION,
|
|
533
|
+
isPrimaryScope: true,
|
|
534
|
+
accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,
|
|
535
|
+
contextPolicy: {},
|
|
536
|
+
}));
|
|
537
|
+
for (const edge of descriptor.edges) {
|
|
538
|
+
const target = DESCRIPTOR_BY_ASPECT_KEY.get(edge.targetAspectKey);
|
|
539
|
+
if (!target) {
|
|
540
|
+
throw new Error(`workbench aspect ${descriptor.aspectKey}: edge ${edge.field} targets unknown aspect ${edge.targetAspectKey}`);
|
|
541
|
+
}
|
|
542
|
+
relationships.push(createResourcesRelationship(target.resourceType, descriptor.resourceType, ResourceRelationshipCardinality.ONE, edge.many ? ResourceRelationshipCardinality.MANY : ResourceRelationshipCardinality.ONE, {
|
|
543
|
+
nature: RelationshipNature.REFERENCE,
|
|
544
|
+
foreignKeyField: edge.field,
|
|
545
|
+
contextPolicy: {},
|
|
546
|
+
/*
|
|
547
|
+
* STANDALONE here too, not only on the application anchor: a relationship without it
|
|
548
|
+
* makes the router mount the holder NESTED under its target and drop the flat route —
|
|
549
|
+
* measured on this very sweep (every edge-carrying aspect 404'd flat while its
|
|
550
|
+
* edge-free siblings answered 200). A reference edge is navigation metadata, never a
|
|
551
|
+
* URL parent.
|
|
552
|
+
*/
|
|
553
|
+
accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,
|
|
554
|
+
/*
|
|
555
|
+
* The aspect graph is CYCLIC by nature — an ICP names its triggers and a trigger names
|
|
556
|
+
* the ICPs it affects; a task prerequisites tasks; an asset derives from an asset. These
|
|
557
|
+
* are REFERENCES in a knowledge graph, not a containment tree, so every edge declares
|
|
558
|
+
* the cycle admissible rather than pretending the graph is a DAG.
|
|
559
|
+
*/
|
|
560
|
+
allowCycle: true,
|
|
561
|
+
/*
|
|
562
|
+
* The two self-edges (a task's prerequisites, an asset's derivation source) are graphs
|
|
563
|
+
* with roots — a MASTER asset derives from nothing — and the factory refuses a
|
|
564
|
+
* self-reference whose parent is mandatory for exactly that reason. Harmless on the
|
|
565
|
+
* non-self edges: every aspect reference is optional by nature (dangling-tolerant).
|
|
566
|
+
*/
|
|
567
|
+
parentResourceRequirement: ResourceParentResourceRequirement.OPTIONAL,
|
|
568
|
+
}));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return relationships;
|
|
572
|
+
}
|
|
573
|
+
/** The factory-map slice for all aspect resources — merged into the workbench map by its owner. */
|
|
574
|
+
export const workbenchAspectResources_ConfigurationsFactoryMap = Object.fromEntries(WORKBENCH_ASPECT_DESCRIPTORS.map((descriptor) => [
|
|
575
|
+
descriptor.resourceType,
|
|
576
|
+
buildWorkbenchAspectConfigurationFactory(descriptor),
|
|
577
|
+
]));
|
|
578
|
+
//# sourceMappingURL=workbench-aspect-resources.custom.shared.schemas.js.map
|