@hyperscale0/udl 2.6.1 → 3.0.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 (213) hide show
  1. package/CHANGELOG.md +2 -330
  2. package/LICENSING.md +1 -2
  3. package/README.md +3 -114
  4. package/TRADEMARKS.md +2 -2
  5. package/dist/diagnostics.d.ts +21 -201
  6. package/dist/diagnostics.d.ts.map +1 -1
  7. package/dist/diagnostics.js +29 -193
  8. package/dist/diagnostics.js.map +1 -1
  9. package/dist/evolution.d.ts +3 -132
  10. package/dist/evolution.d.ts.map +1 -1
  11. package/dist/evolution.js +29 -633
  12. package/dist/evolution.js.map +1 -1
  13. package/dist/finance.d.ts +5 -72
  14. package/dist/finance.d.ts.map +1 -1
  15. package/dist/finance.js +233 -737
  16. package/dist/finance.js.map +1 -1
  17. package/dist/index.d.ts +7 -18
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -12
  20. package/dist/index.js.map +1 -1
  21. package/dist/instrument-references.d.ts.map +1 -1
  22. package/dist/instrument-references.js +3 -2
  23. package/dist/instrument-references.js.map +1 -1
  24. package/dist/schema.d.ts +2079 -4564
  25. package/dist/schema.d.ts.map +1 -1
  26. package/dist/schema.js +270 -1609
  27. package/dist/schema.js.map +1 -1
  28. package/dist/validation.d.ts +8 -104
  29. package/dist/validation.d.ts.map +1 -1
  30. package/dist/validation.js +653 -3271
  31. package/dist/validation.js.map +1 -1
  32. package/docs/README.md +147 -14
  33. package/package.json +7 -11
  34. package/spec/README.md +151 -164
  35. package/spec/darb.udl.json +249 -0
  36. package/spec/udl.schema.json +4104 -3202
  37. package/src/diagnostics.ts +51 -246
  38. package/src/evolution.ts +39 -1045
  39. package/src/finance.ts +287 -1127
  40. package/src/index.ts +18 -142
  41. package/src/instrument-references.ts +3 -2
  42. package/src/schema.ts +285 -1843
  43. package/src/validation.ts +870 -5611
  44. package/conformance/README.md +0 -82
  45. package/conformance/evolution/action-contract.expected.json +0 -10
  46. package/conformance/evolution/action-contract.live.udl +0 -44
  47. package/conformance/evolution/action-contract.next.udl +0 -45
  48. package/conformance/evolution/product-identity.expected.json +0 -10
  49. package/conformance/evolution/product-identity.live.udl +0 -55
  50. package/conformance/evolution/product-identity.next.udl +0 -55
  51. package/conformance/evolution/version-required.expected.json +0 -10
  52. package/conformance/evolution/version-required.live.udl +0 -55
  53. package/conformance/evolution/version-required.next.udl +0 -58
  54. package/conformance/invalid/action-without-transition.expected.json +0 -10
  55. package/conformance/invalid/action-without-transition.udl +0 -60
  56. package/conformance/invalid/agent-description-too-long.expected.json +0 -10
  57. package/conformance/invalid/agent-description-too-long.udl +0 -57
  58. package/conformance/invalid/blank-title.expected.json +0 -10
  59. package/conformance/invalid/blank-title.udl +0 -54
  60. package/conformance/invalid/call-binds-results.expected.json +0 -10
  61. package/conformance/invalid/call-binds-results.udl +0 -314
  62. package/conformance/invalid/call-unknown-action.expected.json +0 -10
  63. package/conformance/invalid/call-unknown-action.udl +0 -314
  64. package/conformance/invalid/composition-dial-duplicate-key.expected.json +0 -10
  65. package/conformance/invalid/composition-dial-duplicate-key.udl +0 -73
  66. package/conformance/invalid/depth-budget.expected.json +0 -10
  67. package/conformance/invalid/depth-budget.udl +0 -49
  68. package/conformance/invalid/duplicate-subject.expected.json +0 -10
  69. package/conformance/invalid/duplicate-subject.udl +0 -230
  70. package/conformance/invalid/forged-effects.expected.json +0 -10
  71. package/conformance/invalid/forged-effects.udl +0 -76
  72. package/conformance/invalid/format-version.expected.json +0 -10
  73. package/conformance/invalid/format-version.udl +0 -54
  74. package/conformance/invalid/instrument-id-not-snake-case.expected.json +0 -10
  75. package/conformance/invalid/instrument-id-not-snake-case.udl +0 -54
  76. package/conformance/invalid/invalid-aggregate-gate-shape.expected.json +0 -10
  77. package/conformance/invalid/invalid-aggregate-gate-shape.udl +0 -1819
  78. package/conformance/invalid/invalid-check-duration.expected.json +0 -10
  79. package/conformance/invalid/invalid-check-duration.udl +0 -219
  80. package/conformance/invalid/invalid-dial-anchor.expected.json +0 -10
  81. package/conformance/invalid/invalid-dial-anchor.udl +0 -219
  82. package/conformance/invalid/invalid-exception-parent-ref.expected.json +0 -10
  83. package/conformance/invalid/invalid-exception-parent-ref.udl +0 -2487
  84. package/conformance/invalid/invalid-exposure-shape.expected.json +0 -10
  85. package/conformance/invalid/invalid-exposure-shape.udl +0 -1819
  86. package/conformance/invalid/invalid-journeys.expected.json +0 -10
  87. package/conformance/invalid/invalid-journeys.udl +0 -77
  88. package/conformance/invalid/invalid-remainder.expected.json +0 -10
  89. package/conformance/invalid/invalid-remainder.udl +0 -220
  90. package/conformance/invalid/invalid-schema-keyword.expected.json +0 -10
  91. package/conformance/invalid/invalid-schema-keyword.udl +0 -220
  92. package/conformance/invalid/invalid-utf8.expected.json +0 -10
  93. package/conformance/invalid/invalid-utf8.udl +0 -1
  94. package/conformance/invalid/leaf-effect-mismatch.expected.json +0 -10
  95. package/conformance/invalid/leaf-effect-mismatch.udl +0 -314
  96. package/conformance/invalid/malformed-json.expected.json +0 -10
  97. package/conformance/invalid/malformed-json.udl +0 -1
  98. package/conformance/invalid/missing-create-action.expected.json +0 -10
  99. package/conformance/invalid/missing-create-action.udl +0 -48
  100. package/conformance/invalid/missing-exception-amount-field.expected.json +0 -10
  101. package/conformance/invalid/missing-exception-amount-field.udl +0 -2487
  102. package/conformance/invalid/missing-exception-contract.expected.json +0 -14
  103. package/conformance/invalid/missing-exception-contract.udl +0 -2523
  104. package/conformance/invalid/missing-exception-reason-field.expected.json +0 -10
  105. package/conformance/invalid/missing-exception-reason-field.udl +0 -2487
  106. package/conformance/invalid/not-an-object.expected.json +0 -10
  107. package/conformance/invalid/not-an-object.udl +0 -1
  108. package/conformance/invalid/payout-reconcile-not-a-bank-debit.expected.json +0 -10
  109. package/conformance/invalid/payout-reconcile-not-a-bank-debit.udl +0 -259
  110. package/conformance/invalid/piece-plan-without-partition.expected.json +0 -10
  111. package/conformance/invalid/piece-plan-without-partition.udl +0 -305
  112. package/conformance/invalid/private-action-independent-approval.expected.json +0 -10
  113. package/conformance/invalid/private-action-independent-approval.udl +0 -314
  114. package/conformance/invalid/quote-freeze-set-incomplete.expected.json +0 -10
  115. package/conformance/invalid/quote-freeze-set-incomplete.udl +0 -261
  116. package/conformance/invalid/quote-named-reference-gate.expected.json +0 -10
  117. package/conformance/invalid/quote-named-reference-gate.udl +0 -50
  118. package/conformance/invalid/reconcile-named-reference-gate.expected.json +0 -10
  119. package/conformance/invalid/reconcile-named-reference-gate.udl +0 -50
  120. package/conformance/invalid/unfund-order-not-reversed.expected.json +0 -10
  121. package/conformance/invalid/unfund-order-not-reversed.udl +0 -314
  122. package/conformance/invalid/unknown-key.expected.json +0 -10
  123. package/conformance/invalid/unknown-key.udl +0 -55
  124. package/conformance/invalid/unknown-reference-gate-field.expected.json +0 -10
  125. package/conformance/invalid/unknown-reference-gate-field.udl +0 -1819
  126. package/conformance/invalid/unknown-required-field.expected.json +0 -10
  127. package/conformance/invalid/unknown-required-field.udl +0 -220
  128. package/conformance/invalid/unreachable-state.expected.json +0 -10
  129. package/conformance/invalid/unreachable-state.udl +0 -55
  130. package/conformance/invalid/wrong-exception-amount-field.expected.json +0 -10
  131. package/conformance/invalid/wrong-exception-amount-field.udl +0 -2487
  132. package/conformance/invalid/wrong-exception-reason-field.expected.json +0 -10
  133. package/conformance/invalid/wrong-exception-reason-field.udl +0 -2487
  134. package/conformance/valid/agent-description.expected.json +0 -6
  135. package/conformance/valid/agent-description.udl +0 -66
  136. package/conformance/valid/attested.expected.json +0 -6
  137. package/conformance/valid/attested.udl +0 -251
  138. package/conformance/valid/cards.expected.json +0 -6
  139. package/conformance/valid/cards.udl +0 -1579
  140. package/conformance/valid/commerce-escrow.expected.json +0 -6
  141. package/conformance/valid/commerce-escrow.udl +0 -1512
  142. package/conformance/valid/compiled-crowdfunding.expected.json +0 -6
  143. package/conformance/valid/compiled-crowdfunding.udl +0 -1843
  144. package/conformance/valid/compiled-watch-club.expected.json +0 -6
  145. package/conformance/valid/compiled-watch-club.udl +0 -2486
  146. package/conformance/valid/complete-contract.expected.json +0 -6
  147. package/conformance/valid/complete-contract.udl +0 -218
  148. package/conformance/valid/effect-signatures.expected.json +0 -6
  149. package/conformance/valid/effect-signatures.udl +0 -75
  150. package/conformance/valid/hand-edited.expected.json +0 -6
  151. package/conformance/valid/hand-edited.udl +0 -1
  152. package/conformance/valid/insured-car-marketplace.expected.json +0 -6
  153. package/conformance/valid/insured-car-marketplace.udl +0 -1050
  154. package/conformance/valid/insured-travel.expected.json +0 -6
  155. package/conformance/valid/insured-travel.udl +0 -3469
  156. package/conformance/valid/minimal.expected.json +0 -6
  157. package/conformance/valid/minimal.udl +0 -62
  158. package/conformance/valid/piece-plan-calls.expected.json +0 -6
  159. package/conformance/valid/piece-plan-calls.udl +0 -314
  160. package/conformance/valid/protection.expected.json +0 -6
  161. package/conformance/valid/protection.udl +0 -1551
  162. package/conformance/valid/string-escaping.expected.json +0 -6
  163. package/conformance/valid/string-escaping.udl +0 -54
  164. package/conformance/valid/vocabulary.expected.json +0 -6
  165. package/conformance/valid/vocabulary.udl +0 -2012
  166. package/dist/allocation.d.ts +0 -60
  167. package/dist/allocation.d.ts.map +0 -1
  168. package/dist/allocation.js +0 -177
  169. package/dist/allocation.js.map +0 -1
  170. package/dist/check-profiles.d.ts +0 -57
  171. package/dist/check-profiles.d.ts.map +0 -1
  172. package/dist/check-profiles.js +0 -62
  173. package/dist/check-profiles.js.map +0 -1
  174. package/dist/distribution.d.ts +0 -15
  175. package/dist/distribution.d.ts.map +0 -1
  176. package/dist/distribution.js +0 -49
  177. package/dist/distribution.js.map +0 -1
  178. package/dist/effects.d.ts +0 -58
  179. package/dist/effects.d.ts.map +0 -1
  180. package/dist/effects.js +0 -1126
  181. package/dist/effects.js.map +0 -1
  182. package/dist/reference.d.ts +0 -3
  183. package/dist/reference.d.ts.map +0 -1
  184. package/dist/reference.js +0 -28
  185. package/dist/reference.js.map +0 -1
  186. package/dist/vocabulary.d.ts +0 -23
  187. package/dist/vocabulary.d.ts.map +0 -1
  188. package/dist/vocabulary.js +0 -1052
  189. package/dist/vocabulary.js.map +0 -1
  190. package/docs/funding-custody.md +0 -165
  191. package/docs/guide/01-a-document.md +0 -37
  192. package/docs/guide/02-money-steps.md +0 -23
  193. package/docs/guide/03-laws.md +0 -18
  194. package/docs/guide/04-fees-and-remainder.md +0 -36
  195. package/docs/guide/05-checks-updates-dials.md +0 -7
  196. package/docs/guide/06-effects.md +0 -11
  197. package/docs/guide/07-evolution.md +0 -11
  198. package/docs/guide/08-implementing.md +0 -13
  199. package/docs/guide/09-schedules-and-allocation.md +0 -132
  200. package/docs/llms-full.txt +0 -2008
  201. package/docs/llms.txt +0 -14
  202. package/docs/piece-plans.md +0 -148
  203. package/docs/reference/canonical.md +0 -16
  204. package/docs/reference/clauses.md +0 -1585
  205. package/docs/reference/cli.md +0 -24
  206. package/docs/reference/diagnostics.md +0 -38
  207. package/skills/udl/SKILL.md +0 -28
  208. package/src/allocation.ts +0 -259
  209. package/src/check-profiles.ts +0 -80
  210. package/src/distribution.ts +0 -61
  211. package/src/effects.ts +0 -1920
  212. package/src/reference.ts +0 -31
  213. package/src/vocabulary.ts +0 -1635
