@hyperscale0/udl 4.5.1 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/field-value.d.ts +5 -0
  3. package/dist/field-value.d.ts.map +1 -0
  4. package/dist/field-value.js +85 -0
  5. package/dist/field-value.js.map +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/object-contract.d.ts +27 -11
  11. package/dist/object-contract.d.ts.map +1 -1
  12. package/dist/object-contract.js +18 -92
  13. package/dist/object-contract.js.map +1 -1
  14. package/dist/object-semantics-schema.d.ts +5565 -0
  15. package/dist/object-semantics-schema.d.ts.map +1 -0
  16. package/dist/object-semantics-schema.js +119 -0
  17. package/dist/object-semantics-schema.js.map +1 -0
  18. package/dist/object-semantics.d.ts +6 -0
  19. package/dist/object-semantics.d.ts.map +1 -0
  20. package/dist/object-semantics.js +279 -0
  21. package/dist/object-semantics.js.map +1 -0
  22. package/dist/object-structures.d.ts +4 -0
  23. package/dist/object-structures.d.ts.map +1 -0
  24. package/dist/object-structures.js +134 -0
  25. package/dist/object-structures.js.map +1 -0
  26. package/dist/schema.d.ts +205 -0
  27. package/dist/schema.d.ts.map +1 -1
  28. package/dist/schema.js +5 -1
  29. package/dist/schema.js.map +1 -1
  30. package/dist/validation.js +1 -1
  31. package/dist/validation.js.map +1 -1
  32. package/package.json +2 -2
  33. package/spec/udl.schema.json +296 -245
  34. package/src/field-value.ts +87 -0
  35. package/src/index.ts +1 -0
  36. package/src/object-contract.ts +62 -98
  37. package/src/object-semantics-schema.ts +148 -0
  38. package/src/object-semantics.ts +378 -0
  39. package/src/object-structures.ts +255 -0
  40. package/src/schema.ts +5 -1
  41. package/src/validation.ts +1 -1
@@ -0,0 +1,87 @@
1
+ import * as z from "zod";
2
+ import { udlObjectIdSchema, type UdlField } from "./schema.js";
3
+
4
+ const amount = z.string().regex(/^(0|[1-9][0-9]{0,17})$/);
5
+ const text = z.string().min(1).max(2048);
6
+
7
+ /** Values, rather than declarations. The same validator feeds forms and admission. */
8
+ export function udlFieldValueSchema(field: UdlField): z.ZodType {
9
+ let schema: z.ZodType;
10
+ switch (field.type) {
11
+ case "money":
12
+ schema = amount
13
+ .refine(
14
+ (value) =>
15
+ /^(0|[1-9][0-9]{0,17})$/.test(value) &&
16
+ (field.minimum === undefined ||
17
+ BigInt(value) >= BigInt(field.minimum)) &&
18
+ (field.maximum === undefined ||
19
+ BigInt(value) <= BigInt(field.maximum)),
20
+ "Money is outside its declared bounds",
21
+ )
22
+ .meta({
23
+ ...(field.minimum !== undefined
24
+ ? { "x-udl-minimum": field.minimum }
25
+ : {}),
26
+ ...(field.maximum !== undefined
27
+ ? { "x-udl-maximum": field.maximum }
28
+ : {}),
29
+ });
30
+ break;
31
+ case "account":
32
+ schema = text;
33
+ break;
34
+ case "ref":
35
+ schema = field.targetKind === "object" ? udlObjectIdSchema : text;
36
+ break;
37
+ case "date":
38
+ schema = z.iso.datetime({ offset: true });
39
+ break;
40
+ case "duration":
41
+ schema = z.number().int().safe().positive();
42
+ break;
43
+ case "text": {
44
+ let value = z
45
+ .string()
46
+ .min(field.minLength ?? 1)
47
+ .max(field.maxLength ?? 2048);
48
+ if (field.pattern) value = value.regex(new RegExp(field.pattern));
49
+ schema = value;
50
+ break;
51
+ }
52
+ case "integer": {
53
+ let value = z.number().int().safe();
54
+ if (field.minimum !== undefined) value = value.min(field.minimum);
55
+ if (field.maximum !== undefined) value = value.max(field.maximum);
56
+ schema = value;
57
+ break;
58
+ }
59
+ case "percent":
60
+ schema = z.number().int().min(0).max(10000);
61
+ break;
62
+ case "boolean":
63
+ schema = z.boolean();
64
+ break;
65
+ case "enum":
66
+ schema = z.enum(field.values);
67
+ break;
68
+ case "list": {
69
+ const item =
70
+ field.item === "money"
71
+ ? amount
72
+ : field.item === "date"
73
+ ? z.iso.datetime({ offset: true })
74
+ : field.item === "integer"
75
+ ? z.number().int().safe()
76
+ : field.item === "ref" && field.targetKind === "object"
77
+ ? udlObjectIdSchema
78
+ : text;
79
+ schema = z.array(item).max(field.maxItems);
80
+ break;
81
+ }
82
+ }
83
+ if ("value" in field && field.value !== undefined)
84
+ schema = z.literal(field.value);
85
+ if (field.description) schema = schema.describe(field.description);
86
+ return schema;
87
+ }
package/src/index.ts CHANGED
@@ -30,4 +30,5 @@ export {
30
30
  validateReportDefinition,
31
31
  reportExpressionTypes,
32
32
  } from "./reporting-validation.js";
