@hyperscale0/udl 4.2.1 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/schema.ts CHANGED
@@ -73,7 +73,7 @@ export const udlFieldSchema = z.discriminatedUnion("type", [
73
73
  z.strictObject({
74
74
  ...fieldBase,
75
75
  type: z.literal("account"),
76
- owner: name,
76
+ owner: z.union([name, z.strictObject({ adapter: name })]),
77
77
  key: name.optional(),
78
78
  book: z.enum(["cash", "claim"]).default("cash"),
79
79
  contra: z.literal(true).optional(),
@@ -154,10 +154,43 @@ export const udlObjectFieldSchema = z.discriminatedUnion("type", [
154
154
  boundedListField,
155
155
  ]);
156
156
 
157
+ /** Values are constants or resolved typed paths, never executable strings. */
158
+ export const udlValueSchema = z.union([
159
+ z.strictObject({ literal: scalar }),
160
+ z.strictObject({ field: path }),
161
+ ]);
162
+ const value = udlValueSchema;
163
+
164
+ const comparisonRequirement = z.strictObject({
165
+ kind: z.literal("compare"),
166
+ left: value,
167
+ operator: z.enum(["==", "!=", "<", "<=", ">", ">="]),
168
+ right: value,
169
+ });
170
+
171
+ // Alternatives are OR; each invocation path is a conjunction of scoped guards.
172
+ export const udlSubjectConditionsSchema = z
173
+ .array(
174
+ z
175
+ .array(
176
+ z.strictObject({
177
+ instrument: instrumentId,
178
+ action: name,
179
+ guard: comparisonRequirement,
180
+ valueType: z.enum(["money", "date"]).optional(),
181
+ }),
182
+ )
183
+ .min(1)
184
+ .max(32),
185
+ )
186
+ .min(1)
187
+ .max(128);
188
+
157
189
  export const udlSubjectRequirementSchema = z.strictObject({
158
190
  field: udlObjectFieldSchema,
159
191
  // Present only for an authored rename.
160
192
  objectField: name.optional(),
193
+ when: udlSubjectConditionsSchema.optional(),
161
194
  });
162
195
 
163
196
  export const adapterSubjectSnapshotSchema = z.strictObject({
@@ -192,6 +225,7 @@ export type AttachmentPartyBinding = z.infer<
192
225
  >;
193
226
  export const udlObjectAttachmentSchema = z.strictObject({
194
227
  name,
228
+ parent: name.optional(),
195
229
  instrument: instrumentId,
196
230
  parties: z.record(name, attachmentPartyBindingSchema),
197
231
  });
@@ -207,12 +241,6 @@ export const udlObjectKindSchema = z.strictObject({
207
241
  columns: z.array(name).max(8),
208
242
  });
209
243
 
210
- /** Values are constants or resolved typed paths, never executable strings. */
211
- export const udlValueSchema = z.union([
212
- z.strictObject({ literal: scalar }),
213
- z.strictObject({ field: path }),
214
- ]);
215
- const value = udlValueSchema;
216
244
  const values = z.array(value).min(1).max(256);
217
245
 
218
246
  const selection = z.strictObject({
@@ -295,13 +323,6 @@ export const udlCalculationSchema = z.discriminatedUnion("op", [
295
323
  }),
296
324
  ]);
297
325
 
298
- const comparisonRequirement = z.strictObject({
299
- kind: z.literal("compare"),
300
- left: value,
301
- operator: z.enum(["==", "!=", "<", "<=", ">", ">="]),
302
- right: value,
303
- });
304
-
305
326
  export const udlRequirementSchema = z.discriminatedUnion("kind", [
306
327
  z.strictObject({
307
328
  kind: z.literal("hours"),
package/src/validation.ts CHANGED
@@ -153,7 +153,7 @@ function commonField(
153
153
  (field) =>
154
154
  field?.type !== "account" ||
155
155
  field.book !== first.book ||
156
- field.owner !== first.owner ||
156
+ JSON.stringify(field.owner) !== JSON.stringify(first.owner) ||
157
157
  field.key !== first.key ||
158
158
  field.contra !== first.contra,
159
159
  )
@@ -455,8 +455,32 @@ export function validateUdl(value: unknown): UdlValidationResult {
455
455
  )
456
456
  )
457
457
  add(where, `${f.name} is a sealed instance field`);
458
+ if (f.type === "account" && typeof f.owner === "object") {
459
+ const binding = f.owner.adapter;
460
+ const declarations = Object.values(instrument?.actions ?? {})
461
+ .flatMap((action) => action.subject?.adapters ?? [])
462
+ .filter((entry) => entry.binding === binding);
463
+ if (!declarations.length)
464
+ add(
465
+ where,
466
+ `${f.name} needs adapter declaration ${binding}`,
467
+ "subject_adapter_unbound",
468
+ );
469
+ const providers = new Set(
470
+ declarations.flatMap((entry) =>
471
+ entry.snapshot ? [entry.snapshot.provider] : [],
472
+ ),
473
+ );
474
+ if (providers.size > 1)
475
+ add(
476
+ where,
477
+ `${binding} names conflicting providers`,
478
+ "subject_adapter_unbound",
479
+ );
480
+ }
458
481
  if (
459
482
  f.type === "account" &&
483
+ typeof f.owner === "string" &&
460
484
  f.owner !== "self" &&
461
485
  !moneyParty(document, instrument, f.owner)
462
486
  )
@@ -1269,14 +1293,14 @@ export function validateUdl(value: unknown): UdlValidationResult {
1269
1293
  from.book !== to.book
1270
1294
  )
1271
1295
  add(where, "moves cannot cross account books", "UDL4001");
1272
- const samePartyAccount =
1296
+ const sameBoundAccount =
1273
1297
  from?.type === "account" &&
1274
1298
  to?.type === "account" &&
1275
1299
  from.owner !== "self" &&
1276
- from.owner === to.owner &&
1300
+ JSON.stringify(from.owner) === JSON.stringify(to.owner) &&
1277
1301
  from.book === to.book &&
1278
1302
  (from.key ?? "balance") === (to.key ?? "balance");
1279
- if (move.from === move.to || samePartyAccount)
1303
+ if (move.from === move.to || sameBoundAccount)
1280
1304
  add(where, "a transfer needs distinct accounts", "UDL4001");
1281
1305
  } else expect(move.transfer, "text", where, action.input, action);
1282
1306
  }