package/dist/schema.js CHANGED
@@ -1,1676 +1,337 @@
1
- import { referencePatternPrefix } from "./reference.js";
2
1
  import { z } from "zod";
3
- import { udlCheckEvidenceProfiles } from "./check-profiles.js";
4
- export const udlEffectKinds = [
5
- "decides",
6
- "holds",
7
- "moves",
8
- "notifies",
9
- "reads",
10
- "schedules",
11
- ];
12
- export const UDL_FORMAT_VERSION = 1;
13
- const snakeCasePattern = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
14
- const camelCasePattern = /^[a-z][A-Za-z0-9]*$/;
15
- const fieldPathPattern = /^[a-z][A-Za-z0-9]*(?:\.[a-z][A-Za-z0-9]*)*$/;
16
- const idPrefixPattern = /^[a-z]{2,8}$/;
17
- const eventNamePattern = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/;
18
- const udlSnakeCaseSchema = z
2
+ export const UDL_FORMAT_VERSION = 3;
3
+ const name = z
19
4
  .string()
20
- .regex(snakeCasePattern, "must be a snake_case identifier");
21
- const udlInstrumentIdSchema = udlSnakeCaseSchema.meta({
22
- "x-udl-reference": "instrument",
23
- });
24
- const udlActionNameSchema = z
25
- .string()
26
- .regex(snakeCasePattern, "must be a snake_case action identifier");
27
- const udlFieldNameSchema = z
28
- .string()
29
- .regex(camelCasePattern, "must be a camelCase field name");
30
- export const udlPublicActionSchema = z
31
- .string()
32
- .regex(camelCasePattern, "must be a camelCase public action name");
33
- export const udlInstrumentActionIdSchema = z
34
- .string()
35
- .max(160)
36
- .regex(/^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/, "must name an instrument action as instrument_id.action_key");
37
- const nonEmptyTextSchema = z
5
+ .regex(/^[a-z][a-zA-Z0-9_]*$/)
6
+ .max(80);
7
+ const text = z.string().min(1).max(2048);
8
+ const amount = z.string().regex(/^(0|[1-9][0-9]{0,17})$/);
9
+ const integer = z.number().int().safe();
10
+ const scalar = z.union([z.string(), integer, z.boolean()]);
11
+ const path = z
38
12
  .string()
