@hyperscale0/adl 1.0.0-beta.1 → 1.0.0-beta.11

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 (44) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/LICENSE.md +356 -0
  3. package/NOTICE.md +8 -0
  4. package/README.md +28 -144
  5. package/SECURITY.md +0 -4
  6. package/conformance/cases.json +50 -770
  7. package/conformance/subject-adapter.ts +26 -0
  8. package/dist/boundary-fixture.d.ts +21 -0
  9. package/dist/boundary-fixture.d.ts.map +1 -0
  10. package/dist/boundary-fixture.js +31 -0
  11. package/dist/boundary-fixture.js.map +1 -0
  12. package/dist/index.d.ts +66 -195
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +97 -105
  15. package/dist/index.js.map +1 -1
  16. package/dist/vocabulary.d.ts +5 -90
  17. package/dist/vocabulary.d.ts.map +1 -1
  18. package/dist/vocabulary.js +12 -135
  19. package/dist/vocabulary.js.map +1 -1
  20. package/docs/assets/adl.svg +11 -0
  21. package/docs/assets/brand/adl-horizontal-white.svg +1 -0
  22. package/docs/assets/brand/adl-horizontal.svg +1 -0
  23. package/docs/assets/brand/adl-stacked-white.svg +1 -0
  24. package/docs/assets/brand/adl-stacked.svg +1 -0
  25. package/docs/assets/brand/manifest.json +30 -0
  26. package/docs/authoring-guide.md +33 -417
  27. package/package.json +19 -18
  28. package/LICENSE +0 -661
  29. package/LICENSING.md +0 -51
  30. package/TRADEMARKS.md +0 -30
  31. package/dist/conformance.d.ts +0 -41
  32. package/dist/conformance.d.ts.map +0 -1
  33. package/dist/conformance.js +0 -486
  34. package/dist/conformance.js.map +0 -1
  35. package/examples/meridian-bank/README.md +0 -65
  36. package/examples/meridian-bank/example.spec.ts +0 -162
  37. package/examples/meridian-bank/fixtures/payout-status-replay.json +0 -19
  38. package/examples/meridian-bank/fixtures/payout-status-settled.json +0 -19
  39. package/examples/meridian-bank/fixtures/payout-submit-accepted.json +0 -26
  40. package/examples/meridian-bank/fixtures/payout-submit-declined.json +0 -25
  41. package/examples/meridian-bank/fixtures/statement-camt053.json +0 -45
  42. package/examples/meridian-bank/fixtures/webhook-payout-settled.json +0 -11
  43. package/examples/meridian-bank/index.ts +0 -262
  44. package/spec/manifest.schema.json +0 -757