33
+ export { udlFieldValueSchema } from "./field-value.js";
33
34
  export * from "./object-contract.js";
@@ -1,9 +1,18 @@
1
1
  import * as z from "zod";
2
+ import { udlFieldValueSchema } from "./field-value.js";
3
+ import {
4
+ projectDocumentSemantics,
5
+ presentationLabel,
6
+ } from "./object-semantics.js";
7
+ import type { DocumentSemantics } from "./object-semantics-schema.js";
8
+ export * from "./object-semantics-schema.js";
9
+ export {
10
+ projectDocumentSemantics,
11
+ presentationLabel,
12
+ } from "./object-semantics.js";
2
13
  import {
3
14
  udlExternalIdSchema,
4
- udlObjectIdSchema,
5
15
  type UdlDocument,
6
- type UdlField,
7
16
  type UdlObjectField,
8
17
  type UdlObjectKind,
9
18
  type UdlSubjectRequirement,
@@ -69,10 +78,41 @@ export interface ObjectActionDiscovery extends ObjectActionContext {
69
78
  creationOnlyNames?: readonly string[];
70
79
  inputSchema: JsonSchemaDocument;
71
80
  fieldsSchema: JsonSchemaDocument;
72
- availability:
73
- | { status: "available" }
74
- | { status: "unavailable"; code: string; message: string };
81
+ availability: ObjectActionAvailability;
75
82
  }
83
+ export type ObjectActionBlockerCode =
84
+ | "permission_required"
85
+ | "state_changed"
86
+ | "not_due"
87
+ | "deadline_passed"
88
+ | "clock_pending"
89
+ | "condition_not_met"
90
+ | "already_used"
91
+ | "evidence_required"
92
+ | "account_unavailable"
93
+ | "insufficient_money"
94
+ | "setup_required"
95
+ | "invalid_input"
96
+ | "operation_pending";
97
+ export type ObjectActionAvailability =
98
+ | { status: "available" }
99
+ | {
100
+ status: "requires_input";
101
+ subjectFields: readonly string[];
102
+ actionInputs: readonly string[];
103
+ parties: readonly string[];
104
+ reason: string;
105
+ }
106
+ | {
107
+ status: "unavailable";
108
+ blockers: readonly {
109
+ code: ObjectActionBlockerCode;
110
+ reason: string;
111
+ recheckAt?: string;
112
+ }[];
113
+ }
114
+ | { status: "unknown"; reason: string };
115
+
76
116
  export type EvidenceState =
77
117
  | "missing"
78
118
  | "pending"
@@ -107,6 +147,7 @@ export type RetainedObjectKind = Pick<
107
147
  creation: false;
108
148
  };
