@hyperscale0/udl 1.0.0-rc.1 → 1.0.123

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 (234) hide show
  1. package/CHANGELOG.md +5 -211
  2. package/LICENSE.md +356 -0
  3. package/NOTICE.md +8 -0
  4. package/README.md +14 -106
  5. package/dist/canonical.d.ts.map +1 -1
  6. package/dist/canonical.js +1 -29
  7. package/dist/canonical.js.map +1 -1
  8. package/dist/diagnostics.d.ts +13 -165
  9. package/dist/diagnostics.d.ts.map +1 -1
  10. package/dist/diagnostics.js +33 -157
  11. package/dist/diagnostics.js.map +1 -1
  12. package/dist/field-value.d.ts +5 -0
  13. package/dist/field-value.d.ts.map +1 -0
  14. package/dist/field-value.js +85 -0
  15. package/dist/field-value.js.map +1 -0
  16. package/dist/finance.d.ts +10 -53
  17. package/dist/finance.d.ts.map +1 -1
  18. package/dist/finance.js +252 -536
  19. package/dist/finance.js.map +1 -1
  20. package/dist/index.d.ts +9 -13
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +9 -9
  23. package/dist/index.js.map +1 -1
  24. package/dist/json.d.ts +2 -0
  25. package/dist/json.d.ts.map +1 -0
  26. package/dist/json.js +30 -0
  27. package/dist/json.js.map +1 -0
  28. package/dist/limits.d.ts +3 -28
  29. package/dist/limits.d.ts.map +1 -1
  30. package/dist/limits.js +5 -31
  31. package/dist/limits.js.map +1 -1
  32. package/dist/object-contract.d.ts +140 -0
  33. package/dist/object-contract.d.ts.map +1 -0
  34. package/dist/object-contract.js +314 -0
  35. package/dist/object-contract.js.map +1 -0
  36. package/dist/object-semantics-schema.d.ts +5710 -0
  37. package/dist/object-semantics-schema.d.ts.map +1 -0
  38. package/dist/object-semantics-schema.js +119 -0
  39. package/dist/object-semantics-schema.js.map +1 -0
  40. package/dist/object-semantics.d.ts +6 -0
  41. package/dist/object-semantics.d.ts.map +1 -0
  42. package/dist/object-semantics.js +279 -0
  43. package/dist/object-semantics.js.map +1 -0
  44. package/dist/object-structures.d.ts +4 -0
  45. package/dist/object-structures.d.ts.map +1 -0
  46. package/dist/object-structures.js +134 -0
  47. package/dist/object-structures.js.map +1 -0
  48. package/dist/reporting-validation.d.ts +8 -0
  49. package/dist/reporting-validation.d.ts.map +1 -0
  50. package/dist/reporting-validation.js +252 -0
  51. package/dist/reporting-validation.js.map +1 -0
  52. package/dist/reporting.d.ts +477 -0
  53. package/dist/reporting.d.ts.map +1 -0
  54. package/dist/reporting.js +169 -0
  55. package/dist/reporting.js.map +1 -0
  56. package/dist/schema.d.ts +5572 -2710
  57. package/dist/schema.d.ts.map +1 -1
  58. package/dist/schema.js +444 -1121
  59. package/dist/schema.js.map +1 -1
  60. package/dist/validation.d.ts +10 -73
  61. package/dist/validation.d.ts.map +1 -1
  62. package/dist/validation.js +1109 -2569
  63. package/dist/validation.js.map +1 -1
  64. package/docs/assets/brand/manifest.json +30 -0
  65. package/docs/assets/brand/udl-horizontal-white.svg +1 -0
  66. package/docs/assets/brand/udl-horizontal.svg +1 -0
  67. package/docs/assets/brand/udl-stacked-white.svg +1 -0
  68. package/docs/assets/brand/udl-stacked.svg +1 -0
  69. package/docs/assets/udl.svg +11 -0
  70. package/package.json +13 -22
  71. package/spec/README.md +300 -172
  72. package/spec/cars.udl.json +249 -0
  73. package/spec/udl.schema.json +3066 -2242
  74. package/src/canonical.ts +1 -35
  75. package/src/diagnostics.ts +76 -209
  76. package/src/field-value.ts +87 -0
  77. package/src/finance.ts +327 -832
  78. package/src/index.ts +18 -95
  79. package/src/json.ts +34 -0
  80. package/src/limits.ts +5 -31
  81. package/src/object-contract.ts +559 -0
  82. package/src/object-semantics-schema.ts +148 -0
  83. package/src/object-semantics.ts +378 -0
  84. package/src/object-structures.ts +255 -0
  85. package/src/reporting-validation.ts +383 -0
  86. package/src/reporting.ts +178 -0
  87. package/src/schema.ts +510 -1277
  88. package/src/validation.ts +1630 -4545
  89. package/LICENSE +0 -661
  90. package/LICENSING.md +0 -51
  91. package/TRADEMARKS.md +0 -35
  92. package/conformance/README.md +0 -82
  93. package/conformance/evolution/action-contract.expected.json +0 -10
  94. package/conformance/evolution/action-contract.live.udl +0 -44
  95. package/conformance/evolution/action-contract.next.udl +0 -45
  96. package/conformance/evolution/product-identity.expected.json +0 -10
  97. package/conformance/evolution/product-identity.live.udl +0 -55
  98. package/conformance/evolution/product-identity.next.udl +0 -55
  99. package/conformance/evolution/version-required.expected.json +0 -10
  100. package/conformance/evolution/version-required.live.udl +0 -55
  101. package/conformance/evolution/version-required.next.udl +0 -58
  102. package/conformance/invalid/action-without-transition.expected.json +0 -10
  103. package/conformance/invalid/action-without-transition.udl +0 -60
  104. package/conformance/invalid/agent-description-too-long.expected.json +0 -10
  105. package/conformance/invalid/agent-description-too-long.udl +0 -57
  106. package/conformance/invalid/blank-title.expected.json +0 -10
  107. package/conformance/invalid/blank-title.udl +0 -54
  108. package/conformance/invalid/composition-dial-duplicate-key.expected.json +0 -10
  109. package/conformance/invalid/composition-dial-duplicate-key.udl +0 -73
  110. package/conformance/invalid/depth-budget.expected.json +0 -10
  111. package/conformance/invalid/depth-budget.udl +0 -49
  112. package/conformance/invalid/duplicate-subject.expected.json +0 -10
  113. package/conformance/invalid/duplicate-subject.udl +0 -230
  114. package/conformance/invalid/forged-effects.expected.json +0 -10
  115. package/conformance/invalid/forged-effects.udl +0 -76
  116. package/conformance/invalid/format-version.expected.json +0 -10
  117. package/conformance/invalid/format-version.udl +0 -54
  118. package/conformance/invalid/instrument-id-not-snake-case.expected.json +0 -10
  119. package/conformance/invalid/instrument-id-not-snake-case.udl +0 -54
  120. package/conformance/invalid/invalid-aggregate-gate-shape.expected.json +0 -10
  121. package/conformance/invalid/invalid-aggregate-gate-shape.udl +0 -1819
  122. package/conformance/invalid/invalid-check-duration.expected.json +0 -10
  123. package/conformance/invalid/invalid-check-duration.udl +0 -219
  124. package/conformance/invalid/invalid-dial-anchor.expected.json +0 -10
  125. package/conformance/invalid/invalid-dial-anchor.udl +0 -219
  126. package/conformance/invalid/invalid-exception-parent-ref.expected.json +0 -10
  127. package/conformance/invalid/invalid-exception-parent-ref.udl +0 -2487
  128. package/conformance/invalid/invalid-exposure-shape.expected.json +0 -10
  129. package/conformance/invalid/invalid-exposure-shape.udl +0 -1819
  130. package/conformance/invalid/invalid-remainder.expected.json +0 -10
  131. package/conformance/invalid/invalid-remainder.udl +0 -220
  132. package/conformance/invalid/invalid-schema-keyword.expected.json +0 -10
  133. package/conformance/invalid/invalid-schema-keyword.udl +0 -220
  134. package/conformance/invalid/invalid-utf8.expected.json +0 -10
  135. package/conformance/invalid/invalid-utf8.udl +0 -1
  136. package/conformance/invalid/malformed-json.expected.json +0 -10
  137. package/conformance/invalid/malformed-json.udl +0 -1
  138. package/conformance/invalid/missing-create-action.expected.json +0 -10
  139. package/conformance/invalid/missing-create-action.udl +0 -48
  140. package/conformance/invalid/missing-exception-amount-field.expected.json +0 -10
  141. package/conformance/invalid/missing-exception-amount-field.udl +0 -2487
  142. package/conformance/invalid/missing-exception-contract.expected.json +0 -14
  143. package/conformance/invalid/missing-exception-contract.udl +0 -2523
  144. package/conformance/invalid/missing-exception-reason-field.expected.json +0 -10
  145. package/conformance/invalid/missing-exception-reason-field.udl +0 -2487
  146. package/conformance/invalid/not-an-object.expected.json +0 -10
  147. package/conformance/invalid/not-an-object.udl +0 -1
  148. package/conformance/invalid/payout-reconcile-not-a-bank-debit.expected.json +0 -10
  149. package/conformance/invalid/payout-reconcile-not-a-bank-debit.udl +0 -259
  150. package/conformance/invalid/quote-freeze-set-incomplete.expected.json +0 -10
  151. package/conformance/invalid/quote-freeze-set-incomplete.udl +0 -261
  152. package/conformance/invalid/quote-named-reference-gate.expected.json +0 -10
  153. package/conformance/invalid/quote-named-reference-gate.udl +0 -50
  154. package/conformance/invalid/reconcile-named-reference-gate.expected.json +0 -10
  155. package/conformance/invalid/reconcile-named-reference-gate.udl +0 -50
  156. package/conformance/invalid/unknown-key.expected.json +0 -10
  157. package/conformance/invalid/unknown-key.udl +0 -55
  158. package/conformance/invalid/unknown-reference-gate-field.expected.json +0 -10
  159. package/conformance/invalid/unknown-reference-gate-field.udl +0 -1819
  160. package/conformance/invalid/unknown-required-field.expected.json +0 -10
  161. package/conformance/invalid/unknown-required-field.udl +0 -220
  162. package/conformance/invalid/unreachable-state.expected.json +0 -10
  163. package/conformance/invalid/unreachable-state.udl +0 -55
  164. package/conformance/invalid/wrong-exception-amount-field.expected.json +0 -10
  165. package/conformance/invalid/wrong-exception-amount-field.udl +0 -2487
  166. package/conformance/invalid/wrong-exception-reason-field.expected.json +0 -10
  167. package/conformance/invalid/wrong-exception-reason-field.udl +0 -2487
  168. package/conformance/valid/agent-description.expected.json +0 -6
  169. package/conformance/valid/agent-description.udl +0 -66
  170. package/conformance/valid/cards.expected.json +0 -6
  171. package/conformance/valid/cards.udl +0 -1579
  172. package/conformance/valid/commerce-escrow.expected.json +0 -6
  173. package/conformance/valid/commerce-escrow.udl +0 -1512
  174. package/conformance/valid/compiled-crowdfunding.expected.json +0 -6
  175. package/conformance/valid/compiled-crowdfunding.udl +0 -1843
  176. package/conformance/valid/compiled-watch-club.expected.json +0 -6
  177. package/conformance/valid/compiled-watch-club.udl +0 -2486
  178. package/conformance/valid/complete-contract.expected.json +0 -6
  179. package/conformance/valid/complete-contract.udl +0 -218
  180. package/conformance/valid/effect-signatures.expected.json +0 -6
  181. package/conformance/valid/effect-signatures.udl +0 -75
  182. package/conformance/valid/hand-edited.expected.json +0 -6
  183. package/conformance/valid/hand-edited.udl +0 -1
  184. package/conformance/valid/insured-car-marketplace.expected.json +0 -6
  185. package/conformance/valid/insured-car-marketplace.udl +0 -1050
  186. package/conformance/valid/insured-travel.expected.json +0 -6
  187. package/conformance/valid/insured-travel.udl +0 -3469
  188. package/conformance/valid/minimal.expected.json +0 -6
  189. package/conformance/valid/minimal.udl +0 -54
  190. package/conformance/valid/protection.expected.json +0 -6
  191. package/conformance/valid/protection.udl +0 -1551
  192. package/conformance/valid/string-escaping.expected.json +0 -6
  193. package/conformance/valid/string-escaping.udl +0 -54
  194. package/dist/check-profiles.d.ts +0 -57
  195. package/dist/check-profiles.d.ts.map +0 -1
  196. package/dist/check-profiles.js +0 -62
  197. package/dist/check-profiles.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -184
  201. package/dist/cli.js.map +0 -1
  202. package/dist/effects.d.ts +0 -41
  203. package/dist/effects.d.ts.map +0 -1
  204. package/dist/effects.js +0 -110
  205. package/dist/effects.js.map +0 -1
  206. package/dist/evolution.d.ts +0 -120
  207. package/dist/evolution.d.ts.map +0 -1
  208. package/dist/evolution.js +0 -567
  209. package/dist/evolution.js.map +0 -1
  210. package/dist/parser.d.ts +0 -4
  211. package/dist/parser.d.ts.map +0 -1
  212. package/dist/parser.js +0 -44
  213. package/dist/parser.js.map +0 -1
  214. package/docs/README.md +0 -14
  215. package/docs/guide/01-a-document.md +0 -37
  216. package/docs/guide/02-money-steps.md +0 -23
  217. package/docs/guide/03-laws.md +0 -18
  218. package/docs/guide/04-fees-and-remainder.md +0 -15
  219. package/docs/guide/05-checks-updates-dials.md +0 -7
  220. package/docs/guide/06-effects.md +0 -11
  221. package/docs/guide/07-evolution.md +0 -11
  222. package/docs/guide/08-implementing.md +0 -13
  223. package/docs/llms-full.txt +0 -1241
  224. package/docs/llms.txt +0 -14
  225. package/docs/reference/canonical.md +0 -16
  226. package/docs/reference/clauses.md +0 -986
  227. package/docs/reference/cli.md +0 -22
  228. package/docs/reference/diagnostics.md +0 -32
  229. package/skills/udl/SKILL.md +0 -28
  230. package/src/check-profiles.ts +0 -80
  231. package/src/cli.ts +0 -196
  232. package/src/effects.ts +0 -163
  233. package/src/evolution.ts +0 -965
  234. package/src/parser.ts +0 -58