@@ -0,0 +1,26 @@
1
+ /** Synthetic conformance metadata, not a real provider requirement. */
2
+ import type { ProviderAdapter } from "../src/index.js";
3
+
4
+ export const subjectAdapter: ProviderAdapter = {
5
+ provider: "conformance_subject",
6
+ capability: "subject",
7
+ bindings: {
8
+ subject: "strict",
9
+ },
10
+ operationMap: {
11
+ "subject.check": {
12
+ operation: "subject.check",
13
+ direction: "tenant_initiated",
14
+ obligationKind: "subject_check",
15
+ resourceKind: "subject",
16
+ resourceIdPath: "subjectId",
17
+ subjectRequirements: [
18
+ {
19
+ name: "referenceCode",
20
+ type: "text",
21
+ minLength: 1,
22
+ },
23
+ ],
24
+ },
25
+ },
26
+ };
@@ -0,0 +1,21 @@
1
+ import { type BoundaryInstruction, type BoundaryObservation, type BoundaryOutcome } from "./index.js";
2
+ /** The sandbox boundary adapter the platform registers. No transport, clock, accounts or inferred outcome. */
3
+ export declare const genericAdapter: {
4
+ readonly provider: "conformance_boundary";
5
+ readonly capability: "boundary";
6
+ readonly bindings: {
7
+ readonly instruction: "strict";
8
+ };
9
+ readonly operationMap: {
10
+ readonly "boundary.observe": {
11
+ readonly operation: "boundary.observe";
12
+ readonly direction: "system_settlement";
13
+ readonly obligationKind: "boundary_observation";
14
+ readonly resourceKind: "instruction";
15
+ readonly resourceIdPath: "instructionId";
16
+ readonly subjectRequirements: readonly [];
17
+ };
18
+ };
19
+ };
20
+ export declare function observeBoundary(instruction: BoundaryInstruction, outcome: BoundaryOutcome, externalReference: string, observedAt: string): BoundaryObservation;
21
+ //# sourceMappingURL=boundary-fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary-fixture.d.ts","sourceRoot":"","sources":["../src/boundary-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAEpB,8GAA8G;AAC9G,eAAO,MAAM,cAAc;uBAEb,sBAAsB;yBACpB,UAAU;;iBACV,WAAW,EAAE,QAAQ;;;iBAE/B,kBAAkB;qBAChB,SAAS,EAAE,kBAAkB;qBAC7B,SAAS,EAAE,mBAAmB;qBAC9B,cAAc,EAAE,sBAAsB;qBACtC,YAAY,EAAE,aAAa;qBAC3B,cAAc,EAAE,eAAe;qBAC/B,mBAAmB;;;CAIb,CAAC;AAEf,wBAAgB,eAAe,CAC7B,WAAW,EAAE,mBAAmB,EAChC,OAAO,EAAE,eAAe,EACxB,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,GACjB,mBAAmB,CAUrB"}
@@ -0,0 +1,31 @@
1
+ import { createProviderAdapterRegistry, boundaryObservationSchema, } from "./index.js";
2
+ /** The sandbox boundary adapter the platform registers. No transport, clock, accounts or inferred outcome. */
3
+ export const genericAdapter = createProviderAdapterRegistry()([
4
+ {
5
+ provider: "conformance_boundary",
6
+ capability: "boundary",
7
+ bindings: { instruction: "strict" },
8
+ operationMap: {
9
+ "boundary.observe": {
10
+ operation: "boundary.observe",
11
+ direction: "system_settlement",
12
+ obligationKind: "boundary_observation",
13
+ resourceKind: "instruction",
14
+ resourceIdPath: "instructionId",
15
+ subjectRequirements: [],
16
+ },
17
+ },
18
+ },
19
+ ])[0];
20
+ export function observeBoundary(instruction, outcome, externalReference, observedAt) {
21
+ if (instruction.adapter !== genericAdapter.provider)
22
+ throw new Error("instruction adapter mismatch");
23
+ return boundaryObservationSchema.parse({
24
+ adapter: genericAdapter.provider,
25
+ instructionId: instruction.id,
26
+ outcome,
27
+ externalReference,
28
+ observedAt,
29
+ });
30
+ }
31
+ //# sourceMappingURL=boundary-fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary-fixture.js","sourceRoot":"","sources":["../src/boundary-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,GAI1B,MAAM,YAAY,CAAC;AAEpB,8GAA8G;AAC9G,MAAM,CAAC,MAAM,cAAc,GAAG,6BAA6B,EAAE,CAAC;IAC5D;QACE,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE;QACnC,YAAY,EAAE;YACZ,kBAAkB,EAAE;gBAClB,SAAS,EAAE,kBAAkB;gBAC7B,SAAS,EAAE,mBAAmB;gBAC9B,cAAc,EAAE,sBAAsB;gBACtC,YAAY,EAAE,aAAa;gBAC3B,cAAc,EAAE,eAAe;gBAC/B,mBAAmB,EAAE,EAAE;aACxB;SACF;KACF;CACO,CAAC,CAAC,CAAC,CAAC,CAAC;AAEf,MAAM,UAAU,eAAe,CAC7B,WAAgC,EAChC,OAAwB,EACxB,iBAAyB,EACzB,UAAkB;IAElB,IAAI,WAAW,CAAC,OAAO,KAAK,cAAc,CAAC,QAAQ;QACjD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,OAAO,yBAAyB,CAAC,KAAK,CAAC;QACrC,OAAO,EAAE,cAAc,CAAC,QAAQ;QAChC,aAAa,EAAE,WAAW,CAAC,EAAE;QAC7B,OAAO;QACP,iBAAiB;QACjB,UAAU;KACX,CAAC,CAAC;AACL,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,219 +1,90 @@
1
- import { statementAvailabilities, type ChargePosting, type ChargeVatMode, type FinancialAddressMechanism, type FinancialAddressPricing, type FinancialAddressQuota, type FxRateReadBack, type LimitAccessMode, type LimitDimension, type PartnerBankDedupeKey, type PartnerBankLifecycleState, type PartnerBankSigning, type PartnerBankStatementFormat, type PartnerBankWireCodec, type ProviderAuthEnvelope, type ProviderEgressMode, type ProviderNotificationMechanism, type ProviderOperationDirection, type ProviderResourceBinding, type ProviderResponseEnvelope, type ProviderTimestampField, type RailSubstitution, type StatementAvailability, type StatementDebitReference, type StatusEnquiryKey, type ValueDateOffset } from "./vocabulary.js";
2
- export { certifyPartnerBankAdapter, partnerBankConformanceCodes, partnerBankConformanceFindings, type PartnerBankConformanceCode, type PartnerBankConformanceFinding, } from "./conformance.js";
3
- export { statementAvailabilities };
4
- export type { ProviderEgressMode, ProviderOperationDirection, ProviderResourceBinding, ProviderResponseEnvelope, };
1
+ import * as z from "zod";
2
+ import { type UdlObjectField } from "@hyperscale0/udl";
3
+ import { subjectFieldTypes, type ProviderOperationDirection, type ProviderResourceBinding, type SubjectFieldType } from "./vocabulary.js";
4
+ export { subjectFieldTypes };
5
+ export type { ProviderOperationDirection, ProviderResourceBinding, SubjectFieldType, };
6
+ export declare const boundaryOutcomeSchema: z.ZodEnum<{
7
+ acknowledged: "acknowledged";
8
+ confirmed: "confirmed";
9
+ rejected: "rejected";
10
+ unknown: "unknown";
11
+ }>;
12
+ export declare const boundaryInstructionSchema: z.ZodReadonly<z.ZodObject<{
13
+ id: z.ZodString;
14
+ environment: z.ZodEnum<{
15
+ live: "live";
16
+ sandbox: "sandbox";
17
+ }>;
18
+ tenantId: z.ZodString;
19
+ productId: z.ZodString;
20
+ productBuildId: z.ZodString;
21
+ buildDigest: z.ZodString;
22
+ target: z.ZodReadonly<z.ZodObject<{
23
+ kind: z.ZodString;
24
+ attachment: z.ZodString;
25
+ instanceId: z.ZodString;
26
+ }, z.core.$strict>>;
27
+ sourceAccountId: z.ZodString;
28
+ destinationAccountId: z.ZodString;
29
+ amount: z.ZodString;
30
+ currency: z.ZodString;
31
+ adapter: z.ZodString;
32
+ }, z.core.$strict>>;
33
+ export declare const boundaryObservationSchema: z.ZodReadonly<z.ZodObject<{
34
+ adapter: z.ZodString;
35
+ externalReference: z.ZodString;
36
+ instructionId: z.ZodString;
37
+ outcome: z.ZodEnum<{
38
+ acknowledged: "acknowledged";
39
+ confirmed: "confirmed";
40
+ rejected: "rejected";
41
+ unknown: "unknown";
42
+ }>;
43
+ observedAt: z.ZodISODateTime;
44
+ }, z.core.$strict>>;
45
+ export type BoundaryInstruction = z.infer<typeof boundaryInstructionSchema>;
46
+ export type BoundaryObservation = z.infer<typeof boundaryObservationSchema>;
47
+ export type BoundaryOutcome = z.infer<typeof boundaryOutcomeSchema>;
48
+ /** Adapter implementation. Dispatch and enquiry never choose money bindings. */
49
+ export interface BoundaryAdapter {
50
+ readonly id: string;
51
+ dispatch(instruction: BoundaryInstruction): Promise<BoundaryObservation | undefined>;
52
+ observe(instruction: BoundaryInstruction): Promise<BoundaryObservation | undefined>;
53
+ }
5
54
  export interface ProviderAdapterVocabulary {
6
55
  readonly capability: string;
7
- readonly domain: string;
8
- readonly meter: string;
9
56
  readonly obligation: string;
10
57
  readonly operation: string;
11
58
  readonly resource: string;
12
- readonly webhookOperation: string;
13
- readonly webhookResource: string;
14
- readonly webhookResourceId: string;
15
59
  }
