@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/CHANGELOG.md +16 -0
- package/dist/object-contract.d.ts +11 -7
- package/dist/object-contract.d.ts.map +1 -1
- package/dist/object-contract.js +95 -5
- package/dist/object-contract.js.map +1 -1
- package/dist/schema.d.ts +201 -12
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +28 -13
- package/dist/schema.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +16 -4
- package/dist/validation.js.map +1 -1
- package/docs/README.md +30 -28
- package/package.json +2 -2
- package/spec/README.md +30 -28
- package/spec/udl.schema.json +113 -51
- package/src/object-contract.ts +135 -18
- package/src/schema.ts +35 -14
- package/src/validation.ts +28 -4
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
|
|
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 ||
|
|
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
|
}
|