@@ -1,22 +0,0 @@
1
- <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 1.0.0-rc.1. Edit the source, not cli.md. -->
2
-
3
- # Command reference
4
-
5
- The installed `udl` binary exposes the following commands and exit codes.
6
-
7
- ```text
8
- udl - the Universal Domain Language toolchain
9
-
10
- usage:
11
- udl validate <file> parse <file> and report every issue found
12
- udl fmt <file> [--write] print the canonical form, or rewrite the file
13
- udl canon <file> [--digest] print canonical bytes, or their SHA-256 digest
14
- udl diff <live> <next> check <next> against the append-only law
15
- udl explain UDL#### explain one stable diagnostic code
16
- udl help print this
17
-
18
- exit codes:
19
- 0 the document is admissible, or the change is additive
20
- 1 the document was refused, or the change breaks the append-only law
21
- 2 the command line was wrong, or a file could not be read
22
- ```
@@ -1,32 +0,0 @@
1
- <!-- Generated by scripts/docs/build.ts from @hyperscale0/udl 1.0.0-rc.1. Edit the source, not diagnostics.md. -->
2
-
3
- # Diagnostic reference
4
-
5
- Codes are stable. Titles and messages may become clearer without changing the code. Apply the listed fix, then validate the whole document again.
6
-
7
- | Code | Family | Category | Title | Fix |
8
- | --------- | --------- | ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
9
- | `UDL1001` | admission | invalid_utf8 | Invalid UTF-8 | Encode the document as valid UTF-8. |
10
- | `UDL1002` | admission | invalid_json | Invalid JSON | Repair the JSON syntax before validation. |
11
- | `UDL1003` | admission | invalid_shape | Invalid document shape | Match the published UDL JSON Schema. |
12
- | `UDL1004` | admission | resource_limit | Resource limit exceeded | Reduce the source size, nesting, values, strings, references, or financial paths named by the message. |
13
- | `UDL2001` | document | invalid_semantics | Duplicate declaration | Give each declaration a unique name. |
14
- | `UDL2002` | document | invalid_semantics | Document law violation | Repair the declaration, subject contract, or derived effects named by the message. |
15
- | `UDL2005` | document | invalid_semantics | Derived effects mismatch | Regenerate the action effects from its clauses. |
16
- | `UDL3001` | lifecycle | invalid_semantics | Lifecycle is not closed | Declare every state and action transition, and make every state reachable. |
17
- | `UDL4001` | finance | invalid_semantics | Money graph violation | Balance every funded amount and close every hold on each lifecycle path. |
18
- | `UDL5001` | gates | invalid_semantics | Reference gate violation | Point the gate at a declared instrument, action, state, field, and reference. |
19
- | `UDL5002` | gates | invalid_semantics | Check requirement violation | Use a declared check with compatible evidence and recurrence. |
20
- | `UDL5003` | gates | invalid_semantics | Exposure gate violation | Use declared account and money fields for the exposure gate. |
21
- | `UDL5004` | gates | invalid_semantics | Aggregate law violation | Point the aggregate at compatible parent and child fields. |
22
- | `UDL5005` | gates | invalid_semantics | Settlement or payout violation | Use a declared settlement account and a compatible payout statement line. |
23
- | `UDL5006` | gates | invalid_semantics | Quote and commit violation | Declare one complete quote freeze set and one matching commit action. |
24
- | `UDL5007` | gates | invalid_semantics | Reconcile exception child violation | Name a declared child whose reference points back to this instrument. |
25
- | `UDL5008` | gates | invalid_semantics | Action clause violation | Repair the clause fields and keep incompatible clauses separate. |
26
- | `UDL5009` | gates | invalid_semantics | Reconcile exception amount field is missing or optional | Name the exception child's required money field in amountField. |
27
- | `UDL5010` | gates | invalid_semantics | Reconcile exception amount field has the wrong type | Point amountField at a money field declared by the exception child. |
28
- | `UDL5011` | gates | invalid_semantics | Reconcile exception reason field is missing or optional | Name the exception child's required text field in reasonField. |
29
- | `UDL5012` | gates | invalid_semantics | Reconcile exception reason field has the wrong type | Point reasonField at a required plain text field declared by the exception child. |
30
- | `UDL6001` | schema | invalid_semantics | Unsupported JSON Schema | Use only the sealed UDL JSON Schema subset. |
31
- | `UDL7001` | evolution | invalid_evolution | Stored contract changed | Keep stored identities and contracts unchanged, and add only allowed optional declarations. |
32
- | `UDL7002` | evolution | invalid_evolution | Version was not increased | Increase the product version for every semantic change. |
@@ -1,28 +0,0 @@
1
- ---
2
- name: udl
3
- description: Read, validate, explain, or write Universal Domain Language documents directly. Use for canonical UDL JSON and format-level diagnostics. SDK and MCP consumers should use their generated contracts instead.
4
- ---
5
-
6
- # UDL
7
-
8
- Use UDL when the task concerns the canonical product document, format conformance, or a validator implementation. Use HSX when authoring a product from reusable modules.
9
-
10
- ## Read first
11
-
12
- 1. Read `docs/guide/01-a-document.md` for the document frame.
13
- 2. Read `docs/guide/03-laws.md` before changing lifecycle, money, gates, or references.
14
- 3. Look up exact fields in `spec/udl.schema.json` and clauses in `docs/reference/clauses.md`.
15
-
16
- ## Edit loop
17
-
18
- 1. Change the smallest complete clause.
19
- 2. Run `udl validate <file>`.
20
- 3. Look up each `UDL####` code with `udl explain UDL####` or `docs/reference/diagnostics.md`.
21
- 4. Apply the listed fix. Validate the whole document again.
22
- 5. Run `udl canon <file> --digest` before pinning bytes or an identity.
23
-
24
- Never hand-author `effects`. Derive them from action clauses. Never represent money as a JSON number. Use an integer minor-unit string and its declared currency. Never add a money move without checking every terminal lifecycle path for a matching drain or unwind.
25
-
26
- For a stored definition, validate both documents before `udl diff <live> <next>`. Treat any `UDL7xxx` result as a refused change. A data migration does not make an incompatible document edit additive.
27
-
28
- Validator work must run the complete `conformance/` contract and follow `docs/reference/canonical.md` byte for byte.
@@ -1,80 +0,0 @@
1
- /**
2
- * Evidence profiles that an instrument action may require before it runs.
3
- * The public grammar and the runtime both read this table. A pair absent here
4
- * cannot gate an action.
5
- */
6
- export interface UdlCheckEvidenceProfile {
7
- readonly family: string;
8
- readonly checkKind: string;
9
- readonly kindField?: string;
10
- readonly statusField: string;
11
- readonly statuses: readonly string[];
12
- }
13
-
14
- export const udlCheckEvidenceProfiles = [
15
- {
16
- family: "national_identity",
17
- checkKind: "identity_verification",
18
- kindField: "checkKind",
19
- statusField: "verificationStatus",
20
- statuses: [
21
- "requested",
22
- "waiting",
23
- "completed",
24
- "rejected",
25
- "expired",
26
- "error",
27
- ],
28
- },
29
- {
30
- family: "national_identity",
31
- checkKind: "contact_ownership_verification",
32
- kindField: "checkKind",
33
- statusField: "matchResult",
34
- statuses: ["match", "no_match"],
35
- },
36
- {
37
- family: "credit_bureau",
38
- checkKind: "consumer_inquiry",
39
- kindField: "inquiryType",
40
- statusField: "bureauStatus",
41
- statuses: ["returned", "acknowledged", "correction_required"],
42
- },
43
- {
44
- family: "credit_bureau",
45
- checkKind: "commercial_inquiry",
46
- kindField: "inquiryType",
47
- statusField: "bureauStatus",
48
- statuses: ["returned", "acknowledged", "correction_required"],
49
- },
50
- {
51
- family: "credit_bureau",
52
- checkKind: "facility_report",
53
- kindField: "inquiryType",
54
- statusField: "bureauStatus",
55
- statuses: ["returned", "acknowledged", "correction_required"],
56
- },
57
- {
58
- family: "sanctions_screening",
59
- checkKind: "screen",
60
- kindField: "checkKind",
61
- statusField: "screeningStatus",
62
- statuses: ["clear", "hit", "manual_review"],
63
- },
64
- {
65
- family: "enforcement_instrument",
66
- checkKind: "note_request",
67
- statusField: "requestStatus",
68
- statuses: ["debtor_approved", "debtor_rejected", "auto_cancelled"],
69
- },
70
- ] as const satisfies readonly UdlCheckEvidenceProfile[];
71
-
72
- /** Returns the tenant-gateable evidence profile for one family and check. */
73
- export function udlCheckEvidenceProfile(
74
- family: string,
75
- checkKind: string,
76
- ): UdlCheckEvidenceProfile | undefined {
77
- return udlCheckEvidenceProfiles.find(
78
- (profile) => profile.family === family && profile.checkKind === checkKind,
79
- );
80
- }
package/src/cli.ts DELETED
@@ -1,196 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * The `udl` command. Actions over the same library the compiler uses:
4
- * `validate` answers whether a document is admissible, `fmt` writes the
5
- * canonical bytes, and `diff` answers whether a change is legal against a
6
- * document that already has live instances.
7
- */
8
- import { readFile, writeFile } from "node:fs/promises";
9
-
10
- import {
11
- canonicalizeUdl,
12
- canonicalDigest,
13
- diffValidatedUdlEvolution,
14
- parseUdl,
15
- UdlError,
16
- type UdlDocument,
17
- udlDiagnostic,
18
- } from "./index.js";
19
-
20
- const USAGE = `udl - the Universal Domain Language toolchain
21
-
22
- usage:
23
- udl validate <file> parse <file> and report every issue found
24
- udl fmt <file> [--write] print the canonical form, or rewrite the file
25
- udl canon <file> [--digest] print canonical bytes, or their SHA-256 digest
26
- udl diff <live> <next> check <next> against the append-only law
27
- udl explain UDL#### explain one stable diagnostic code
28
- udl help print this
29
-
30
- exit codes:
31
- 0 the document is admissible, or the change is additive
32
- 1 the document was refused, or the change breaks the append-only law
33
- 2 the command line was wrong, or a file could not be read`;
34
-
35
- async function main(argv: readonly string[]): Promise<number> {
36
- const [command, ...rest] = argv;
37
- switch (command) {
38
- case "help":
39
- case "-h":
40
- case "--help":
41
- console.log(USAGE);
42
- return 0;
43
- case "validate":
44
- return validateCommand(rest);
45
- case "fmt":
46
- return formatCommand(rest);
47
- case "canon":
48
- return canonCommand(rest);
49
- case "diff":
50
- return diffCommand(rest);
51
- case "explain":
52
- return explainCommand(rest);
53
- case undefined:
54
- return usageError("udl: no command given");
55
- default:
56
- return usageError(`udl: unknown command ${command}`);
57
- }
58
- }
59
-
60
- async function canonCommand(args: readonly string[]): Promise<number> {
61
- const digest = args.includes("--digest");
62
- const [file, ...extra] = args.filter((argument) => argument !== "--digest");
63
- if (file === undefined || extra.length > 0) {
64
- return usageError("usage: udl canon <file> [--digest]");
65
- }
66
- const bytes = await readDocument(file);
67
- if (bytes === null) return 2;
68
- let document: UdlDocument;
69
- try {
70
- document = parseUdl(bytes);
71
- } catch (error) {
72
- return reportIssues(file, error);
73
- }
74
- if (digest) console.log(await canonicalDigest(document));
75
- else process.stdout.write(canonicalizeUdl(bytes));
76
- return 0;
77
- }
78
-
79
- function explainCommand(args: readonly string[]): number {
80
- const [code, ...extra] = args;
81
- if (code === undefined || extra.length > 0) {
82
- return usageError("usage: udl explain UDL####");
83
- }
84
- const diagnostic = udlDiagnostic(code);
85
- if (!diagnostic) {
86
- return usageError(`udl: unknown diagnostic code ${code}`);
87
- }
88
- console.log(`${code} ${diagnostic.title}`);
89
- console.log(`category: ${diagnostic.category}`);
90
- console.log(`family: ${diagnostic.family}`);
91
- console.log(`fix: ${diagnostic.fix}`);
92
- return 0;
93
- }
94
-
95
- async function validateCommand(args: readonly string[]): Promise<number> {
96
- const [file, ...extra] = args;
97
- if (file === undefined || extra.length > 0) {
98
- return usageError("usage: udl validate <file>");
99
- }
100
- const bytes = await readDocument(file);
101
- if (bytes === null) return 2;
102
- if (parseDocument(file, bytes) === null) return 1;
103
- console.log(`${file}: ok`);
104
- return 0;
105
- }
106
-
107
- async function formatCommand(args: readonly string[]): Promise<number> {
108
- const write = args.includes("--write");
109
- const [file, ...extra] = args.filter((argument) => argument !== "--write");
110
- if (file === undefined || extra.length > 0) {
111
- return usageError("usage: udl fmt <file> [--write]");
112
- }
113
- const bytes = await readDocument(file);
114
- if (bytes === null) return 2;
115
-
116
- let canonical: string;
117
- try {
118
- canonical = canonicalizeUdl(bytes);
119
- } catch (error) {
120
- return reportIssues(file, error);
121
- }
122
-
123
- if (!write) {
124
- process.stdout.write(canonical);
125
- return 0;
126
- }
127
- // The bytes decoded cleanly already: canonicalizeUdl refuses anything else.
128
- if (new TextDecoder().decode(bytes) === canonical) return 0;
129
- await writeFile(file, canonical);
130
- console.log(`${file}: formatted`);
131
- return 0;
132
- }
133
-
134
- async function diffCommand(args: readonly string[]): Promise<number> {
135
- const [livePath, nextPath, ...extra] = args;
136
- if (livePath === undefined || nextPath === undefined || extra.length > 0) {
137
- return usageError("usage: udl diff <live> <next>");
138
- }
139
- const liveBytes = await readDocument(livePath);
140
- const nextBytes = await readDocument(nextPath);
141
- if (liveBytes === null || nextBytes === null) return 2;
142
-
143
- const live = parseDocument(livePath, liveBytes);
144
- const next = parseDocument(nextPath, nextBytes);
145
- if (live === null || next === null) return 1;
146
-
147
- // `parseDocument` already validated both, so take the door that does not
148
- // validate a second time.
149
- const violations = diffValidatedUdlEvolution(live, next);
150
- if (violations.length === 0) {
151
- console.log(`${nextPath}: additive`);
152
- return 0;
153
- }
154
- for (const violation of violations) {
155
- console.error(
156
- `${nextPath}: ${violation.code} ${violation.path}: ${violation.message}`,
157
- );
158
- }
159
- return 1;
160
- }
161
-
162
- async function readDocument(file: string): Promise<Uint8Array | null> {
163
- try {
164
- return await readFile(file);
165
- } catch (error) {
166
- const reason = error instanceof Error ? error.message : String(error);
167
- console.error(`udl: cannot read ${file}: ${reason}`);
168
- return null;
169
- }
170
- }
171
-
172
- /** Parses one document, printing its issues. Null means the document was refused. */
173
- function parseDocument(file: string, bytes: Uint8Array): UdlDocument | null {
174
- try {
175
- return parseUdl(bytes);
176
- } catch (error) {
177
- reportIssues(file, error);
178
- return null;
179
- }
180
- }
181
-
182
- function reportIssues(file: string, error: unknown): number {
183
- if (!(error instanceof UdlError)) throw error;
184
- for (const issue of error.issues) {
185
- console.error(`${file}: ${issue.code} ${issue.path}: ${issue.message}`);
186
- }
187
- return 1;
188
- }
189
-
190
- function usageError(message: string): number {
191
- console.error(message);
192
- console.error(USAGE);
193
- return 2;
194
- }
195
-
196
- process.exitCode = await main(process.argv.slice(2));
package/src/effects.ts DELETED
@@ -1,163 +0,0 @@
1
- export const udlEffectKinds = [
2
- "decides",
3
- "holds",
4
- "moves",
5
- "notifies",
6
- "reads",
7
- "schedules",
8
- ] as const;
9
-
10
- export type UdlEffectKind = (typeof udlEffectKinds)[number];
11
-
12
- type EffectDescriptor = {
13
- readonly kind: UdlEffectKind;
14
- readonly per: "clause" | "element";
15
- readonly signature:
16
- | { readonly fixed: string }
17
- | { readonly fromField: string }
18
- | { readonly movementClass: true };
19
- };
20
-
21
- type ActionClauseDescriptor = {
22
- readonly effects?: readonly EffectDescriptor[];
23
- readonly scope: "action" | "instrument";
24
- readonly target: string;
25
- };
26
-
27
- type EffectRow = {
28
- readonly channel?: string;
29
- readonly role?: string;
30
- readonly signature: string;
31
- readonly source: string;
32
- };
33
-
34
- export type DerivedUdlEffects = Partial<
35
- Readonly<Record<UdlEffectKind, readonly EffectRow[]>>
36
- >;
37
-
38
- export type UdlMovementClass =
39
- | "collection.pay_in"
40
- | "deposit.attributed"
41
- | "payout.external"
42
- | "transfer.internal";
43
-
44
- interface Movement {
45
- readonly bind?: Readonly<Record<string, unknown>>;
46
- readonly operation: string;
47
- }
48
-
49
- function boundPath(move: Movement, endpoint: string): string | undefined {
50
- const binding = move.bind?.[endpoint];
51
- if (binding === null || typeof binding !== "object") return undefined;
52
- const path = (binding as { readonly path?: unknown }).path;
53
- return typeof path === "string" ? path : undefined;
54
- }
55
-
56
- /**
57
- * Maps a UDL money operation to the meter and pricing class that owns it.
58
- * Internal-transfer endpoint bindings are product accounts by construction.
59
- * External collection, deposit, and payout operations carry their role in the
60
- * operation family because their remote endpoint is not a UDL account binding.
61
- */
62
- export function movementClass(move: Movement): UdlMovementClass {
63
- if (move.operation.startsWith("internal_transfer.")) {
64
- const source = boundPath(move, "sourceAccountId");
65
- const destination = boundPath(move, "destinationAccountId");
66
- if (
67
- source?.startsWith("fields.") &&
68
- destination === "refs.escrowAccountId"
69
- ) {
70
- return "collection.pay_in";
71
- }
72
- return "transfer.internal";
73
- }
74
- if (move.operation.startsWith("collection.pay_in.")) {
75
- return "collection.pay_in";
76
- }
77
- if (move.operation.startsWith("deposit.")) {
78
- return "deposit.attributed";
79
- }
80
- if (move.operation.startsWith("payout.")) {
81
- return "payout.external";
82
- }
83
- throw new Error(`cannot classify UDL movement operation ${move.operation}`);
84
- }
85
-
86
- /** Derive the ABI effect rows declared by the action-clause vocabulary. */
87
- export function deriveUdlActionEffects(
88
- action: Readonly<Record<string, unknown>>,
89
- vocabulary: readonly ActionClauseDescriptor[],
90
- ): DerivedUdlEffects {
91
- const effects: Partial<Record<UdlEffectKind, EffectRow[]>> = {};
92
- for (const clause of vocabulary) {
93
- if (clause.scope !== "action" || !clause.effects) continue;
94
- const value = actionClauseValue(action, clause.target);
95
- if (value === undefined) continue;
96
- for (const descriptor of clause.effects) {
97
- const values =
98
- descriptor.per === "element" && Array.isArray(value) ? value : [value];
99
- for (const [index, candidate] of values.entries()) {
100
- const object = recordValue(candidate);
101
- if (
102
- descriptor.kind === "holds" &&
103
- "fixed" in descriptor.signature &&
104
- descriptor.signature.fixed === "reserve" &&
105
- object?.operation !== "internal_transfer.reserve"
106
- ) {
107
- continue;
108
- }
109
- const suffix = effectSignatureSuffix(descriptor, object);
110
- if (!suffix) continue;
111
- (effects[descriptor.kind] ??= []).push({
112
- signature: `${descriptor.kind}.${suffix}`,
113
- source:
114
- descriptor.per === "element"
115
- ? `${clause.target}[${index}]`
116
- : clause.target,
117
- ...(descriptor.kind === "notifies" &&
118
- typeof object?.channel === "string" &&
119
- typeof object.role === "string"
120
- ? { channel: object.channel, role: object.role }
121
- : {}),
122
- });
123
- }
124
- }
125
- }
126
- return effects;
127
- }
128
-
129
- function actionClauseValue(
130
- action: Readonly<Record<string, unknown>>,
131
- target: string,
132
- ): unknown {
133
- const [head, tail] = target.split(".");
134
- if (!head) return undefined;
135
- const value = action[head];
136
- if (!tail) return value;
137
- return recordValue(value)?.[tail];
138
- }
139
-
140
- function effectSignatureSuffix(
141
- descriptor: EffectDescriptor,
142
- value: Readonly<Record<string, unknown>> | undefined,
143
- ): string | undefined {
144
- if ("fixed" in descriptor.signature) return descriptor.signature.fixed;
145
- if ("fromField" in descriptor.signature) {
146
- const field = value?.[descriptor.signature.fromField];
147
- return typeof field === "string" ? field : undefined;
148
- }
149
- if (!value || !("movementClass" in descriptor.signature)) return undefined;
150
- const operation = value.operation;
151
- const bind = recordValue(value.bind);
152
- return typeof operation === "string"
153
- ? movementClass({ ...(bind ? { bind } : {}), operation })
154
- : undefined;
155
- }
156
-
157
- function recordValue(
158
- value: unknown,
159
- ): Readonly<Record<string, unknown>> | undefined {
160
- return value !== null && typeof value === "object" && !Array.isArray(value)
161
- ? (value as Readonly<Record<string, unknown>>)
162
- : undefined;
163
- }