16
60
  export interface ProviderAdapter<Vocabulary extends ProviderAdapterVocabulary = ProviderAdapterVocabulary> {
17
61
  readonly bindings: Partial<Record<Vocabulary["resource"], ProviderResourceBinding>>;
18
62
  readonly capability: Vocabulary["capability"];
19
- readonly config: ProviderConfig;
20
- readonly domain?: Vocabulary["domain"];
21
- readonly egress: ProviderEgressMode;
22
63
  readonly operationMap: Partial<Record<Vocabulary["operation"], ProviderOperationBinding<Vocabulary>>>;
23
- readonly profile?: PartnerBankAdapterProfile;
24
64
  readonly provider: string;
25
- readonly webhookMap: Readonly<Record<string, ProviderWebhookTransitionPlan<Vocabulary>>>;
26
65
  }
27
66
  export interface ProviderOperationBinding<Vocabulary extends ProviderAdapterVocabulary = ProviderAdapterVocabulary> {
28
- /**
29
- * Authoritative read-after-write status enquiry for a command whose response
30
- * may have been lost. Presence is an explicit provider-readiness commitment;
31
- * runtimes must never infer enquiry support from the command URL.
32
- */
33
- readonly statusEnquiry?: {
34
- readonly keys: StatusEnquiryKey;
35
- readonly pathTemplate: string;
36
- };
37
- /**
38
- * How THIS operation's response carries its outcome. Per operation, never
39
- * per provider and never inherited from another provider's class: one real
40
- * provider answers two ways, wrapping six of seven endpoints in
41
- * `{status, message, data}` while the seventh returns a bare object. Absent
42
- * means the class is not established yet -- an unclassified operation, not
43
- * a silent `http_200_body_status`.
44
- */
45
- readonly envelope?: ProviderResponseEnvelope;
46
67
  readonly operation: Vocabulary["operation"];
47
68
  readonly direction: ProviderOperationDirection;
48
- readonly meter?: Vocabulary["meter"];
49
69
  readonly obligationKind: Vocabulary["obligation"];
50
70
  readonly resourceIdPath: string;
51
71
  readonly resourceKind: Vocabulary["resource"];
52
- }
53
- export interface ProviderWebhookTransitionPlan<Vocabulary extends ProviderAdapterVocabulary = ProviderAdapterVocabulary> {
54
- readonly eventKind: string;
55
- readonly operationName: Vocabulary["webhookOperation"];
56
- readonly optionalPayloadFields: readonly string[];
57
- readonly requiredFieldGroups: readonly (readonly string[])[];
58
- readonly requiredPayloadFields: readonly string[];
59
- readonly resourceIdField: Vocabulary["webhookResourceId"];
60
- readonly resourceKind: Vocabulary["webhookResource"];
61
- readonly timestampField: ProviderTimestampField;
62
- }
63
- export type ProviderCredentialReference = {
64
- readonly name: string;
65
- readonly source: "environment";
66
- } | {
67
- readonly account: string;
68
- readonly service: string;
69
- readonly source: "keychain";
70
- };
71
- export interface ProviderConfig {
72
- readonly baseUrl?: string;
73
- readonly credentialRef?: ProviderCredentialReference;
74
- }
75
- /**
76
- * The fetch window facts for one declared statement format. The paging fields
77
- * are required for an end-of-day format (`availability: "T-1"` pages over
78
- * history) and absent for an intraday one; conformance enforces that split.
79
- */
80
- export interface PartnerBankStatementWindow {
81
- readonly availability: StatementAvailability;
82
- readonly lookbackDays?: number;
83
- readonly statementDaysPerRequest?: number;
84
- }
85
- export interface PartnerBankAdapterProfile {
86
- readonly auth: {
87
- readonly envelope: ProviderAuthEnvelope;
88
- readonly signing: PartnerBankSigning;
89
- };
90
72
  /**
91
- * How the bank bills its own charges. `posting` says when the charge lands
92
- * (debited separately after the payment, or netted out of the instructed
93
- * amount); `vat` says whether the tax arrives as its own ledger line or
94
- * folded into the charge. Absent when a bank's charge behavior is not
95
- * established -- an unmodeled dimension, not a claim of post-hoc billing.
73
+ * Stored object metadata fields required by this operation. An array of UDL
74
+ * object fields validated against the exported UDL schema and the closed
75
+ * subject field type vocabulary. Absence means requirements are undeclared;
76
+ * an explicit empty array declares no requirements. Neither implies
77
+ * provider readiness.
96
78
  */
97
- readonly charges?: {
98
- /**
99
- * The text this bank prints on its own charge lines, as prefixes: the
100
- * trailing part names the charged thing. Recognizing a charge is Executor
101
- * knowledge, so it is declared per bank and never inferred downstream, and
102
- * a bank whose charge wording is not established leaves this absent rather
103
- * than borrowing another bank's words.
104
- */
105
- readonly narratives?: {
106
- readonly charge: readonly string[];
107
- readonly vat: readonly string[];
108
- };
109
- readonly posting: ChargePosting;
110
- readonly vat: ChargeVatMode;
111
- };
112
- readonly executedReadBack: {
113
- readonly fxRate: FxRateReadBack;
114
- readonly railSubstitution: RailSubstitution;
115
- };
116
- readonly idempotencySpine: {
117
- readonly dedupeKeys: readonly PartnerBankDedupeKey[];
118
- readonly endToEndId: {
119
- readonly maxLength: number;
120
- };
121
- readonly instructionId: {
122
- readonly maxLength: number;
123
- };
124
- readonly paymentReference: {
125
- readonly maxLength: number;
126
- };
127
- };
128
- readonly kind: "partner_bank";
129
- readonly lifecycleVocabulary: Readonly<Record<string, PartnerBankLifecycleState>>;
130
- readonly limitFacts: {
131
- readonly access: LimitAccessMode;
132
- readonly dimensions: readonly LimitDimension[];
133
- };
134
- readonly notificationMechanisms: readonly ProviderNotificationMechanism[];
135
- /**
136
- * Which reference on a statement debit line names the instruction behind it.
137
- * A statement entry carries two: the bank's own entry reference and the
138
- * client reference the instruction was sent with. Banks disagree about which
139
- * one comes back on the statement, and picking the wrong one makes every
140
- * payout the platform instructed look like an unexplained debit. Absent when
141
- * a bank's statement reference identity is not established.
142
- */
143
- readonly statementDebitReference?: StatementDebitReference;
144
- readonly statementFormats: readonly PartnerBankStatementFormat[];
145
- readonly financialAddressProvisioning: {
146
- readonly mechanism: FinancialAddressMechanism;
147
- readonly pricing: FinancialAddressPricing;
148
- readonly quota: FinancialAddressQuota;
149
- };
150
- readonly windows: {
151
- readonly paymentCutoff: {
152
- readonly afterCutoffValueDate: ValueDateOffset;
153
- readonly beforeCutoffValueDate: ValueDateOffset;
154
- /** Bank-local wall-clock time, HH:MM. */
155
- readonly time: string;
156
- /** The IANA time zone the cutoff time is read in (e.g. Europe/Dublin). */
157
- readonly timeZone: string;
158
- };
159
- /** One window per declared statement format, keyed by that format. */
160
- readonly statements: Partial<Readonly<Record<PartnerBankStatementFormat, PartnerBankStatementWindow>>>;
161
- readonly valueDateMaxDaysAhead: number;
162
- };
163
- readonly wireCodec: PartnerBankWireCodec;
164
- }
165
- /**
166
- * The insurance-distribution marketplace class, the partner bank's opposite
167
- * number: an aggregator API whose whole truth arrives in the command
168
- * response. Every field below is a recorded observation of one such surface,
169
- * not a reading of a standard.
170
- *
171
- * What makes the class distinct from `partner_bank`, and why each field exists:
172
- *
173
- * - **Nothing to poll.** No status enquiry, no list, no read-back, no
174
- * webhook, on any line. `notificationMechanisms` is empty by evidence, and
175
- * the issuance response is the ONLY carrier of the policy identity -- so
176
- * the transport must capture it durably BEFORE acknowledging the caller.
177
- * - **No idempotency header exists anywhere in the corpus.** Dedupe runs on
178
- * caller-minted business reference numbers replayed verbatim at each step,
179
- * which is why the spine is a reference chain rather than a key.
180
- * - **Auth is a bespoke credential exchange**, not OAuth2 client
181
- * credentials: username/password against the provider's own login route
182
- * yields a bearer with no documented lifetime, refresh, or scope.
183
- * - **Errors are prose.** No machine error code appears anywhere, so an
184
- * adapter cannot build a decline taxonomy from the docs alone.
185
- */
186
- export interface InsuranceDistributorAdapterProfile {
187
- readonly kind: "insurance_distributor";
188
- readonly wireCodec: "rest_json";
189
- readonly auth: {
190
- readonly envelope: ProviderAuthEnvelope;
191
- /** How the bearer is obtained, when the provider documents it at all. */
192
- readonly acquisition: "password_login" | "out_of_band";
193
- readonly signing: "none";
194
- };
195
- /**
196
- * Envelope class per DOCUMENTED operation name, because one provider answers
197
- * in more than one shape. Keys are the provider's own operation names, not
198
- * platform operations -- this profile describes a surface no adapter has
199
- * integrated yet.
200
- */
201
- readonly envelopes: Readonly<Record<string, ProviderResponseEnvelope>>;
202
- /**
203
- * The correlation chain that stands in for an idempotency key: which
204
- * references the CALLER mints and which the provider mints, in the order the
205
- * exchange replays them.
206
- */
207
- readonly referenceChain: {
208
- readonly clientMinted: readonly string[];
209
- readonly providerMinted: readonly string[];
210
- };
211
- readonly notificationMechanisms: readonly ProviderNotificationMechanism[];
212
- /** Whether the provider publishes machine-readable failure codes. */
213
- readonly errorModel: "prose_only" | "coded";
214
- /** True when the command response is the only carrier of the created fact. */
215
- readonly captureBeforeAcknowledge: boolean;
79
+ readonly subjectRequirements?: readonly UdlObjectField[];
216
80
  }