39
- .refine((value) => value.trim().length > 0, "must not be blank");
40
- // Agent-facing presentation prose. Every MCP tool description an agent reads is
41
- // this string, so it is capped: a 400-character ceiling keeps a 60-tool surface
42
- // under the token budget a client spends before it can pick a tool.
43
- const udlAgentDescriptionSchema = nonEmptyTextSchema.max(400);
44
- const fieldPathSchema = z
45
- .string()
46
- .regex(fieldPathPattern, "must be a dot-separated camelCase field path");
47
- const instanceValuePathSchema = z
48
- .string()
49
- .regex(/^(?:fields|refs)\.[a-z][A-Za-z0-9]*$/, "must name one declared fields.* or refs.* value");
50
- const jsonObjectSchema = z.record(z.string(), z.json());
51
- const stringMapSchema = z.record(nonEmptyTextSchema, nonEmptyTextSchema);
52
- const providerFamilyIds = [
53
- "accounts_ledger_first_party",
54
- "identity_verification",
55
- "national_identity",
56
- "credit_bureau",
57
- "sanctions_screening",
58
- "enforcement_instrument",
59
- "credit_servicing",
60
- "collections_agency",
61
- "settlement_ingestion",
62
- "payouts",
63
- "banking_or_card_issuing",
64
- "insurance_carrier",
65
- "insurance_claims_adjuster",
66
- "air_carrier",
67
- "lodging_supplier",
68
- "email_or_notification",
69
- ];
70
- export const udlProviderFamilyIdSchema = z.enum(providerFamilyIds);
71
- const udlDeclaredValuePolicySchema = z.enum(["required", "optional", "none"]);
72
- const udlSubjectSchema = z.strictObject({
73
- declaredValue: udlDeclaredValuePolicySchema,
74
- kind: udlSnakeCaseSchema,
75
- schema: jsonObjectSchema,
76
- title: nonEmptyTextSchema,
77
- version: z.number().int().positive(),
78
- });
79
- const udlInstrumentSubjectSchema = z.strictObject({
80
- extensible: z.boolean().optional(),
81
- kinds: z.array(udlSnakeCaseSchema).min(1),
82
- });
83
- const udlExampleSchema = z.strictObject({
84
- input: jsonObjectSchema,
85
- name: nonEmptyTextSchema,
86
- output: z.json().optional(),
87
- });
88
- const udlLifecycleTransitionSchema = z.strictObject({
89
- from: z.array(udlSnakeCaseSchema).min(1),
90
- to: udlSnakeCaseSchema,
91
- });
92
- const udlLifecycleSchema = z.strictObject({
93
- initial: udlSnakeCaseSchema,
94
- states: z.array(udlSnakeCaseSchema).min(1),
95
- transitions: z.record(udlActionNameSchema, udlLifecycleTransitionSchema),
96
- });
97
- const udlUpdatePolicySchema = z.strictObject({
98
- examples: z.array(udlExampleSchema).optional(),
99
- fields: z.array(udlFieldNameSchema).min(1),
100
- states: z.array(udlSnakeCaseSchema).min(1),
101
- });
102
- const udlDateComparisonOperatorSchema = z.enum([">=", ">", "<=", "<", "=="]);
103
- const udlDateComparisonSchema = z.strictObject({
104
- localPath: fieldPathSchema,
105
- operator: udlDateComparisonOperatorSchema,
106
- referencedPath: fieldPathSchema,
107
- });
108
- const udlStoredFieldSchema = z.strictObject({ field: udlFieldNameSchema });
109
- const udlOffsetSchema = z.union([nonEmptyTextSchema, udlStoredFieldSchema]);
110
- const udlAttestationSchema = z.strictObject({
111
- forAction: udlActionNameSchema.optional(),
112
- action: instanceValuePathSchema,
113
- digest: instanceValuePathSchema,
114
- role: instanceValuePathSchema,
115
- expiresAt: instanceValuePathSchema,
116
- instrument: instanceValuePathSchema,
117
- party: z.string().regex(/^[a-z][A-Za-z0-9_]*$/),
118
- consume: udlActionNameSchema,
119
- });
120
- const udlGateSchema = z.strictObject({
121
- attests: udlAttestationSchema.optional(),
122
- /** Local field key <- referenced instance path; create actions only. */
123
- bind: z.record(udlFieldNameSchema, fieldPathSchema).optional(),
124
- dateComparison: udlDateComparisonSchema.optional(),
125
- dueBefore: z
126
- .strictObject({ field: udlFieldNameSchema, offset: nonEmptyTextSchema })
127
- .optional(),
128
- field: udlFieldNameSchema,
129
- /** Local instance path === referenced instance path at admission. */
130
- match: z.record(fieldPathSchema, fieldPathSchema).optional(),
131
- /** Opt-in over an optional reference field: absent passes, set is held to the clause. */
13
+ .regex(/^[a-z][a-zA-Z0-9_]*(?:\.[a-z][a-zA-Z0-9_]*)*$/)
14
+ .max(240);
15
+ const instrumentId = name.meta({ "x-udl-reference": "instrument" });
16
+ /** Currency belongs to the document. Accounts and amounts cannot override it. */
17
+ export const udlPartySchema = z.strictObject({
18
+ kind: z.enum(["person", "business", "staff"]),
19
+ role: name.optional(),
20
+ });
21
+ const fieldBase = {
22
+ name,
132
23
  optional: z.literal(true).optional(),
133
- statuses: z.array(udlSnakeCaseSchema).min(1),
134
- /** One dependent ever per referenced instance; create actions only. */
135
- unique: z
136
- .union([
137
- z.literal(true),
138
- z.strictObject({
139
- namespace: udlSnakeCaseSchema,
140
- byFields: z.array(udlFieldNameSchema).min(1).max(8),
141
- }),
142
- ])
143
- .optional(),
144
- });
145
- const udlBindingSchema = z.discriminatedUnion("from", [
146
- z.strictObject({ from: z.literal("const"), value: z.string() }),
147
- z.strictObject({ from: z.literal("instance"), path: fieldPathSchema }),
148
- z.strictObject({ from: z.literal("input"), path: fieldPathSchema }),
149
- ]);
150
- const udlStepOperationSchema = z.enum([
151
- "account.escrow.provision",
152
- "account.freeze",
153
- "account.unfreeze",
154
- ]);
155
- const udlMoveOperationSchema = z.enum([
156
- "internal_transfer.create",
157
- "internal_transfer.reserve",
158
- "internal_transfer.post",
159
- "internal_transfer.void",
160
- ]);
161
- export const udlKernelOperationSchema = z.enum([
162
- ...udlStepOperationSchema.options,
163
- ...udlMoveOperationSchema.options,
164
- ]);
165
- const udlStepSchema = z.strictObject({
166
- bind: z.record(fieldPathSchema, udlBindingSchema),
167
- capture: stringMapSchema.optional(),
168
- operation: udlStepOperationSchema,
169
- });
170
- const udlMoveSchema = z.strictObject({
171
- bind: z.record(fieldPathSchema, udlBindingSchema),
172
- capture: stringMapSchema.optional(),
173
- key: udlSnakeCaseSchema,
174
- operation: udlMoveOperationSchema,
175
- });
176
- const udlDueRecurrenceSchema = z.strictObject({
177
- countField: udlFieldNameSchema.optional(),
178
- delinquency: z.literal("parent_policy").optional(),
179
- drainAction: udlActionNameSchema.optional(),
180
- period: z.union([
181
- nonEmptyTextSchema,
182
- z.strictObject({
183
- calendar: z.enum(["gregorian", "hijri"]),
184
- monthEnd: z.literal("clamp_to_last_day"),
185
- months: z.number().int().positive(),
186
- }),
187
- ]),
188
- liability: z.literal("one_open").optional(),
189
- untilAction: udlActionNameSchema.optional(),
190
- untilField: udlFieldNameSchema.optional(),
191
- });
192
- const udlDueParentStatusSchema = z.strictObject({
193
- instrumentId: udlInstrumentIdSchema,
194
- refField: udlFieldNameSchema,
195
- statuses: z.array(udlSnakeCaseSchema).min(1),
196
- });
197
- const udlDueSchema = z.strictObject({
198
- every: udlDueRecurrenceSchema.optional(),
199
- field: udlFieldNameSchema,
200
- offset: udlOffsetSchema.optional(),
201
- whenParentStatus: udlDueParentStatusSchema.optional(),
202
- });
203
- const udlDeadlineSchema = z.strictObject({
204
- field: udlFieldNameSchema,
205
- offset: udlOffsetSchema.optional(),
206
- });
207
- const udlSetsAtSchema = z.strictObject({
208
- field: udlFieldNameSchema,
209
- marker: z.literal(true).optional(),
210
- offset: nonEmptyTextSchema,
211
- });
212
- const udlQuoteChargeSchema = z.strictObject({
213
- bps: z.number().int().min(0).max(9_999),
214
- withinOffset: nonEmptyTextSchema.optional(),
215
- });
216
- export const udlQuoteRetainedRoleSchema = z.enum([
217
- "payer",
218
- "beneficiary",
219
- "subjectHolder",
220
- ]);
221
- // A priced, expiring offer. The quoting action splits `baseField` into a charge
222
- // and a net at its own clock, freezes the fields the price was read from, and
223
- // stamps an expiry. Only the paired `commit` action may spend the offer, and
224
- // only while it is unexpired and its frozen fields still hold.
225
- const udlQuoteShape = {
226
- /** Date-time whose remaining time picks the charge tier; omit for elapsed age. */
227
- anchorField: udlFieldNameSchema.optional(),
228
- baseField: udlFieldNameSchema,
229
- chargeRef: udlFieldNameSchema,
230
- /** Party role that keeps the quoted charge instead of moving it through an escrow payout action. */
231
- chargeRetainedBy: udlQuoteRetainedRoleSchema.optional(),
232
- charges: z.array(udlQuoteChargeSchema).min(1),
233
- /** Fixed ISO-8601 duration from the quoting action, or a stored deadline. */
234
- expires: z.union([
235
- z.strictObject({ field: udlFieldNameSchema }),
236
- z.strictObject({ offset: nonEmptyTextSchema }),
237
- ]),
238
- fixes: z.array(udlFieldNameSchema).min(1),
239
- netDestinationField: udlFieldNameSchema,
240
- netRef: udlFieldNameSchema,
24
+ description: text.optional(),
241
25
  };
242
- const udlQuoteSchema = z.strictObject(udlQuoteShape);
243
- /**
244
- * Where a quoting action stamps the instant its offer stops being committable.
245
- * Derived from the author's net ref rather than a fixed name, so two offers on
246
- * one instrument never share the slot.
247
- */
248
- export function quoteExpiresAtRefKey(quote) {
249
- return `${quote.netRef}ExpiresAt`;
250
- }
251
- /**
252
- * Where a quoting action stamps the fingerprint of the field values it froze.
253
- * The committing action recomputes it against the live fields, which is how an
254
- * offer priced on one set of terms refuses to pay out against another.
255
- */
256
- export function quoteFrozenRefKey(quote) {
257
- return `${quote.netRef}Frozen`;
258
- }
259
- /**
260
- * Every ref key one quote seeds: the charge and the net the author named, plus
261
- * the two the machinery derives to hold the offer itself. One derivation, read
262
- * by the validator, the compiler, and the runtime alike.
263
- */
264
- export function quoteSeededRefKeys(quote) {
265
- return [
266
- quote.chargeRef,
267
- quote.netRef,
268
- quoteExpiresAtRefKey(quote),
269
- quoteFrozenRefKey(quote),
270
- ];
271
- }
272
- const udlDecisionSchema = z.strictObject({
273
- capability: udlProviderFamilyIdSchema,
274
- deadlineMs: z.number().int().positive().max(15_000),
275
- // A decision that never came back is not an approval. `approve` would let a
276
- // document release money because a counterparty went quiet, so the grammar
277
- // admits only the fail-closed answer. Every published document already
278
- // writes `decline`; widening later is additive, narrowing later would not
279
- // be.
280
- onTimeout: z.literal("decline"),
281
- });
282
- const udlAggregateCheckSchema = z.discriminatedUnion("kind", [
26
+ export const udlFieldSchema = z.discriminatedUnion("type", [
283
27
  z.strictObject({
284
- amountField: udlFieldNameSchema,
285
- kind: z.literal("sum_at_least"),
286
- targetField: udlFieldNameSchema,
28
+ ...fieldBase,
29
+ type: z.literal("money"),
30
+ value: amount.optional(),
31
+ minimum: amount.optional(),
32
+ maximum: amount.optional(),
287
33
  }),
288
34
  z.strictObject({
289
- amountField: udlFieldNameSchema,
290
- kind: z.literal("sum_below"),
291
- targetField: udlFieldNameSchema,
35
+ ...fieldBase,
36
+ type: z.literal("account"),
37
+ owner: name,
38
+ key: name.optional(),
39
+ book: z.enum(["cash", "claim"]).default("cash"),
40
+ contra: z.literal(true).optional(),
41
+ external: z.literal(true).optional(),
292
42
  }),
293
43
  z.strictObject({
294
- amountField: udlFieldNameSchema,
295
- kind: z.literal("sum_exactly"),
296
- targetField: udlFieldNameSchema,
44
+ ...fieldBase,
45
+ type: z.literal("ref"),
46
+ target: z.union([instrumentId, z.array(instrumentId).min(1).max(16)]),
297
47
  }),
298
- // Siblings summed against a cap read from the LOCKED anchor row's
299
- // machinery-seeded ref (never a stored field on the counted rows).
300
48
  z.strictObject({
301
- amountField: udlFieldNameSchema,
302
- anchorRef: udlFieldNameSchema,
303
- kind: z.literal("sum_within_anchor_ref"),
49
+ ...fieldBase,
50
+ type: z.literal("date"),
51
+ value: z.iso.datetime({ offset: true }).optional(),
304
52
  }),
305
53
  z.strictObject({
306
- kind: z.literal("count_at_least"),
307
- targetField: udlFieldNameSchema,
54
+ ...fieldBase,
55
+ type: z.literal("duration"),
56
+ value: integer.positive().optional(),
308
57
  }),
309
58
  z.strictObject({
310
- kind: z.literal("count_exactly"),
311
- value: z.number().int().min(0),
59
+ ...fieldBase,
60
+ type: z.literal("text"),
61
+ value: text.optional(),
62
+ maxLength: integer.positive().max(2048).optional(),
312
63
  }),
313
64
  z.strictObject({
314
- kind: z.literal("ordered"),
315
- field: udlFieldNameSchema,
316
- positionField: udlFieldNameSchema,
65
+ ...fieldBase,
66
+ type: z.literal("integer"),
67
+ value: integer.optional(),
68
+ minimum: integer.optional(),
69
+ maximum: integer.optional(),
317
70
  }),
318
71
  z.strictObject({
319
- kind: z.literal("schedule"),
320
- positionField: udlFieldNameSchema,
321
- dateField: udlFieldNameSchema,
322
- datesField: udlFieldNameSchema,
323
- amounts: z
324
- .array(z.strictObject({
325
- amountField: udlFieldNameSchema,
326
- totalField: udlFieldNameSchema,
327
- }))
328
- .min(1)
329
- .max(8),
330
- remainder: z.literal("first"),
72
+ ...fieldBase,
73
+ type: z.literal("percent"),
74
+ value: integer.min(0).max(10000).optional(),
331
75
  }),
332
- z.strictObject({ kind: z.literal("all_in") }),
333
76
  z.strictObject({
334
- field: udlFieldNameSchema,
335
- kind: z.literal("count_equals_field"),
77
+ ...fieldBase,
78
+ type: z.literal("boolean"),
79
+ value: z.boolean().optional(),
80
+ }),
81
+ z.strictObject({
82
+ ...fieldBase,
83
+ type: z.literal("enum"),
84
+ values: z.array(name).min(1).max(64),
85
+ value: name.optional(),
86
+ }),
87
+ z.strictObject({
88
+ ...fieldBase,
89
+ type: z.literal("list"),
90
+ item: z.enum(["money", "date", "text", "integer", "ref"]),
91
+ target: instrumentId.optional(),
92
+ maxItems: integer.min(1).max(366),
336
93
  }),
337
94
  ]);
338
- const udlAggregateConditionSchema = z.strictObject({
339
- anchorField: udlFieldNameSchema.optional(),
340
- dueBefore: z
341
- .strictObject({
342
- field: udlFieldNameSchema,
343
- offset: udlOffsetSchema.optional(),
344
- })
95
+ /** Values are constants or resolved typed paths, never executable strings. */
96
+ export const udlValueSchema = z.union([
97
+ z.strictObject({ literal: scalar }),
98
+ z.strictObject({ field: path }),
99
+ ]);
100
+ const value = udlValueSchema;
101
+ const values = z.array(value).min(1).max(256);
102
+ const selection = z.strictObject({
103
+ instrument: z.union([instrumentId, z.array(instrumentId).min(1).max(16)]),
104
+ reference: name,
105
+ anchor: path,
106
+ states: z.array(name).min(1).max(64),
107
+ limit: integer.min(1).max(366),
108
+ order: z.array(path).min(1).max(4).optional(),
109
+ window: z
110
+ .strictObject({ field: name, milliseconds: integer.positive() })
111
+ .optional(),
112
+ where: z
113
+ .record(path, value)
114
+ .refine((filters) => Object.keys(filters).length <= 8, "at most eight selection filters")
115
+ .meta({ maxProperties: 8 })
345
116
  .optional(),
346
- check: udlAggregateCheckSchema,
347
- instrumentId: udlInstrumentIdSchema.optional(),
348
- over: z.enum(["children", "siblings"]),
349
- refField: udlFieldNameSchema,
350
- statuses: z.array(udlSnakeCaseSchema).min(1),
351
- });
352
- const udlExposureRequirementSchema = z.strictObject({
353
- groupField: udlFieldNameSchema.optional(),
354
- minimumField: udlFieldNameSchema.optional(),
355
- measure: z.strictObject({ allocation: udlSnakeCaseSchema }).optional(),
356
- amountField: udlFieldNameSchema,
357
- anchorField: udlFieldNameSchema,
358
- capField: udlFieldNameSchema,
359
- capOnAnchor: z.literal(true).optional(),
360
- childInstrumentId: udlInstrumentIdSchema,
361
- statuses: z.array(udlSnakeCaseSchema).min(1),
362
- });
363
- // The tenant-backend decision port: the action's caller asserts the acting
364
- // party, checked at admission against the instrument's party bindings for the
365
- // allowed roles.
366
- const udlPartyRoleSchema = z.string().regex(/^[a-z][A-Za-z0-9_]*$/);
367
- const udlPortSchema = z.strictObject({
368
- allowedParties: z.array(udlPartyRoleSchema).min(1),
369
- capture: udlFieldNameSchema.optional(),
370
- });
371
- const udlPayoutSchema = z.strictObject({
372
- amount: instanceValuePathSchema,
373
- beneficiaryField: udlFieldNameSchema,
374
- beneficiaryPartyField: udlFieldNameSchema,
375
- capture: udlFieldNameSchema,
376
- currencyField: udlFieldNameSchema,
377
- sourceAccountField: udlFieldNameSchema,
378
- speed: z.literal("standard"),
379
117
  });
380
- /**
381
- * One expectation an action asserts about money it cannot see yet: how much,
382
- * in which currency, which way, and against whose provider-side row. Evidence
383
- * that lands later either matches under the declared law or the expectation
384
- * breaks: past the window the kernel raises the declared exception child, binds
385
- * the unmatched amount and the break reason onto it, and spends the due
386
- * occurrence. There is no third outcome and no silent wait.
387
- */
388
- const udlReconcileSchema = z.strictObject({
389
- /** `fields.<name>` or `refs.<name>`: the money this action says will land. */
390
- amount: instanceValuePathSchema,
391
- /** Where the matched evidence id lands in refs. */
392
- capture: udlFieldNameSchema,
393
- /** The ref naming the provider-side row the evidence must cite. */
394
- counterpartyRef: udlFieldNameSchema,
395
- currencyField: udlFieldNameSchema,
396
- direction: z.enum(["credit", "debit"]),
397
- /** Exactly one source. A reconcile never reads two ledgers at once. */
398
- evidence: z.enum(["movement", "provider_confirmation", "statement_line"]),
399
- /**
400
- * Where an expectation that never matched lands. The named child instrument
401
- * carries the break, and `maxOpen` caps how many of them one instance may
402
- * hold open at once, the same count measure the aggregate invariants use.
403
- */
404
- exception: z.strictObject({
405
- amountField: udlFieldNameSchema,
406
- childInstrumentId: udlInstrumentIdSchema,
407
- maxOpen: z.number().int().min(1).max(1_000),
408
- reasonField: udlFieldNameSchema,
409
- refField: udlFieldNameSchema,
118
+ /** A finite, ordered calculation graph. Every result is a declared field. */
119
+ export const udlCalculationSchema = z.discriminatedUnion("op", [
120
+ z.strictObject({
121
+ target: name,
122
+ op: z.literal("at"),
123
+ list: path,
124
+ position: value,
410
125
  }),
411
- /**
412
- * How a candidate becomes the match. `exact` demands equal minor units,
413
- * `tolerance` allows a bounded difference, `window` drops the amount test
414
- * for a source that nets several items into one line.
415
- */
416
- match: z.discriminatedUnion("law", [
417
- z.strictObject({ law: z.literal("exact") }),
418
- z.strictObject({
419
- dial: udlSnakeCaseSchema,
420
- law: z.literal("tolerance"),
421
- minorUnits: z.number().int().positive(),
422
- }),
423
- z.strictObject({ law: z.literal("window") }),
424
- ]),
425
- /** Fixed ISO-8601 duration from the expecting action, or a stored deadline. */
426
- within: z.union([
427
- z.strictObject({ field: udlFieldNameSchema }),
428
- z.strictObject({ offset: nonEmptyTextSchema }),
429
- ]),
430
- });
431
- const udlSignedSumSourceSchema = z.strictObject({
432
- amountField: udlFieldNameSchema,
433
- instrumentId: udlInstrumentIdSchema,
434
- refField: udlFieldNameSchema,
435
- sign: z.enum(["add", "subtract"]),
436
- statuses: z.array(udlSnakeCaseSchema).min(1),
437
- subtotalRef: udlFieldNameSchema,
438
- });
439
- const udlSignedSumSchema = z.strictObject({
440
- amountRef: udlFieldNameSchema,
441
- onNegative: z.literal("refuse"),
442
- onZero: z.enum(["refuse", "skip_steps"]),
443
- sources: z.array(udlSignedSumSourceSchema).min(1),
444
- });
445
- const udlDistributeSchema = z.strictObject({
446
- amountRef: udlFieldNameSchema,
447
- onZero: z.enum(["refuse", "skip_steps"]),
448
- pool: z.strictObject({
449
- from: z.literal("parent"),
450
- path: fieldPathSchema,
126
+ z.strictObject({
127
+ target: name,
128
+ op: z.literal("aggregate"),
129
+ selection,
130
+ measure: z.union([z.literal("count"), z.strictObject({ sum: path })]),
451
131
  }),
452
- refField: udlFieldNameSchema,
453
- statuses: z.array(udlSnakeCaseSchema).min(1),
454
- weightField: udlFieldNameSchema,
455
- });
456
- const udlRemainderCollectedSchema = z.strictObject({
457
- amountField: udlFieldNameSchema,
458
- instrumentId: udlInstrumentIdSchema,
459
- path: z.literal("refs").optional(),
460
- refField: udlFieldNameSchema,
461
- statuses: z.array(udlSnakeCaseSchema).min(1),
462
- });
463
- const udlRemainderSchema = z.strictObject({
464
- accumulateRef: udlFieldNameSchema.optional(),
465
- amountRef: udlFieldNameSchema,
466
- collected: z.array(udlRemainderCollectedSchema).optional(),
467
- inputKey: udlFieldNameSchema.optional(),
468
- onZero: z.enum(["refuse", "skip_steps"]),
469
- totalPath: instanceValuePathSchema,
470
- subtractPaths: z.array(instanceValuePathSchema).min(1).max(16).optional(),
471
- });
472
- const udlCheckRequirementVariants = udlCheckEvidenceProfiles.map((profile) => z.strictObject({
473
- checkKind: z.literal(profile.checkKind),
474
- family: z.literal(profile.family),
475
- maxAge: nonEmptyTextSchema.optional(),
476
- statuses: z.array(z.enum(profile.statuses)).min(1),
477
- subjectField: udlFieldNameSchema,
478
- }));
479
- const udlCheckRequirementSchema = z.union(udlCheckRequirementVariants);
480
- const udlDialSchema = z.discriminatedUnion("kind", [
481
132
  z.strictObject({
482
- field: udlFieldNameSchema,
483
- key: udlSnakeCaseSchema,
484
- kind: z.literal("window"),
485
- maxOffset: nonEmptyTextSchema.optional(),
486
- minOffset: nonEmptyTextSchema.optional(),
487
- summary: nonEmptyTextSchema,
488
- title: nonEmptyTextSchema,
133
+ target: name,
134
+ op: z.literal("ratio"),
135
+ amount: value,
136
+ numerator: value,
137
+ denominator: value,
138
+ rounding: z.literal("floor"),
489
139
  }),
140
+ z.strictObject({ target: name, op: z.literal("sum"), values }),
490
141
  z.strictObject({
491
- action: udlActionNameSchema,
492
- key: udlSnakeCaseSchema,
493
- kind: z.literal("decision_deadline_ms"),
494
- maxMs: z.number().int().positive().max(15_000).optional(),
495
- minMs: z.number().int().positive().max(15_000).optional(),
496
- summary: nonEmptyTextSchema,
497
- title: nonEmptyTextSchema,
142
+ target: name,
143
+ op: z.literal("subtract"),
144
+ base: value,
145
+ subtract: values,
498
146
  }),
499
147
  z.strictObject({
500
- key: udlSnakeCaseSchema,
501
- kind: z.literal("reconcile_tolerance"),
502
- /** Ceiling on the minor units any reconcile may forgive under this dial. */
503
- maxMinorUnits: z.number().int().positive().max(1_000_000),
504
- summary: nonEmptyTextSchema,
505
- title: nonEmptyTextSchema,
148
+ target: name,
149
+ op: z.literal("rate"),
150
+ base: value,
151
+ bps: value,
152
+ rounding: z.literal("floor"),
506
153
  }),
154
+ z.strictObject({ target: name, op: z.literal("minimum"), values }),
507
155
  z.strictObject({
508
- key: udlSnakeCaseSchema,
509
- kind: z.literal("unwind_penalty"),
510
- summary: nonEmptyTextSchema,
511
- title: nonEmptyTextSchema,
156
+ target: name,
157
+ op: z.literal("multiply"),
158
+ amount: value,
159
+ units: value,
512
160
  }),
513
- ]);
514
- // A same-instance conservation law: the piece fields (money, minor units)
515
- // must sum exactly to the total field; admission refuses a create whose
516
- // pieces diverge.
517
- // A composition-level dial. Instrument dials tune one instrument's frozen slots;
518
- // this one is a property of the whole product, so it hangs off the document.
519
- // `confirmation_threshold` carries minor units of the kernel currency: a
520
- // money-moving tool call whose declared money inputs sum below the configured
521
- // value skips the explicit confirmation argument.
522
- const udlCompositionDialSchema = z.strictObject({
523
- key: udlSnakeCaseSchema,
524
- kind: z.literal("confirmation_threshold"),
525
- maxMinor: z.number().int().positive().max(1_000_000_000_000).optional(),
526
- summary: nonEmptyTextSchema,
527
- title: nonEmptyTextSchema,
528
- });
529
- const udlPartitionSchema = z.strictObject({
530
- pieceFields: z.array(udlFieldNameSchema).min(2),
531
- totalField: udlFieldNameSchema,
532
- });
533
- const udlDerivedAmountSchema = z.strictObject({
534
- field: udlFieldNameSchema,
535
- rounding: z.literal("floor"),
536
- rule: z.discriminatedUnion("kind", [
537
- z.strictObject({
538
- bps: z.union([z.number().int().min(1).max(9_999), udlStoredFieldSchema]),
539
- kind: z.literal("percentage_of"),
540
- }),
541
- z.strictObject({
542
- kind: z.literal("minimum"),
543
- capField: udlFieldNameSchema,
544
- }),
545
- ]),
546
- sourceField: udlFieldNameSchema,
547
- });
548
- const udlFeeLeafRuleSchema = z.discriminatedUnion("kind", [
549
161
  z.strictObject({
550
- bps: z.number().int().min(1).max(9_999),
551
- kind: z.literal("bps"),
162
+ target: name,
163
+ op: z.literal("divide"),
164
+ amount: value,
165
+ divisor: value,
166
+ rounding: z.literal("floor"),
552
167
  }),
553
168
  z.strictObject({
554
- currencyField: udlFieldNameSchema,
555
- field: udlFieldNameSchema,
556
- kind: z.literal("exact"),
169
+ target: name,
170
+ op: z.literal("shift"),
171
+ date: value,
172
+ milliseconds: value,
173
+ direction: z.enum(["before", "after"]),
557
174
  }),
558
175
  ]);
559
- const nonNegativeDecimalIntegerSchema = z
560
- .string()
561
- .regex(/^(0|[1-9][0-9]*)$/, "must be a nonnegative decimal integer");
562
- const udlFeeRuleSchema = z.strictObject({
563
- amountField: udlFieldNameSchema,
564
- baseField: udlFieldNameSchema,
565
- bearerField: udlFieldNameSchema,
566
- position: z.enum(["carved", "on_top"]),
567
- rule: z.discriminatedUnion("kind", [
568
- ...udlFeeLeafRuleSchema.options,
569
- z.strictObject({
570
- kind: z.literal("tiered"),
571
- tiers: z
572
- .array(z.strictObject({
573
- fromInclusive: nonNegativeDecimalIntegerSchema,
574
- rule: udlFeeLeafRuleSchema,
575
- toExclusive: nonNegativeDecimalIntegerSchema.optional(),
576
- }))
577
- .min(1)
578
- .max(16),
579
- }),
580
- ]),
581
- });
582
- const udlDecidedAmountSchema = z.strictObject({
583
- boundField: udlFieldNameSchema,
584
- field: udlFieldNameSchema,
585
- remainderAction: udlActionNameSchema,
586
- });
587
- const udlEffectRowSchema = z.strictObject({
588
- signature: nonEmptyTextSchema,
589
- source: nonEmptyTextSchema,
590
- });
591
- const udlNotifyEffectSchema = z.strictObject({
592
- channel: udlSnakeCaseSchema,
593
- role: udlSnakeCaseSchema,
594
- signature: nonEmptyTextSchema,
595
- source: nonEmptyTextSchema,
596
- });
597
- const udlEffectsShape = {
598
- decides: z.array(udlEffectRowSchema).min(1).optional(),
599
- holds: z.array(udlEffectRowSchema).min(1).optional(),
600
- moves: z.array(udlEffectRowSchema).min(1).optional(),
601
- notifies: z.array(udlNotifyEffectSchema).min(1).optional(),
602
- reads: z.array(udlEffectRowSchema).min(1).optional(),
603
- schedules: z.array(udlEffectRowSchema).min(1).optional(),
604
- };
605
- const udlEffectsSchema = z.strictObject(udlEffectsShape);
606
- export const udlPieceSchema = z.strictObject({
607
- amount: udlFieldNameSchema,
608
- id: udlSnakeCaseSchema,
609
- refund_to: udlFieldNameSchema,
610
- release_to: udlFieldNameSchema,
611
- });
612
- export const udlPiecePlanSchema = z.strictObject({
613
- fund_order: z.array(udlSnakeCaseSchema),
614
- id: udlSnakeCaseSchema,
615
- pieces: z.array(udlPieceSchema).min(1).max(256),
616
- refund_order: z.array(udlSnakeCaseSchema),
617
- release_order: z.array(udlSnakeCaseSchema),
618
- total: udlFieldNameSchema,
619
- unfund_order: z.array(udlSnakeCaseSchema),
620
- });
621
- export const udlPieceStageStageSchema = z.enum([
622
- "fund",
623
- "release",
624
- "refund",
625
- "unfund",
626
- ]);
627
- export const udlPieceStageSchema = z.strictObject({
628
- plan: udlSnakeCaseSchema,
629
- stage: udlPieceStageStageSchema,
630
- });
631
- export const udlCallSchema = z.strictObject({
632
- action: z
633
- .string()
634
- .regex(/^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$/, "must name an action as library_id.action_id"),
635
- bind: z.record(udlFieldNameSchema, z.string()),
636
- id: udlSnakeCaseSchema,
637
- });
638
- export const udlPrivateParameterKindSchema = z.enum([
639
- "account",
640
- "instance",
641
- "money",
642
- "piece",
643
- "text",
644
- ]);
645
- export const udlPrivateParameterSchema = z.strictObject({
646
- currency: z.string().optional(),
647
- kind: udlPrivateParameterKindSchema,
648
- });
649
- export const udlPrivateLeafSchema = z.strictObject({
650
- bind: z.record(fieldPathSchema, z.string()),
651
- capture: stringMapSchema.optional(),
652
- effects: z
653
- .array(z.strictObject({
654
- kind: z.enum(udlEffectKinds),
655
- signature: nonEmptyTextSchema,
656
- }))
657
- .min(1, "must declare at least one effect"),
658
- evidence: nonEmptyTextSchema,
659
- id: udlSnakeCaseSchema,
660
- operation: udlKernelOperationSchema,
661
- });
662
- export const udlPrivateActionSchema = z.strictObject({
663
- approval: z.enum(["inherit", "independent"]),
664
- calls: z.array(udlCallSchema),
665
- leaves: z.array(udlPrivateLeafSchema),
666
- order: z.array(udlSnakeCaseSchema),
667
- parameters: z.record(udlFieldNameSchema, udlPrivateParameterSchema),
668
- principal: z.enum(["api_key", "user_session"]),
669
- recovery: z.enum(["local", "external"]),
670
- });
671
- export const udlActionLibraryModuleSchema = z.strictObject({
672
- actionOrder: z.array(udlSnakeCaseSchema),
673
- actions: z.record(udlSnakeCaseSchema, udlPrivateActionSchema),
674
- });
675
- export const udlActionLibrarySchema = z.record(udlSnakeCaseSchema, udlActionLibraryModuleSchema);
676
- const templateParametersSchema = z.record(udlFieldNameSchema, z.union([z.string(), z.number().int(), z.boolean()]));
677
- const udlAllocationBucketSchema = z.strictObject({
678
- key: z.enum(["principal", "profit", "cost", "fine"]),
679
- source: z.union([
680
- z.strictObject({
681
- from: z.literal("slice"),
682
- amountField: udlFieldNameSchema,
683
- destinationField: udlFieldNameSchema,
684
- }),
685
- z.strictObject({
686
- from: z.literal("children"),
687
- instrumentId: udlInstrumentIdSchema,
688
- refField: udlFieldNameSchema,
689
- statuses: z.array(udlSnakeCaseSchema).min(1),
690
- amountField: udlFieldNameSchema,
691
- destinationField: udlFieldNameSchema,
692
- }),
693
- z.strictObject({
694
- from: z.literal("children"),
695
- template: udlSnakeCaseSchema,
696
- parameters: templateParametersSchema,
697
- refField: udlFieldNameSchema,
698
- statuses: z.array(udlSnakeCaseSchema).min(1),
699
- amountField: udlFieldNameSchema,
700
- destinationField: udlFieldNameSchema,
701
- }),
702
- ]),
703
- });
704
- const udlAllocationSchema = z.strictObject({
705
- sliceInstrumentId: udlInstrumentIdSchema,
706
- sliceRefField: udlFieldNameSchema,
707
- sliceStatuses: z.array(udlSnakeCaseSchema).min(1),
708
- dueField: udlFieldNameSchema,
709
- positionField: udlFieldNameSchema,
710
- earningRuleField: udlFieldNameSchema,
711
- buckets: z.array(udlAllocationBucketSchema).min(2).max(4),
712
- });
713
- const udlAllocateSchema = z.union([
176
+ export const udlRequirementSchema = z.discriminatedUnion("kind", [
714
177
  z.strictObject({
715
- refField: udlFieldNameSchema.optional(),
716
- mode: z.literal("refund"),
717
- action: udlActionNameSchema.optional(),
718
- assessmentField: udlFieldNameSchema.optional(),
719
- capture: udlFieldNameSchema,
178
+ kind: z.literal("hours"),
179
+ at: path,
180
+ start: integer.min(0).max(23),
181
+ end: integer.min(0).max(23),
182
+ timezone: text,
720
183
  }),
721
184
  z.strictObject({
722
- refField: udlFieldNameSchema,
723
- mode: z.literal("payment"),
724
- assessment: z.literal("self").optional(),
725
- amountField: udlFieldNameSchema.optional(),
726
- sourceAccountField: udlFieldNameSchema,
727
- paymentIdentityField: udlFieldNameSchema,
728
- capture: udlFieldNameSchema,
185
+ kind: z.literal("compare"),
186
+ left: value,
187
+ operator: z.enum(["==", "!=", "<", "<=", ">", ">="]),
188
+ right: value,
729
189
  }),
730
190
  z.strictObject({
731
- refField: udlFieldNameSchema.optional(),
732
- mode: z.literal("payoff"),
733
- amountField: udlFieldNameSchema.optional(),
734
- sourceAccountField: udlFieldNameSchema,
735
- paymentIdentityField: udlFieldNameSchema,
736
- capture: udlFieldNameSchema,
191
+ kind: z.literal("state"),
192
+ reference: path,
193
+ states: z.array(name).min(1).max(64),
737
194
  }),
738
195
  z.strictObject({
739
- refField: udlFieldNameSchema.optional(),
740
- mode: z.literal("write_off"),
741
- capture: udlFieldNameSchema,
196
+ kind: z.literal("unique"),
197
+ namespace: name,
198
+ fields: z.array(path).min(1).max(8),
199
+ }),
200
+ z.strictObject({
201
+ kind: z.literal("aggregate"),
202
+ selection,
203
+ measure: z.union([z.literal("count"), z.strictObject({ sum: path })]),
204
+ operator: z.enum(["==", "<=", ">=", "<", ">"]),
205
+ value,
742
206
  }),
743
- ]);
744
- const udlContributionsSchema = z.strictObject({
745
- field: udlFieldNameSchema,
746
- amountKey: udlFieldNameSchema,
747
- accountKey: udlFieldNameSchema,
748
- totalField: udlFieldNameSchema,
749
- });
750
- // These clauses are opt-in. Absent clauses never add defaults to older documents.
751
- const udlFundingSchema = z.strictObject({
752
- obligationField: udlFieldNameSchema,
753
- ticketInstrumentId: udlInstrumentIdSchema,
754
- ticketRefField: udlFieldNameSchema,
755
- ticketAmountField: udlFieldNameSchema,
756
- ticketInvestorField: udlFieldNameSchema,
757
- ticketAccountField: udlFieldNameSchema,
758
- ticketStatus: udlSnakeCaseSchema,
759
- collectAction: udlActionNameSchema,
760
- principalField: udlFieldNameSchema,
761
- sourceAccountPath: instanceValuePathSchema,
762
- destinationAccountField: udlFieldNameSchema,
763
- terms: z.record(udlFieldNameSchema, udlFieldNameSchema),
764
- capture: udlFieldNameSchema,
765
- });
766
- const udlReceiptDistributionSchema = z.strictObject({
767
- roundField: udlFieldNameSchema,
768
- snapshotRef: udlFieldNameSchema,
769
- receiptField: udlFieldNameSchema,
770
- receiptPath: instanceValuePathSchema,
771
- mode: z.enum(["cash", "loss"]),
772
- feeBps: z.number().int().min(0).max(10000),
773
- vatBps: z.number().int().min(0).max(10000),
774
- feeAccountField: udlFieldNameSchema,
775
- taxAccountField: udlFieldNameSchema,
776
- residualAccountField: udlFieldNameSchema,
777
- capture: udlFieldNameSchema,
778
- });
779
- const udlRequestAuthoritySchema = z.strictObject({
780
- instrumentField: udlFieldNameSchema,
781
- instanceField: udlFieldNameSchema,
782
- actionField: udlFieldNameSchema,
783
- inputField: udlFieldNameSchema,
784
- digestField: udlFieldNameSchema,
785
- roleField: udlFieldNameSchema,
786
- party: udlPartyRoleSchema,
787
- expiresField: udlFieldNameSchema,
788
- });
789
- const udlCascadeEntrySchema = z.union([
790
207
  z.strictObject({
791
- instrumentId: udlInstrumentIdSchema,
792
- action: udlActionNameSchema,
793
- inputField: udlFieldNameSchema,
208
+ kind: z.literal("approval"),
209
+ target: path.default("self"),
210
+ party: name,
211
+ action: name.optional(),
212
+ decision: z.enum(["approved", "declined"]),
794
213
  }),
795
214
  z.strictObject({
796
- instrumentId: udlInstrumentIdSchema,
797
- action: udlActionNameSchema,
798
- refField: udlFieldNameSchema,
799
- statuses: z.array(udlSnakeCaseSchema).min(1),
215
+ kind: z.literal("evidence"),
216
+ subject: path,
217
+ family: name,
218
+ check: name,
219
+ result: name,
220
+ maxAge: integer.positive(),
800
221
  }),
801
222
  ]);
802
- const udlActionShape = {
803
- requestAuthority: udlRequestAuthoritySchema.optional(),
804
- funding: udlFundingSchema.optional(),
805
- receiptDistribution: udlReceiptDistributionSchema.optional(),
806
- requiresAllocation: z
807
- .strictObject({
808
- refField: udlFieldNameSchema.optional(),
809
- slice: z.union([z.literal("self"), udlStoredFieldSchema]),
810
- buckets: z
811
- .array(z.enum(["principal", "profit", "cost", "fine"]))
812
- .min(1)
813
- .max(4),
814
- check: z.enum(["settled", "outstanding"]),
815
- })
816
- .optional(),
817
- unique: z
818
- .strictObject({
819
- namespace: udlSnakeCaseSchema,
820
- byFields: z.array(udlFieldNameSchema).min(1).max(8),
821
- })
822
- .optional(),
823
- requiresInput: z
824
- .record(udlFieldNameSchema, instanceValuePathSchema)
825
- .optional(),
826
- engineOwned: z.literal(true).optional(),
827
- captureEngine: z
828
- .record(udlFieldNameSchema, z.enum(["operationId"]))
829
- .optional(),
830
- allocate: udlAllocateSchema.optional(),
831
- contributionStage: z
832
- .strictObject({
833
- stage: z.enum(["fund", "refund"]),
834
- accountPath: instanceValuePathSchema,
835
- })
836
- .optional(),
837
- transitionsRefs: z
838
- .array(z.strictObject({
839
- field: udlFieldNameSchema,
840
- action: udlActionNameSchema,
841
- }))
842
- .min(1)
843
- .max(16)
844
- .optional(),
845
- cascade: z.array(udlCascadeEntrySchema).min(1).max(8).optional(),
846
- agentDescription: udlAgentDescriptionSchema.optional(),
847
- calls: z.array(udlCallSchema).min(1).optional(),
848
- captureInput: z.record(udlFieldNameSchema, udlFieldNameSchema).optional(),
849
- /** Names the quoting action whose offer this action spends. */
850
- commit: udlActionNameSchema.optional(),
851
- decidedAmount: udlDecidedAmountSchema.optional(),
852
- deadline: udlDeadlineSchema.optional(),
853
- decision: udlDecisionSchema.optional(),
854
- distribute: udlDistributeSchema.optional(),
855
- description: nonEmptyTextSchema.optional(),
856
- due: udlDueSchema.optional(),
857
- earnable: z.boolean().optional(),
858
- effects: udlEffectsSchema.optional(),
859
- eventName: z
860
- .string()
861
- .regex(eventNamePattern)
862
- .meta({ "x-udl-reference": "instrument_event" })
863
- .optional(),
864
- examples: z.array(udlExampleSchema).min(1).optional(),
865
- input: jsonObjectSchema.optional(),
866
- moves: z.array(udlMoveSchema).default([]),
867
- payout: udlPayoutSchema.optional(),
868
- pieceStage: udlPieceStageSchema.optional(),
869
- port: udlPortSchema.optional(),
870
- quote: udlQuoteSchema.optional(),
871
- publicAction: udlPublicActionSchema
872
- .optional()
873
- .describe("Author-approved public action name; the containing action key remains the lifecycle and execution identity"),
874
- principal: z.literal("user_session").optional(),
875
- reconcile: z.array(udlReconcileSchema).min(1).optional(),
876
- remainder: udlRemainderSchema.optional(),
877
- requiresAggregate: z.array(udlAggregateConditionSchema).min(1).optional(),
878
- requiresChecks: z.array(udlCheckRequirementSchema).optional(),
879
- requiresDrainedAccount: z.strictObject({ path: fieldPathSchema }).optional(),
880
- requiresExposure: z.array(udlExposureRequirementSchema).min(1).optional(),
881
- requiresRefs: z.array(udlGateSchema).min(1).optional(),
882
- sandboxFailurePoint: z.enum(["funding", "release"]).optional(),
883
- setsAt: udlSetsAtSchema.optional(),
884
- signedSum: udlSignedSumSchema.optional(),
885
- steps: z.array(udlStepSchema).default([]),
886
- summary: nonEmptyTextSchema,
887
- updates: z.array(udlFieldNameSchema).min(1).optional(),
888
- };
889
- const udlActionSchema = z.strictObject(udlActionShape);
890
- const udlAggregateBaseShape = {
891
- childInstrumentId: udlInstrumentIdSchema,
892
- childRefField: udlFieldNameSchema,
893
- childStatuses: z.array(udlSnakeCaseSchema).min(1),
894
- parentField: udlFieldNameSchema,
895
- };
896
- // Two measures, mutually exclusive by strictness: a sum cap over a child money
897
- // field, or a count cap (optionally windowed to the rolling `days` ending at
898
- // the child's own as-written date-time value).
899
- const udlAggregateSchema = z.union([
223
+ export const udlKernelOperationSchema = z.enum([
224
+ "internal_transfer.create",
225
+ "internal_transfer.reserve",
226
+ "internal_transfer.post",
227
+ "internal_transfer.void",
228
+ ]);
229
+ export const udlMoveSchema = z.discriminatedUnion("operation", [
230
+ z.strictObject({
231
+ key: name,
232
+ operation: z.literal("internal_transfer.create"),
233
+ capture: name.optional(),
234
+ amount: value,
235
+ from: path,
236
+ to: path,
237
+ }),
238
+ z.strictObject({
239
+ key: name,
240
+ operation: z.literal("internal_transfer.reserve"),
241
+ amount: value,
242
+ from: path,
243
+ to: path,
244
+ capture: name,
245
+ }),
900
246
  z.strictObject({
901
- ...udlAggregateBaseShape,
902
- childField: udlFieldNameSchema,
247
+ key: name,
248
+ operation: z.literal("internal_transfer.post"),
249
+ capture: name.optional(),
250
+ transfer: path,
903
251
  }),
904
252
  z.strictObject({
905
- ...udlAggregateBaseShape,
906
- count: z.literal(true),
907
- window: z
908
- .strictObject({
909
- field: udlFieldNameSchema,
910
- days: z.number().int().min(1).max(3650),
911
- })
912
- .optional(),
253
+ key: name,
254
+ operation: z.literal("internal_transfer.void"),
255
+ capture: name.optional(),
256
+ transfer: path,
913
257
  }),
914
258
  ]);
915
- const udlInstrumentShape = {
916
- templateBinding: z
259
+ const clock = z.strictObject({ at: path, offset: integer.optional() });
260
+ export const udlActionSchema = z.strictObject({
261
+ summary: text,
262
+ publicAction: name.optional(),
263
+ event: text,
264
+ actor: z.union([
265
+ z.literal("caller"),
266
+ z.literal("clock"),
267
+ z.strictObject({ party: name }),
268
+ z.strictObject({ parent: instrumentId }),
269
+ ]),
270
+ input: z.array(udlFieldSchema).max(128),
271
+ requires: z.array(udlRequirementSchema).max(128),
272
+ due: clock.optional(),
273
+ deadline: clock.optional(),
274
+ set: z.record(name, value).optional(),
275
+ calculate: z.array(udlCalculationSchema).max(128).optional(),
276
+ moves: z.array(udlMoveSchema).max(256),
277
+ invoke: z
278
+ .array(z.union([
279
+ z.strictObject({
280
+ instrument: instrumentId,
281
+ action: z.literal("create"),
282
+ input: z.record(name, value),
283
+ }),
284
+ z.strictObject({
285
+ reference: path,
286
+ action: name,
287
+ input: z.record(name, value),
288
+ }),
289
+ z.strictObject({
290
+ selection,
291
+ action: name,
292
+ input: z.record(name, value),
293
+ }),
294
+ ]))
295
+ .max(16)
296
+ .optional(),
297
+ approval: z
917
298
  .strictObject({
918
- id: udlSnakeCaseSchema,
919
- parameters: templateParametersSchema,
299
+ target: path,
300
+ action: name,
301
+ party: name,
302
+ expires: path,
303
+ input: z.record(name, value),
304
+ decision: z.enum(["approved", "declined"]),
920
305
  })
921
306
  .optional(),
922
- allocation: udlAllocationSchema.optional(),
923
- contributions: udlContributionsSchema.optional(),
924
- dateOrder: z
925
- .array(z.strictObject({
926
- beforeField: udlFieldNameSchema,
927
- afterField: udlFieldNameSchema,
928
- operator: z.enum(["<", "<="]),
929
- }))
930
- .min(1)
307
+ });
308
+ export const udlLifecycleSchema = z.strictObject({
309
+ states: z.array(name).min(1).max(64),
310
+ initial: name,
311
+ transitions: z.record(name, z.strictObject({ from: z.array(name).min(1).max(64), to: name })),
312
+ });
313
+ export const udlInstrumentSchema = z.strictObject({
314
+ id: instrumentId,
315
+ title: text,
316
+ summary: text,
317
+ fields: z.array(udlFieldSchema).max(256),
318
+ calculate: z.array(udlCalculationSchema).max(128),
319
+ lifecycle: udlLifecycleSchema,
320
+ actions: z.record(name, udlActionSchema),
321
+ actionOrder: z.array(name).min(1).max(128),
322
+ invariants: z.array(udlRequirementSchema).max(128).optional(),
323
+ examples: z
324
+ .array(z.strictObject({ name: text, input: z.json() }))
931
325
  .max(16)
932
326
  .optional(),
933
- actionLibrary: udlActionLibrarySchema.optional(),
934
- actionOrder: z.array(udlActionNameSchema).min(1),
935
- agentDescription: udlAgentDescriptionSchema.optional(),
936
- aggregateInvariants: z.array(udlAggregateSchema).min(1).optional(),
937
- callerParkedStates: z
938
- .record(udlSnakeCaseSchema, nonEmptyTextSchema)
939
- .optional(),
940
- description: nonEmptyTextSchema.optional(),
941
- dials: z.array(udlDialSchema).optional(),
942
- distinctParties: z.literal(true).optional(),
943
- derivedAmounts: z.array(udlDerivedAmountSchema).min(1).max(64).optional(),
944
- feeRules: z.array(udlFeeRuleSchema).min(1).max(4).optional(),
945
- fields: z.record(udlFieldNameSchema, jsonObjectSchema),
946
- id: udlInstrumentIdSchema,
947
- idPrefix: z
948
- .string()
949
- .regex(idPrefixPattern, "must contain 2 to 8 lowercase letters"),
950
- lifecycle: udlLifecycleSchema,
951
- nav: z.array(nonEmptyTextSchema).min(1).optional(),
952
- parties: z.record(udlPartyRoleSchema, udlFieldNameSchema).optional(),
953
- partitions: z.array(udlPartitionSchema).min(1).optional(),
954
- piecePlan: udlPiecePlanSchema.optional(),
955
- required: z.array(udlFieldNameSchema),
956
- subject: udlInstrumentSubjectSchema.optional(),
957
- summary: nonEmptyTextSchema,
958
- surfaceVisibility: z.enum(["internal", "public", "system"]).optional(),
959
- templateId: udlSnakeCaseSchema.optional(),
960
- title: nonEmptyTextSchema,
961
- update: udlUpdatePolicySchema.optional(),
962
- actions: z.record(udlActionNameSchema, udlActionSchema),
963
- };
964
- const udlInstrumentSchema = z.strictObject(udlInstrumentShape);
965
- export const udlClauseVocabulary = [
966
- {
967
- scope: "action",
968
- spelling: "funding",
969
- target: "funding",
970
- cardinality: "one",
971
- effects: [
972
- {
973
- kind: "moves",
974
- per: "clause",
975
- signature: { fixed: "transfer.internal" },
976
- },
977
- ],
978
- },
979
- {
980
- scope: "action",
981
- spelling: "receipt distribution",
982
- target: "receiptDistribution",
983
- cardinality: "one",
984
- effects: [
985
- { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
986
- { kind: "moves", per: "clause", signature: { fixed: "allocation" } },
987
- ],
988
- },
989
- {
990
- cardinality: "one",
991
- scope: "action",
992
- spelling: "requires allocation",
993
- target: "requiresAllocation",
994
- effects: [
995
- { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
996
- ],
997
- },
998
- {
999
- cardinality: "one",
1000
- scope: "instrument",
1001
- spelling: "allocation",
1002
- target: "allocation",
1003
- },
1004
- {
1005
- cardinality: "one",
1006
- scope: "instrument",
1007
- spelling: "contributions",
1008
- target: "contributions",
1009
- },
1010
- {
1011
- cardinality: "one",
1012
- scope: "action",
1013
- spelling: "allocate",
1014
- target: "allocate",
1015
- effects: [
1016
- { kind: "decides", per: "clause", signature: { fixed: "allocation" } },
1017
- { kind: "moves", per: "clause", signature: { fixed: "allocation" } },
1018
- ],
1019
- },
1020
- {
1021
- cardinality: "one",
1022
- scope: "action",
1023
- spelling: "contribution stage",
1024
- target: "contributionStage",
1025
- effects: [
1026
- {
1027
- kind: "moves",
1028
- per: "clause",
1029
- signature: { fixed: "transfer.internal" },
1030
- },
1031
- ],
1032
- },
1033
- {
1034
- cardinality: "many",
1035
- scope: "instrument",
1036
- spelling: "date order",
1037
- target: "dateOrder",
1038
- },
1039
- {
1040
- cardinality: "one",
1041
- scope: "action",
1042
- spelling: "unique",
1043
- target: "unique",
1044
- effects: [
1045
- {
1046
- kind: "decides",
1047
- per: "clause",
1048
- signature: { fixed: "subject_unique" },
1049
- },
1050
- ],
1051
- },
1052
- {
1053
- cardinality: "many",
1054
- scope: "action",
1055
- spelling: "transitions refs",
1056
- target: "transitionsRefs",
1057
- effects: [
1058
- {
1059
- kind: "decides",
1060
- per: "element",
1061
- signature: { fixed: "referenced_transition" },
1062
- },
1063
- ],
1064
- },
1065
- {
1066
- cardinality: "many",
1067
- scope: "action",
1068
- spelling: "cascade",
1069
- target: "cascade",
1070
- effects: [
1071
- {
1072
- kind: "decides",
1073
- per: "element",
1074
- signature: { fixed: "cascade_transition" },
1075
- },
1076
- ],
1077
- },
1078
- {
1079
- cardinality: "one",
1080
- literalKeys: true,
1081
- scope: "instrument",
1082
- spelling: "action library",
1083
- target: "actionLibrary",
1084
- },
1085
- {
1086
- cardinality: "one",
1087
- scope: "action",
1088
- spelling: "agent description",
1089
- target: "agentDescription",
1090
- },
1091
- {
1092
- cardinality: "many",
1093
- literalKeys: true,
1094
- scope: "action",
1095
- spelling: "calls",
1096
- target: "calls",
1097
- },
1098
- {
1099
- cardinality: "one",
1100
- scope: "action",
1101
- spelling: "requires input",
1102
- target: "requiresInput",
1103
- literalKeys: true,
1104
- },
1105
- {
1106
- cardinality: "one",
1107
- scope: "action",
1108
- spelling: "engine owned",
1109
- target: "engineOwned",
1110
- },
1111
- {
1112
- cardinality: "one",
1113
- scope: "action",
1114
- spelling: "capture engine",
1115
- target: "captureEngine",
1116
- literalKeys: true,
1117
- },
1118
- {
1119
- cardinality: "one",
1120
- scope: "action",
1121
- spelling: "capture input",
1122
- target: "captureInput",
1123
- },
1124
- {
1125
- cardinality: "one",
1126
- // The committing action consumes the quote through its priced moves.
1127
- // An extra row here would bill the same movement twice.
1128
- effects: [],
1129
- scope: "action",
1130
- spelling: "commit",
1131
- target: "commit",
1132
- },
1133
- {
1134
- cardinality: "one",
1135
- effects: [
1136
- {
1137
- kind: "holds",
1138
- per: "clause",
1139
- signature: { fixed: "quote" },
1140
- },
1141
- {
1142
- kind: "schedules",
1143
- per: "clause",
1144
- signature: { fixed: "expiry" },
1145
- },
1146
- ],
1147
- linearOutputs: ["netRef"],
1148
- scope: "action",
1149
- spelling: "quote",
1150
- target: "quote",
1151
- },
1152
- {
1153
- cardinality: "one",
1154
- linearOutputs: ["field"],
1155
- scope: "action",
1156
- spelling: "decided amount",
1157
- target: "decidedAmount",
1158
- },
1159
- {
1160
- cardinality: "one",
1161
- effects: [
1162
- {
1163
- kind: "schedules",
1164
- per: "clause",
1165
- signature: { fixed: "deadline" },
1166
- },
1167
- ],
1168
- scope: "action",
1169
- spelling: "deadline",
1170
- target: "deadline",
1171
- },
1172
- {
1173
- cardinality: "one",
1174
- effects: [
1175
- {
1176
- kind: "decides",
1177
- per: "clause",
1178
- signature: { fromField: "capability" },
1179
- },
1180
- ],
1181
- scope: "action",
1182
- spelling: "decision",
1183
- target: "decision",
1184
- },
1185
- {
1186
- cardinality: "one",
1187
- linearOutputs: ["amountRef"],
1188
- scope: "action",
1189
- spelling: "computes distribute",
1190
- target: "distribute",
1191
- },
1192
- {
1193
- cardinality: "one",
1194
- scope: "action",
1195
- spelling: "description",
1196
- target: "description",
1197
- },
1198
- {
1199
- cardinality: "one",
1200
- effects: [
1201
- {
1202
- kind: "schedules",
1203
- per: "clause",
1204
- signature: { fixed: "due" },
1205
- },
1206
- ],
1207
- scope: "action",
1208
- spelling: "due",
1209
- target: "due",
1210
- },
1211
- {
1212
- cardinality: "one",
1213
- scope: "action",
1214
- spelling: "earnable",
1215
- target: "earnable",
1216
- },
1217
- {
1218
- cardinality: "one",
1219
- scope: "action",
1220
- spelling: "event name",
1221
- target: "eventName",
1222
- },
1223
- {
1224
- cardinality: "many",
1225
- scope: "action",
1226
- spelling: "examples",
1227
- target: "examples",
1228
- },
1229
- {
1230
- cardinality: "one",
1231
- scope: "action",
1232
- spelling: "input",
1233
- target: "input",
1234
- },
1235
- {
1236
- cardinality: "many",
1237
- effects: [
1238
- {
1239
- kind: "moves",
1240
- per: "element",
1241
- signature: { movementClass: true },
1242
- },
1243
- {
1244
- kind: "holds",
1245
- per: "element",
1246
- signature: { fixed: "reserve" },
1247
- },
1248
- ],
1249
- linearSink: true,
1250
- scope: "action",
1251
- spelling: "moves",
1252
- target: "moves",
1253
- },
1254
- {
1255
- cardinality: "one",
1256
- effects: [
1257
- {
1258
- kind: "moves",
1259
- per: "clause",
1260
- signature: { fixed: "payout.external" },
1261
- },
1262
- ],
1263
- linearSink: true,
1264
- scope: "action",
1265
- spelling: "payout",
1266
- target: "payout",
1267
- },
1268
- {
1269
- cardinality: "one",
1270
- literalKeys: true,
1271
- scope: "instrument",
1272
- spelling: "piece plan",
1273
- target: "piecePlan",
1274
- },
1275
- {
1276
- cardinality: "one",
1277
- literalKeys: true,
1278
- scope: "action",
1279
- spelling: "piece stage",
1280
- target: "pieceStage",
1281
- },
1282
- {
1283
- cardinality: "one",
1284
- effects: [
1285
- {
1286
- kind: "decides",
1287
- per: "clause",
1288
- signature: { fixed: "tenant_port" },
1289
- },
1290
- ],
1291
- scope: "action",
1292
- spelling: "port",
1293
- target: "port",
1294
- },
1295
- {
1296
- cardinality: "one",
1297
- scope: "action",
1298
- spelling: "principal",
1299
- target: "principal",
1300
- },
1301
- {
1302
- cardinality: "one",
1303
- scope: "action",
1304
- spelling: "public action",
1305
- target: "publicAction",
1306
- },
1307
- {
1308
- cardinality: "many",
1309
- effects: [
1310
- {
1311
- kind: "reads",
1312
- per: "element",
1313
- signature: { fixed: "reconcile" },
1314
- },
1315
- ],
1316
- scope: "action",
1317
- spelling: "reconcile",
1318
- target: "reconcile",
1319
- },
1320
- {
1321
- cardinality: "one",
1322
- linearOutputs: ["amountRef"],
1323
- scope: "action",
1324
- spelling: "computes remainder",
1325
- target: "remainder",
1326
- },
1327
- {
1328
- cardinality: "many",
1329
- effects: [
1330
- {
1331
- kind: "reads",
1332
- per: "clause",
1333
- signature: { fixed: "requires_aggregate" },
1334
- },
1335
- ],
1336
- scope: "action",
1337
- spelling: "requires aggregate",
1338
- target: "requiresAggregate",
1339
- },
1340
- {
1341
- cardinality: "many",
1342
- effects: [
1343
- {
1344
- kind: "reads",
1345
- per: "clause",
1346
- signature: { fixed: "requires_checks" },
1347
- },
1348
- ],
1349
- scope: "action",
1350
- spelling: "requires checks",
1351
- target: "requiresChecks",
1352
- },
1353
- {
1354
- cardinality: "one",
1355
- scope: "action",
1356
- spelling: "requires drained",
1357
- target: "requiresDrainedAccount",
1358
- },
1359
- {
1360
- cardinality: "many",
1361
- scope: "action",
1362
- spelling: "requires exposure",
1363
- target: "requiresExposure",
1364
- },
1365
- {
1366
- cardinality: "many",
1367
- effects: [
1368
- {
1369
- kind: "reads",
1370
- per: "clause",
1371
- signature: { fixed: "requires_refs" },
1372
- },
1373
- ],
1374
- scope: "action",
1375
- spelling: "requires refs",
1376
- target: "requiresRefs",
1377
- },
1378
- {
1379
- cardinality: "one",
1380
- scope: "action",
1381
- spelling: "sandbox failure point",
1382
- target: "sandboxFailurePoint",
1383
- },
1384
- {
1385
- cardinality: "one",
1386
- scope: "action",
1387
- spelling: "sets at",
1388
- target: "setsAt",
1389
- },
1390
- {
1391
- cardinality: "one",
1392
- linearOutputs: ["amountRef"],
1393
- scope: "action",
1394
- spelling: "computes signed_sum",
1395
- target: "signedSum",
1396
- },
1397
- {
1398
- cardinality: "many",
1399
- linearSink: true,
1400
- scope: "action",
1401
- spelling: "steps",
1402
- target: "steps",
1403
- },
1404
- {
1405
- cardinality: "one",
1406
- scope: "action",
1407
- spelling: "summary",
1408
- target: "summary",
1409
- },
1410
- {
1411
- cardinality: "many",
1412
- scope: "action",
1413
- spelling: "updates",
1414
- target: "updates",
1415
- },
1416
- {
1417
- cardinality: "many",
1418
- effects: [
1419
- {
1420
- kind: "notifies",
1421
- per: "element",
1422
- signature: { fromField: "channel" },
1423
- },
1424
- ],
1425
- scope: "action",
1426
- spelling: "notify",
1427
- target: "effects.notifies",
1428
- },
1429
- {
1430
- cardinality: "one",
1431
- scope: "instrument",
1432
- spelling: "agent description",
1433
- target: "agentDescription",
1434
- },
1435
- {
1436
- cardinality: "many",
1437
- scope: "instrument",
1438
- spelling: "aggregate invariants",
1439
- target: "aggregateInvariants",
1440
- },
1441
- {
1442
- cardinality: "one",
1443
- scope: "instrument",
1444
- spelling: "caller parked states",
1445
- target: "callerParkedStates",
1446
- },
1447
- {
1448
- cardinality: "one",
1449
- scope: "instrument",
1450
- spelling: "description",
1451
- target: "description",
1452
- },
1453
- {
1454
- cardinality: "many",
1455
- scope: "instrument",
1456
- spelling: "dials",
1457
- target: "dials",
1458
- },
1459
- {
1460
- cardinality: "one",
1461
- scope: "instrument",
1462
- spelling: "distinct parties",
1463
- target: "distinctParties",
1464
- },
1465
- {
1466
- cardinality: "many",
1467
- scope: "instrument",
1468
- spelling: "computes derived",
1469
- target: "derivedAmounts",
1470
- },
1471
- {
1472
- cardinality: "many",
1473
- scope: "instrument",
1474
- spelling: "computes fees",
1475
- target: "feeRules",
1476
- },
1477
- {
1478
- cardinality: "one",
1479
- scope: "instrument",
1480
- spelling: "id prefix",
1481
- target: "idPrefix",
1482
- },
1483
- {
1484
- cardinality: "many",
1485
- scope: "instrument",
1486
- spelling: "nav",
1487
- target: "nav",
1488
- },
1489
- {
1490
- cardinality: "many",
1491
- scope: "instrument",
1492
- spelling: "partitions",
1493
- target: "partitions",
1494
- },
1495
- {
1496
- cardinality: "one",
1497
- scope: "instrument",
1498
- spelling: "subject",
1499
- target: "subject",
1500
- },
1501
- {
1502
- cardinality: "one",
1503
- scope: "instrument",
1504
- spelling: "summary",
1505
- target: "summary",
1506
- },
1507
- {
1508
- cardinality: "one",
1509
- scope: "instrument",
1510
- spelling: "surface visibility",
1511
- target: "surfaceVisibility",
1512
- },
1513
- {
1514
- cardinality: "one",
1515
- scope: "instrument",
1516
- spelling: "template binding",
1517
- target: "templateBinding",
1518
- },
1519
- {
1520
- cardinality: "one",
1521
- scope: "instrument",
1522
- spelling: "template id",
1523
- target: "templateId",
1524
- },
1525
- {
1526
- cardinality: "one",
1527
- scope: "instrument",
1528
- spelling: "title",
1529
- target: "title",
1530
- },
1531
- {
1532
- cardinality: "one",
1533
- scope: "instrument",
1534
- spelling: "update",
1535
- target: "update",
1536
- },
1537
- {
1538
- cardinality: "one",
1539
- effects: [],
1540
- scope: "action",
1541
- spelling: "request authority",
1542
- target: "requestAuthority",
1543
- },
1544
- ];
1545
- const udlDocumentShapeSchema = z.strictObject({
1546
- dials: z.array(udlCompositionDialSchema).min(1).optional(),
1547
- instruments: z.array(udlInstrumentSchema).min(1),
1548
- product: udlSnakeCaseSchema,
1549
- subjects: z.array(udlSubjectSchema),
1550
- title: nonEmptyTextSchema,
1551
- udl: z.literal(UDL_FORMAT_VERSION),
1552
- version: z.number().int().positive(),
1553
327
  });
1554
- const moneyPatterns = new Set(["^[1-9][0-9]{0,17}$", "^(0|[1-9][0-9]{0,17})$"]);
1555
- function isMoneyField(schema) {
1556
- return (typeof schema?.pattern === "string" && moneyPatterns.has(schema.pattern));
1557
- }
1558
- function referenceTargets(schema, instruments) {
1559
- const prefix = referencePatternPrefix(schema);
1560
- return prefix
1561
- ? instruments.filter((instrument) => instrument.idPrefix === prefix)
1562
- : [];
1563
- }
1564
- function declaredMoneyRefKeys(instrument) {
1565
- return new Set([
1566
- ...Object.values(instrument.actions).flatMap((action) => [
1567
- ...(action.remainder
1568
- ? [
1569
- action.remainder.amountRef,
1570
- ...(action.remainder.accumulateRef
1571
- ? [action.remainder.accumulateRef]
1572
- : []),
1573
- ]
1574
- : []),
1575
- ...(action.signedSum
1576
- ? [
1577
- action.signedSum.amountRef,
1578
- ...action.signedSum.sources.map((source) => source.subtotalRef),
1579
- ]
1580
- : []),
1581
- ...(action.distribute ? [action.distribute.amountRef] : []),
1582
- ...(action.quote ? [action.quote.chargeRef, action.quote.netRef] : []),
1583
- ]),
1584
- ]);
1585
- }
1586
- export const udlDocumentSchema = udlDocumentShapeSchema.superRefine((document, context) => {
1587
- document.instruments.forEach((instrument, instrumentIndex) => {
1588
- instrument.derivedAmounts?.forEach((amount, amountIndex) => {
1589
- const base = [
1590
- "instruments",
1591
- instrumentIndex,
1592
- "derivedAmounts",
1593
- amountIndex,
1594
- ];
1595
- if (!isMoneyField(instrument.fields[amount.field])) {
1596
- context.addIssue({
1597
- code: "custom",
1598
- message: `derived amount target ${amount.field} must be a declared money field`,
1599
- path: [...base, "field"],
1600
- });
1601
- }
1602
- if (!isMoneyField(instrument.fields[amount.sourceField])) {
1603
- context.addIssue({
1604
- code: "custom",
1605
- message: `derived amount source ${amount.sourceField} must be a declared money field`,
1606
- path: [...base, "sourceField"],
1607
- });
1608
- }
1609
- if (amount.field === amount.sourceField) {
1610
- context.addIssue({
1611
- code: "custom",
1612
- message: "a derived amount cannot derive from itself",
1613
- path: [...base, "sourceField"],
1614
- });
1615
- }
1616
- });
1617
- Object.entries(instrument.actions).forEach(([actionName, action]) => {
1618
- const distribute = action.distribute;
1619
- if (!distribute)
1620
- return;
1621
- const base = [
1622
- "instruments",
1623
- instrumentIndex,
1624
- "actions",
1625
- actionName,
1626
- "distribute",
1627
- ];
1628
- const refSchema = instrument.fields[distribute.refField];
1629
- const parents = refSchema
1630
- ? referenceTargets(refSchema, document.instruments)
1631
- : [];
1632
- if (parents.length !== 1) {
1633
- context.addIssue({
1634
- code: "custom",
1635
- message: `distribute refField ${distribute.refField} must identify exactly one parent instrument`,
1636
- path: [...base, "refField"],
1637
- });
1638
- }
1639
- if (!isMoneyField(instrument.fields[distribute.weightField])) {
1640
- context.addIssue({
1641
- code: "custom",
1642
- message: `distribute weightField ${distribute.weightField} must be a declared money field`,
1643
- path: [...base, "weightField"],
1644
- });
1645
- }
1646
- distribute.statuses.forEach((status, statusIndex) => {
1647
- if (instrument.lifecycle.states.includes(status))
1648
- return;
1649
- context.addIssue({
1650
- code: "custom",
1651
- message: `distribute status ${status} is not declared by ${instrument.id}`,
1652
- path: [...base, "statuses", statusIndex],
1653
- });
1654
- });
1655
- const parent = parents[0];
1656
- if (!parent)
1657
- return;
1658
- const poolMatch = /^(fields|refs)\.([a-z][A-Za-z0-9]*)$/.exec(distribute.pool.path);
1659
- const root = poolMatch?.[1];
1660
- const key = poolMatch?.[2];
1661
- const poolDeclared = root === "fields"
1662
- ? isMoneyField(parent.fields[key ?? ""])
1663
- : root === "refs"
1664
- ? declaredMoneyRefKeys(parent).has(key ?? "")
1665
- : false;
1666
- if (!poolDeclared) {
1667
- context.addIssue({
1668
- code: "custom",
1669
- message: `distribute pool ${distribute.pool.path} must be a declared money field or ref of ${parent.id}`,
1670
- path: [...base, "pool", "path"],
1671
- });
1672
- }
1673
- });
1674
- });
328
+ export const udlDocumentSchema = z.strictObject({
329
+ udl: z.literal(UDL_FORMAT_VERSION),
330
+ version: integer.positive(),
331
+ product: name,
332
+ title: text,
333
+ currency: z.literal("SAR"),
334
+ parties: z.record(name, udlPartySchema),
335
+ instruments: z.array(udlInstrumentSchema).min(1).max(256),
1675
336
  });
1676
337
  //# sourceMappingURL=schema.js.map