109
149
  export interface ObjectDiscovery {
150
+ semantics: DocumentSemantics;
110
151
  navigation: readonly { kind: string; title: string; creation: boolean }[];
111
152
  retainedKinds: readonly RetainedObjectKind[];
112
153
  productBuildId: string;
@@ -138,90 +179,6 @@ export interface ObjectActionResponse {
138
179
  evidence: readonly ObjectEvidenceSummary[];
139
180
  }
140
181
 
141
- const amount = z.string().regex(/^(0|[1-9][0-9]{0,17})$/);
142
- const text = z.string().min(1).max(2048);
143
-
144
- /** Values, rather than declarations. The same validator feeds forms and admission. */
145
- export function udlFieldValueSchema(field: UdlField): z.ZodType {
146
- let schema: z.ZodType;
147
- switch (field.type) {
148
- case "money":
149
- schema = amount
150
- .refine(
151
- (value) =>
152
- /^(0|[1-9][0-9]{0,17})$/.test(value) &&
153
- (field.minimum === undefined ||
154
- BigInt(value) >= BigInt(field.minimum)) &&
155
- (field.maximum === undefined ||
156
- BigInt(value) <= BigInt(field.maximum)),
157
- "Money is outside its declared bounds",
158
- )
159
- .meta({
160
- ...(field.minimum !== undefined
161
- ? { "x-udl-minimum": field.minimum }
162
- : {}),
163
- ...(field.maximum !== undefined
164
- ? { "x-udl-maximum": field.maximum }
165
- : {}),
166
- });
167
- break;
168
- case "account":
169
- schema = text;
170
- break;
171
- case "ref":
172
- schema = field.targetKind === "object" ? udlObjectIdSchema : text;
173
- break;
174
- case "date":
175
- schema = z.iso.datetime({ offset: true });
176
- break;
177
- case "duration":
178
- schema = z.number().int().safe().positive();
179
- break;
180
- case "text": {
181
- let value = z
182
- .string()
183
- .min(field.minLength ?? 1)
184
- .max(field.maxLength ?? 2048);
185
- if (field.pattern) value = value.regex(new RegExp(field.pattern));
186
- schema = value;
187
- break;
188
- }
189
- case "integer": {
190
- let value = z.number().int().safe();
191
- if (field.minimum !== undefined) value = value.min(field.minimum);
192
- if (field.maximum !== undefined) value = value.max(field.maximum);
193
- schema = value;
194
- break;
195
- }
196
- case "percent":
197
- schema = z.number().int().min(0).max(10000);
198
- break;
199
- case "boolean":
200
- schema = z.boolean();
201
- break;
202
- case "enum":
203
- schema = z.enum(field.values);
204
- break;
205
- case "list": {
206
- const item =
207
- field.item === "money"
208
- ? amount
209
- : field.item === "date"
210
- ? z.iso.datetime({ offset: true })
211
- : field.item === "integer"
212
- ? z.number().int().safe()
213
- : field.item === "ref" && field.targetKind === "object"
214
- ? udlObjectIdSchema
215
- : text;
216
- schema = z.array(item).max(field.maxItems);
217
- break;
218
- }
219
- }
220
- if ("value" in field && field.value !== undefined)
221
- schema = z.literal(field.value);
222
- if (field.description) schema = schema.describe(field.description);
223
- return schema;
224
- }
225
182
  /** Creation accepts the full metadata union, with every value optional. */
226
183
  export function objectCreateSchema(kind: UdlObjectKind): z.ZodObject {
227
184
  return z.strictObject({
@@ -239,20 +196,22 @@ export function objectCreateSchema(kind: UdlObjectKind): z.ZodObject {
239
196
  });
240
197
  }
241
198
 
242
- /** Canonical UDL supplies meaning; core adds authority, lifecycle and readiness. */
199
+ /** Canonical UDL supplies meaning; core adds authority, instance state and readiness. */
243
200
  export function projectObjectDiscovery(
244
201
  document: UdlDocument,
245
202
  build: Pick<ObjectDiscovery, "productBuildId" | "digest">,
246
203
  ): ObjectDiscovery {
204
+ const semantics = projectDocumentSemantics(document);
247
205
  return {
248
206
  ...build,
207
+ semantics,
249
208
  navigation: document.objects.map((kind) => ({
250
209
  kind: kind.id,
251
210
  title: kind.title,
252
211
  creation: true,
253
212
  })),
254
213
  retainedKinds: [],
255
- kinds: document.objects.map((kind) => ({
214
+ kinds: semantics.objects.map((kind) => ({
256
215
  ...build,
257
216
  creation: true,
258
217
  kind: kind.id,
@@ -344,9 +303,7 @@ export function projectObjectDiscovery(
344
303
  )!.name,
345
304
  },
346
305
  name: action.publicAction,
347
- title: action.publicAction
348
- .replace(/_/g, " ")
349
- .replace(/^./, (letter) => letter.toUpperCase()),
306
+ title: action.title ?? presentationLabel(action.publicAction),
350
307
  summary: action.summary,
351
308
  instrument: instrument.id,
352
309
  action: name,
@@ -393,8 +350,13 @@ export function projectObjectDiscovery(
393
350
  availability: unbound
394
351
  ? {
395
352
  status: "unavailable" as const,
396
- code: "subject_adapter_unbound",
397
- message: `Adapter declaration ${unbound.binding} is unavailable`,
353
+ blockers: [
354
+ {
355
+ code: "setup_required" as const,
356
+ reason:
357
+ "A required external check has not been set up.",
358
+ },
359
+ ],
398
360
  }
399
361
  : { status: "available" as const },
400
362
  },
@@ -559,13 +521,15 @@ export function validateObjectActionSubject(
559
521
  const issues: SubjectRequirementIssue[] = [];
560
522
  if (
561
523
  action.availability.status === "unavailable" &&
562
- action.availability.code === "subject_adapter_unbound"
524
+ action.availability.blockers.some(
525
+ (blocker) => blocker.code === "setup_required",
526
+ )
563
527
  ) {
564
528
  issues.push({
565
529
  code: "subject_adapter_unbound",
566
530
  action: action.name,
567
531
  origin,
568
- message: action.availability.message,
532
+ message: "A required external check has not been set up.",
569
533
  });
570
534
  }
571
535
  const admitted = new Set(action.requirements.map((field) => field.name));
@@ -0,0 +1,148 @@
1
+ import * as z from "zod";
2
+ import {
3
+ udlActionSchema,
4
+ udlCalculationSchema,
5
+ udlFieldSchema,
6
+ udlInstrumentSchema,
7
+ udlObjectKindSchema,
8
+ udlObjectAttachmentSchema,
9
+ udlPartySchema,
10
+ udlRequirementSchema,
11
+ udlValueSchema,
12
+ udlKernelOperationSchema,
13
+ subjectPartyRoles,
14
+ } from "./schema.js";
15
+
16
+ const text = z.string();
17
+ export const cardinalitySchema = z.strictObject({
18
+ min: z.number().int().nonnegative(),
19
+ // null means the declarations supply no upper bound.
20
+ max: z.number().int().nonnegative().nullable(),
21
+ });
22
+ export const semanticOwnerSchema = z.union([
23
+ z.strictObject({ kind: z.literal("instrument"), instrument: text }),
24
+ z.strictObject({ kind: z.literal("role"), role: z.enum(subjectPartyRoles) }),
25
+ z.strictObject({ kind: z.literal("party"), party: text }),
26
+ z.strictObject({ kind: z.literal("adapter"), adapter: text }),
27
+ ]);
28
+ export const semanticAccountSchema = z.strictObject({
29
+ path: text,
30
+ scope: text,
31
+ instrument: text,
32
+ field: text,
33
+ owner: semanticOwnerSchema,
34
+ book: z.enum(["cash", "claim"]),
35
+ key: text.optional(),
36
+ contra: z.boolean(),
37
+ });
38
+ export const semanticRoleSchema = z.union([
39
+ semanticOwnerSchema,
40
+ z.strictObject({ kind: z.enum(["caller", "clock"]) }),
41
+ z.strictObject({ kind: z.literal("parent"), instruments: z.array(text) }),
42
+ ]);
43
+ export const semanticRelationshipSchema = z.strictObject({
44
+ kind: z.enum(["reference", "child"]),
45
+ via: z.enum(["field", "attachment", "invocation", "parent_actor"]),
46
+ name: text,
47
+ targetKind: z.enum(["instrument", "object"]),
48
+ targets: z.array(text),
49
+ cardinality: cardinalitySchema,
50
+ });
51
+ export const semanticMoneyEffectSchema = z.strictObject({
52
+ key: text,
53
+ operation: udlKernelOperationSchema,
54
+ amount: udlValueSchema.nullable(),
55
+ from: z.array(semanticAccountSchema),
56
+ to: z.array(semanticAccountSchema),
57
+ // Expressions describe declared effects, never a receipt or evaluated quote.
58
+ disposition: z.enum(["held", "paid", "released", "claim", "unresolved"]),
59
+ transfer: text.nullable(),
60
+ reservations: z.array(
61
+ z.strictObject({ instrument: text, action: text, key: text }),
62
+ ),
63
+ });
64
+ export const semanticActionSchema = z.strictObject({
65
+ name: text,
66
+ alias: text.nullable(),
67
+ title: text,
68
+ actor: udlActionSchema.shape.actor,
69
+ roles: z.array(semanticRoleSchema),
70
+ from: z.array(text),
71
+ to: text.nullable(),
72
+ references: z.array(udlFieldSchema).optional(),
73
+ invokedBy: z
74
+ .array(
75
+ z.strictObject({
76
+ instrument: text,
77
+ action: text,
78
+ alias: text.nullable(),
79
+ invocation: udlActionSchema.shape.invoke.unwrap().element,
80
+ }),
81
+ )
82
+ .optional(),
83
+ inputs: udlActionSchema.shape.input,
84
+ subject: udlActionSchema.shape.subject,
85
+ prerequisites: udlActionSchema.shape.requires,
86
+ due: udlActionSchema.shape.due.unwrap().nullable(),
87
+ deadline: udlActionSchema.shape.deadline.unwrap().nullable(),
88
+ calculations: z.array(udlCalculationSchema),
89
+ assignments: udlActionSchema.shape.set.unwrap(),
90
+ moves: udlActionSchema.shape.moves,
91
+ moneyEffects: z.array(semanticMoneyEffectSchema),
92
+ invokes: udlActionSchema.shape.invoke.unwrap(),
93
+ });
94
+ export const semanticStructureSchema = z.enum([
95
+ "stage_and_decisions",
96
+ "payment_review",
97
+ "held_funds",
98
+ "reserved_payment",
99
+ "payment_calendar",
100
+ "funding_progress",
101
+ "cancellation_review",
102
+ "external_checks",
103
+ "report_view",
104
+ ]);
105
+ export const semanticInstrumentSchema = z.strictObject({
106
+ id: text,
107
+ title: text,
108
+ subject: text.optional(),
109
+ fields: z.array(udlFieldSchema),
110
+ accounts: z.array(semanticAccountSchema),
111
+ relationships: z.array(semanticRelationshipSchema),
112
+ calculations: z.array(udlCalculationSchema),
113
+ invariants: z.array(udlRequirementSchema),
114
+ initialState: text,
115
+ lifecycle: z.strictObject({
116
+ initial: text,
117
+ states: z.array(
118
+ z.strictObject({ name: text, label: text, terminal: z.boolean() }),
119
+ ),
120
+ cancellationActions: z.array(text),
121
+ }),
122
+ actions: z.array(semanticActionSchema),
123
+ reports: udlInstrumentSchema.shape.reports.unwrap(),
124
+ structures: z.array(semanticStructureSchema),
125
+ });
126
+ export const documentSemanticsSchema = z.strictObject({
127
+ currency: text,
128
+ parties: z.record(text, udlPartySchema),
129
+ objects: z.array(
130
+ z.strictObject({
131
+ ...udlObjectKindSchema.shape,
132
+ attachments: z.array(
133
+ z.strictObject({
134
+ ...udlObjectAttachmentSchema.shape,
135
+ cardinality: cardinalitySchema,
136
+ }),
137
+ ),
138
+ }),
139
+ ),
140
+ instruments: z.array(semanticInstrumentSchema),
141
+ });
142
+ export type DocumentSemantics = z.infer<typeof documentSemanticsSchema>;
143
+ export type SemanticInstrument = z.infer<typeof semanticInstrumentSchema>;
144
+ export type SemanticAction = z.infer<typeof semanticActionSchema>;
145
+ export type SemanticAccount = z.infer<typeof semanticAccountSchema>;
146
+ export type SemanticOwner = z.infer<typeof semanticOwnerSchema>;
147
+ export type SemanticRelationship = z.infer<typeof semanticRelationshipSchema>;
148
+ export type SemanticMoneyEffect = z.infer<typeof semanticMoneyEffectSchema>;