217
81
  /** Bind a product vocabulary once, then retain literal adapter registry types. */
218
82
  export declare function createProviderAdapterRegistry<Vocabulary extends ProviderAdapterVocabulary>(): <const Adapters extends readonly ProviderAdapter<Vocabulary>[]>(adapters: Adapters) => Adapters;
83
+ export type AdapterConformanceCode = "operation_map_empty" | "resource_binding_missing" | "declaration_invalid";
84
+ export interface AdapterConformanceFinding {
85
+ readonly code: AdapterConformanceCode;
86
+ readonly message: string;
87
+ readonly path: string;
88
+ }
89
+ export declare function adapterConformanceFindings<Vocabulary extends ProviderAdapterVocabulary>(adapter: ProviderAdapter<Vocabulary>): readonly AdapterConformanceFinding[];
219
90
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,45 @@
1
- import { providerKeyPattern, statementAvailabilities, } from "./vocabulary.js";
2
- export { certifyPartnerBankAdapter, partnerBankConformanceCodes, partnerBankConformanceFindings, } from "./conformance.js";
3
- export { statementAvailabilities };
1
+ import * as z from "zod";
2
+ import { udlObjectFieldSchema } from "@hyperscale0/udl";
3
+ import { providerKeyPattern, subjectFieldTypes, } from "./vocabulary.js";
4
+ export { subjectFieldTypes };
5
+ const boundaryId = z.string().min(1).max(240);
6
+ export const boundaryOutcomeSchema = z.enum([
7
+ "acknowledged",
8
+ "unknown",
9
+ "confirmed",
10
+ "rejected",
11
+ ]);
12
+ export const boundaryInstructionSchema = z
13
+ .strictObject({
14
+ id: boundaryId,
15
+ environment: z.enum(["sandbox", "live"]),
16
+ tenantId: boundaryId,
17
+ productId: boundaryId,
18
+ productBuildId: boundaryId,
19
+ buildDigest: boundaryId,
20
+ target: z
21
+ .strictObject({
22
+ kind: boundaryId,
23
+ attachment: boundaryId,
24
+ instanceId: boundaryId,
25
+ })
26
+ .readonly(),
27
+ sourceAccountId: boundaryId,
28
+ destinationAccountId: boundaryId,
29
+ amount: z.string().regex(/^[1-9][0-9]{0,17}$/),
30
+ currency: z.string().regex(/^[A-Z]{3}$/),
31
+ adapter: z.string().regex(providerKeyPattern),
32
+ })
33
+ .readonly();
34
+ export const boundaryObservationSchema = z
35
+ .strictObject({
36
+ adapter: z.string().regex(providerKeyPattern),
37
+ externalReference: boundaryId,
38
+ instructionId: boundaryId,
39
+ outcome: boundaryOutcomeSchema,
40
+ observedAt: z.iso.datetime(),
41
+ })
42
+ .readonly();
4
43
  /** Validate an adapter registry once at load, then preserve its exact type. */
5
44
  function defineProviderAdapters(adapters) {
6
45
  const identities = new Set();
@@ -13,12 +52,7 @@ function defineProviderAdapters(adapters) {
13
52
  throw new Error(`duplicate provider adapter ${identity}`);
14
53
  }
15
54
  identities.add(identity);
16
- validateConfig(adapter.provider, adapter.config);
17
55
  validateOperations(adapter);
18
- validateWebhooks(adapter);
19
- if (adapter.profile) {
20
- validatePartnerBankProfile(adapter.provider, adapter.profile);
21
- }
22
56
  }
23
57
  return adapters;
24
58
  }
@@ -26,119 +60,77 @@ function defineProviderAdapters(adapters) {
26
60
  export function createProviderAdapterRegistry() {
27
61
  return (adapters) => defineProviderAdapters(adapters);
28
62
  }
29
- function validateConfig(provider, config) {
30
- if (config.baseUrl) {
31
- try {
32
- new URL(config.baseUrl);
33
- }
34
- catch {
35
- throw new Error(`${provider} config.baseUrl must be an absolute URL`);
36
- }
37
- }
38
- const credential = config.credentialRef;
39
- if (!credential)
40
- return;
41
- if (credential.source === "environment") {
42
- assertNonblank(provider, "config.credentialRef.name", credential.name);
43
- return;
44
- }
45
- assertNonblank(provider, "config.credentialRef.account", credential.account);
46
- assertNonblank(provider, "config.credentialRef.service", credential.service);
47
- }
48
63
  function validateOperations(adapter) {
49
64
  for (const [operation, binding] of Object.entries(adapter.operationMap)) {
50
65
  if (operation !== binding.operation) {
51
66
  throw new Error(`${adapter.provider} operationMap key ${operation} must match operation ${binding.operation}`);
52
67
  }
53
68
  assertNonblank(adapter.provider, `${operation}.resourceIdPath`, binding.resourceIdPath);
54
- if (binding.statusEnquiry) {
55
- assertNonblank(adapter.provider, `${operation}.statusEnquiry.pathTemplate`, binding.statusEnquiry.pathTemplate);
56
- const placeholder = binding.statusEnquiry.keys === "instruction_id"
57
- ? "{instructionId}"
58
- : "{providerReference}";
59
- if (!binding.statusEnquiry.pathTemplate.includes(placeholder)) {
60
- throw new Error(`${adapter.provider} ${operation}.statusEnquiry.pathTemplate must include ${placeholder}`);
69
+ if (binding.subjectRequirements !== undefined) {
70
+ if (!Array.isArray(binding.subjectRequirements) ||
71
+ binding.subjectRequirements.length > 128) {
72
+ throw new Error(`${adapter.provider} ${operation}.subjectRequirements must be an array of at most 128 fields`);
73
+ }
74
+ for (const [index, requirement,] of binding.subjectRequirements.entries()) {
75
+ const result = udlObjectFieldSchema.safeParse(requirement);
76
+ if (!result.success || result.data.optional) {
77
+ throw new Error(`${adapter.provider} ${operation}.subjectRequirements[${index}] does not match UDL object field schema: ${result.success ? "action requirements cannot be optional" : result.error.message}`);
78
+ }
79
+ if (!subjectFieldTypes.includes(requirement.type)) {
80
+ throw new Error(`${adapter.provider} ${operation}.subjectRequirements[${index}] unknown subject field type ${requirement.type}`);
81
+ }
61
82
  }
62
83
  }
63
84
  }
64
85
  }
65
- function validateWebhooks(adapter) {
66
- for (const [event, plan] of Object.entries(adapter.webhookMap)) {
67
- if (event !== plan.eventKind) {
68
- throw new Error(`${adapter.provider} webhookMap key ${event} must match eventKind ${plan.eventKind}`);
69
- }
86
+ function assertNonblank(provider, field, value) {
87
+ if (value.trim().length === 0) {
88
+ throw new Error(`${provider} ${field} must not be blank`);
70
89
  }
71
90
  }
72
- function validatePartnerBankProfile(provider, profile) {
73
- if (!profile.notificationMechanisms.includes("poll")) {
74
- throw new Error(`${provider} partner-bank profile must support polling`);
75
- }
76
- if (Object.keys(profile.lifecycleVocabulary).length === 0) {
77
- throw new Error(`${provider} partner-bank profile has no lifecycle vocabulary`);
78
- }
79
- assertUnique(provider, "statementFormats", profile.statementFormats);
80
- assertUnique(provider, "notificationMechanisms", profile.notificationMechanisms);
81
- assertUnique(provider, "limitFacts.dimensions", profile.limitFacts.dimensions);
82
- const spine = profile.idempotencySpine;
83
- // The dedupe keys widened from a fixed triple to a list, so the list now
84
- // carries the guarantees the tuple used to give for free.
85
- if (spine.dedupeKeys.length === 0) {
86
- throw new Error(`${provider} idempotencySpine.dedupeKeys is empty`);
87
- }
88
- assertUnique(provider, "idempotencySpine.dedupeKeys", spine.dedupeKeys);
89
- const spineLimits = {
90
- endToEndId: spine.endToEndId,
91
- instructionId: spine.instructionId,
92
- paymentReference: spine.paymentReference,
93
- };
94
- for (const [field, limit] of Object.entries(spineLimits)) {
95
- assertPositiveInteger(provider, `idempotencySpine.${field}.maxLength`, limit.maxLength);
96
- }
97
- const windows = profile.windows;
98
- if (!/^\d{2}:\d{2}$/.test(windows.paymentCutoff.time)) {
99
- throw new Error(`${provider} windows.paymentCutoff.time must be HH:MM`);
100
- }
101
- // Intl.DateTimeFormat treats an undefined timeZone as the runtime default,
102
- // so presence must be checked before validity.
103
- const timeZone = windows.paymentCutoff.timeZone;
104
- if (typeof timeZone !== "string") {
105
- throw new Error(`${provider} windows.paymentCutoff.timeZone must be a valid IANA time zone`);
106
- }
91
+ export function adapterConformanceFindings(adapter) {
92
+ const findings = bindingFindings(adapter);
93
+ if (Object.keys(adapter.operationMap).length === 0)
94
+ findings.unshift({
95
+ code: "operation_map_empty",
96
+ path: "operationMap",
97
+ message: "An adapter must declare an operation.",
98
+ });
107
99
  try {
108
- new Intl.DateTimeFormat("en", { timeZone });
109
- }
110
- catch {
111
- throw new Error(`${provider} windows.paymentCutoff.timeZone must be a valid IANA time zone`);
100
+ createProviderAdapterRegistry()([adapter]);
112
101
  }
113
- assertPositiveInteger(provider, "windows.valueDateMaxDaysAhead", windows.valueDateMaxDaysAhead);
114
- for (const [format, window] of Object.entries(windows.statements)) {
115
- // TS requires availability, but a plain-JS consumer can send a window
116
- // without one, and an availability-less window is unschedulable.
117
- if (!statementAvailabilities.includes(window.availability)) {
118
- throw new Error(`${provider} windows.statements.${format}.availability must be one ` +
119
- `of ${statementAvailabilities.join(", ")}`);
120
- }
121
- if (window.lookbackDays !== undefined) {
122
- assertPositiveInteger(provider, `windows.statements.${format}.lookbackDays`, window.lookbackDays);
123
- }
124
- if (window.statementDaysPerRequest !== undefined) {
125
- assertPositiveInteger(provider, `windows.statements.${format}.statementDaysPerRequest`, window.statementDaysPerRequest);
126
- }
102
+ catch (error) {
103
+ findings.push({
104
+ code: "declaration_invalid",
105
+ path: "",
106
+ message: error instanceof Error ? error.message : String(error),
107
+ });
127
108
  }
109
+ return findings;
128
110
  }
129
- function assertPositiveInteger(provider, field, value) {
130
- if (!Number.isInteger(value) || value <= 0) {
131
- throw new Error(`${provider} ${field} must be a positive integer`);
132
- }
133
- }
134
- function assertUnique(provider, field, values) {
135
- if (new Set(values).size !== values.length) {
136
- throw new Error(`${provider} ${field} contains duplicates`);
137
- }
138
- }
139
- function assertNonblank(provider, field, value) {
140
- if (value.trim().length === 0) {
141
- throw new Error(`${provider} ${field} must not be blank`);
111
+ function bindingFindings(adapter) {
112
+ const findings = [];
113
+ const bindings = adapter.bindings;
114
+ const claimed = new Set();
115
+ const require = (kind, path) => {
116
+ if (bindings[kind] !== undefined || claimed.has(kind))
117
+ return;
118
+ claimed.add(kind);
119
+ // Provider facts commit onto platform resources through a declared match
120
+ // mode; a resource the adapter settles without one has no lawful way to
121
+ // bind the observation.
122
+ findings.push({
123
+ code: "resource_binding_missing",
124
+ message: `resource kind ${kind} is settled by this adapter but declares no ` +
125
+ "binding mode, so provider facts have no lawful way to commit",
126
+ path,
127
+ });
128
+ };
129
+ for (const binding of Object.values(adapter.operationMap)) {
130
+ if (!binding)
131
+ continue;
132
+ require(binding.resourceKind, `bindings.${binding.resourceKind}`);
142
133
  }
134
+ return findings;
143
135
  }
144
136
  //# sourceMappingURL=index.js.map