@agent-cards/sdk 0.20.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 (121) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/PREFLIGHT.md +314 -0
  3. package/README.md +864 -0
  4. package/dist/adyen-merchant-hosted.generated.d.ts +277 -0
  5. package/dist/adyen-merchant-hosted.generated.js +1902 -0
  6. package/dist/adyen.generated.d.ts +24 -0
  7. package/dist/adyen.generated.js +64 -0
  8. package/dist/agentcard.d.ts +72 -0
  9. package/dist/agentcard.js +71 -0
  10. package/dist/api-module.d.ts +54 -0
  11. package/dist/api-module.js +85 -0
  12. package/dist/attachment.d.ts +11 -0
  13. package/dist/attachment.js +50 -0
  14. package/dist/braintree.d.ts +2 -0
  15. package/dist/braintree.generated.d.ts +10 -0
  16. package/dist/braintree.generated.js +302 -0
  17. package/dist/braintree.js +2 -0
  18. package/dist/builtin-registry.generated.d.ts +2 -0
  19. package/dist/builtin-registry.generated.js +1 -0
  20. package/dist/buy.d.ts +374 -0
  21. package/dist/buy.js +314 -0
  22. package/dist/card-fields.generated.d.ts +3 -0
  23. package/dist/card-fields.generated.js +88 -0
  24. package/dist/cdp.d.ts +202 -0
  25. package/dist/cdp.js +2540 -0
  26. package/dist/checkout-com.generated.d.ts +4 -0
  27. package/dist/checkout-com.generated.js +183 -0
  28. package/dist/client.d.ts +929 -0
  29. package/dist/client.js +1896 -0
  30. package/dist/cse-body.d.ts +25 -0
  31. package/dist/cse-body.js +41 -0
  32. package/dist/fiserv.d.ts +65 -0
  33. package/dist/fiserv.generated.d.ts +73 -0
  34. package/dist/fiserv.generated.js +830 -0
  35. package/dist/fiserv.js +104 -0
  36. package/dist/hosted-form.d.ts +44 -0
  37. package/dist/hosted-form.js +78 -0
  38. package/dist/index.d.ts +37 -0
  39. package/dist/index.js +26 -0
  40. package/dist/languages.d.ts +23 -0
  41. package/dist/languages.js +76 -0
  42. package/dist/lifecycle.d.ts +235 -0
  43. package/dist/lifecycle.js +405 -0
  44. package/dist/mercado-checkout.d.ts +20 -0
  45. package/dist/mercado-checkout.generated.d.ts +52 -0
  46. package/dist/mercado-checkout.generated.js +198 -0
  47. package/dist/mercado-checkout.js +108 -0
  48. package/dist/merchant-handoff.d.ts +54 -0
  49. package/dist/merchant-handoff.js +100 -0
  50. package/dist/merchant-hosted.d.ts +140 -0
  51. package/dist/merchant-hosted.js +170 -0
  52. package/dist/merchant-total-watch.d.ts +115 -0
  53. package/dist/merchant-total-watch.js +268 -0
  54. package/dist/merchant-total.d.ts +257 -0
  55. package/dist/merchant-total.js +383 -0
  56. package/dist/owned-shop.generated.d.ts +24 -0
  57. package/dist/owned-shop.generated.js +108 -0
  58. package/dist/paysafe.generated.d.ts +12 -0
  59. package/dist/paysafe.generated.js +87 -0
  60. package/dist/playwright.d.ts +3 -0
  61. package/dist/playwright.js +3 -0
  62. package/dist/pre-claim.d.ts +123 -0
  63. package/dist/pre-claim.js +386 -0
  64. package/dist/preflight-capabilities.generated.d.ts +1311 -0
  65. package/dist/preflight-capabilities.generated.js +2007 -0
  66. package/dist/preflight-catalog.json +5031 -0
  67. package/dist/preflight-playwright.d.ts +34 -0
  68. package/dist/preflight-playwright.js +355 -0
  69. package/dist/preflight-schemas.json +1144 -0
  70. package/dist/preflight.d.ts +1 -0
  71. package/dist/preflight.generated.d.ts +2023 -0
  72. package/dist/preflight.generated.js +604 -0
  73. package/dist/preflight.js +2 -0
  74. package/dist/preparation.d.ts +38 -0
  75. package/dist/preparation.js +191 -0
  76. package/dist/prepared-processor.d.ts +43 -0
  77. package/dist/prepared-processor.js +172 -0
  78. package/dist/recurly.generated.d.ts +1 -0
  79. package/dist/recurly.generated.js +87 -0
  80. package/dist/registry.d.ts +145 -0
  81. package/dist/registry.js +338 -0
  82. package/dist/requests.d.ts +66 -0
  83. package/dist/requests.js +76 -0
  84. package/dist/spreedly.generated.d.ts +10 -0
  85. package/dist/spreedly.generated.js +332 -0
  86. package/dist/stripe-checkout.d.ts +81 -0
  87. package/dist/stripe-checkout.generated.d.ts +82 -0
  88. package/dist/stripe-checkout.generated.js +1153 -0
  89. package/dist/stripe-checkout.js +140 -0
  90. package/dist/substitute.d.ts +38 -0
  91. package/dist/substitute.js +23 -0
  92. package/dist/substitutions.generated.d.ts +11 -0
  93. package/dist/substitutions.generated.js +818 -0
  94. package/dist/voice-pkce.d.ts +49 -0
  95. package/dist/voice-pkce.js +147 -0
  96. package/dist/voice-retry.d.ts +6 -0
  97. package/dist/voice-retry.js +27 -0
  98. package/dist/voice-stream.generated.d.ts +118 -0
  99. package/dist/voice-stream.generated.js +170 -0
  100. package/dist/voice-types.d.ts +708 -0
  101. package/dist/voice-types.js +16 -0
  102. package/dist/voice.d.ts +365 -0
  103. package/dist/voice.js +807 -0
  104. package/dist/webhooks.d.ts +87 -0
  105. package/dist/webhooks.js +136 -0
  106. package/examples/existing-browser.mjs +62 -0
  107. package/examples/preflight/classify-direct.mjs +21 -0
  108. package/examples/preflight/classify-kernel.mjs +30 -0
  109. package/examples/preflight/inspect-browser.mjs +44 -0
  110. package/examples/preflight/kernel-native/README.md +112 -0
  111. package/examples/preflight/kernel-native/documented-adapters.json +113 -0
  112. package/examples/preflight/kernel-native/inventory.json +242 -0
  113. package/examples/preflight/kernel-native/qualification.mjs +182 -0
  114. package/examples/preflight/kernel-profile.empty.json +11 -0
  115. package/examples/preflight/mollie-hosted.observations.json +23 -0
  116. package/examples/preflight/mollie-hosted.result.json +103 -0
  117. package/examples/preflight/stripe-script.direct.result.json +92 -0
  118. package/examples/preflight/stripe-script.observations.json +16 -0
  119. package/examples/preflight/stripe-script.result.json +87 -0
  120. package/package.json +53 -0
  121. package/readme.test.mjs +53 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Which outbound requests carry a raw card, and where the card sits inside them.
3
+ *
4
+ * This is the only merchant-specific knowledge in the system, and it is keyed by
5
+ * PSP rather than by merchant: every Shopify store shares one entry, every Stripe
6
+ * site shares another. The list is served from the Agentcard API at runtime so
7
+ * integrators pick up new processors without shipping a release.
8
+ */
9
+ export type Encoding = 'json' | 'form';
10
+ /**
11
+ * How a paused request gets its card. `token` (the default when absent): the
12
+ * cardholder's device calls the processor and the runtime replays the
13
+ * processor's response. `cse`: the processor encrypts the card in-page
14
+ * (Adyen); the device returns ciphertext and the runtime substitutes it into
15
+ * the paused body and lets the request continue. `hosted_form`: the device
16
+ * submits the processor's own form and the paused request resolves with no
17
+ * processor answer (served only to SDKs that ask for it on sync).
18
+ */
19
+ export type CheckoutMode = 'token' | 'cse' | 'hosted_form';
20
+ export interface Recognizer {
21
+ /** Stable id, e.g. "shopify" */
22
+ psp: string;
23
+ /** Matches the tokenization endpoint. */
24
+ match: RegExp;
25
+ /**
26
+ * Anchored (`^…$`) regex SOURCES for the hostnames this PSP may ever receive
27
+ * a card on — the server's own allowlist, served next to `match` and carried
28
+ * through syncRegistry() verbatim.
29
+ *
30
+ * Strings, not RegExps, because that is the wire shape and because nothing
31
+ * here matches with them: they exist so a transport that can only pre-filter
32
+ * by URL (CDP's `Fetch.enable`, which takes globs) can be armed from the
33
+ * registry instead of a hardcoded list. Optional so a hand-built registry
34
+ * (tests, pinning) still type-checks.
35
+ */
36
+ hosts?: string[];
37
+ encoding: Encoding;
38
+ /**
39
+ * Request headers that must be replayed verbatim for the merchant to accept
40
+ * the call (signatures, client tokens). Matched case-insensitively.
41
+ */
42
+ passthroughHeaders: RegExp[];
43
+ /**
44
+ * Form keys that carry the card number. When present, a request on this
45
+ * recognizer's endpoints is a card request only if its body carries one of
46
+ * them: a Stripe confirmation that pays with a method the page already
47
+ * created (`payment_method=pm_...`) carries none, so there is nothing for
48
+ * the cardholder to approve. Served by the API and carried through
49
+ * syncRegistry() verbatim.
50
+ */
51
+ cardFields?: string[];
52
+ /**
53
+ * The JSON counterpart of cardFields, read on a JSON recognizer only: paths
54
+ * of object keys and array indices to the members that carry the card
55
+ * number or the encrypted card, such as ['payment_source', 'card', 'number'].
56
+ * A request is a card request only if one of them holds a non-empty string
57
+ * or a number, so a wallet or saved-card payment sent to the same endpoint
58
+ * is not. Carried through syncRegistry() verbatim.
59
+ */
60
+ jsonCardFields?: (string | number)[][];
61
+ /**
62
+ * With cardFields or jsonCardFields: the endpoints (regex source over
63
+ * hostname + pathname, anchored) where a request without a card is routine
64
+ * and never ours, so it continues untouched; a request without a card
65
+ * anywhere else is refused.
66
+ */
67
+ passWithoutCard?: string;
68
+ /**
69
+ * True when the card is encrypted inside the page before the request leaves.
70
+ * A digit swap is useless here; the vault must re-run the PSP's client-side
71
+ * crypto instead. Refused locally unless the entry also carries
72
+ * `mode: 'cse'`, which says the vault does exactly that.
73
+ */
74
+ clientSideEncrypted?: boolean;
75
+ /** How the card reaches the processor; absent means 'token'. Carried verbatim by syncRegistry. */
76
+ mode?: CheckoutMode;
77
+ /**
78
+ * Dark launch: a capability name the API attaches to an entry it serves only
79
+ * to a client that declared it (?capabilities=). Absent on every entry in
80
+ * BUILTIN_REGISTRY, because a gated entry is left out of the built-in
81
+ * fallback. Carried verbatim by syncRegistry for a client that does declare
82
+ * it, so the SDK never has to act on it.
83
+ */
84
+ capability?: string;
85
+ /**
86
+ * True when this processor cannot be taken over on an ordinary authorization:
87
+ * the cardholder must have approved a prepare() first. authorize() refuses
88
+ * such a request locally, before it prompts, when no preparation is active
89
+ * (PreparationRequiredError), and the API answers 409 preparation_required.
90
+ * Absent on every BUILTIN_REGISTRY entry today and carried verbatim by
91
+ * syncRegistry for a build that reaches a processor which sets it.
92
+ */
93
+ preparationRequired?: boolean;
94
+ /**
95
+ * Anchored regex SOURCES (a subset of `hosts`) marking the vendor's
96
+ * SANDBOX/TEST hostnames, so the backend and the device can tell a test host
97
+ * from a live one. Carried verbatim by syncRegistry; the SDK never matches
98
+ * with them (recognition still uses `hosts`), so it acts on neither this nor
99
+ * the pin below. Present in BUILTIN_REGISTRY for the host-separated processors.
100
+ */
101
+ sandboxHosts?: string[];
102
+ /**
103
+ * True when a processor's host environment is enforced: the API refuses a
104
+ * create whose host environment disagrees with the row's test/live nature, and
105
+ * the device sends only documented test cards to a sandbox host. Absent on
106
+ * every entry today; carried verbatim by syncRegistry, and the SDK never acts
107
+ * on it.
108
+ */
109
+ hostEnvironmentPinned?: boolean;
110
+ }
111
+ import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
112
+ export { BUILTIN_REGISTRY };
113
+ /** How the recogniser reads a URL. `sandbox`: the client's credentials are sandbox credentials. */
114
+ export interface RecognizerOptions {
115
+ /**
116
+ * With sandbox credentials a local fixture store is a store too: `http://127.0.0.1[:port]` and
117
+ * `http://localhost[:port]` are recognised by their path against each processor's endpoints, so a
118
+ * request the fixture posts to itself pauses as one to the processor would. Production is unchanged:
119
+ * https only, no port.
120
+ */
121
+ sandbox?: boolean;
122
+ }
123
+ /** True for a plain-http loopback address (`127.0.0.1` or `localhost`, any port); the only http a sandbox recogniser admits. */
124
+ export declare function isLoopbackUrl(parsed: URL): boolean;
125
+ /** The glob patterns that pause every loopback request under a sandbox client; `isCardRequest` is the exact check. */
126
+ export declare const LOOPBACK_URL_PATTERNS: readonly string[];
127
+ export declare function findRecognizer(url: string, registry?: Recognizer[], opts?: RecognizerOptions): Recognizer | null;
128
+ /**
129
+ * True when this processor cannot be taken over on an ordinary authorization:
130
+ * the cardholder must have approved a prepare() first (the `preparationRequired`
131
+ * field above). authorize() applies this before it prompts and refuses with
132
+ * PreparationRequiredError when no preparation is active; the API answers 409
133
+ * preparation_required for the same case. This is the SAME rule payment-core's
134
+ * recognizerRequiresPreparation applies on the server, a strict `=== true`, so a
135
+ * value that rode in over the wire (syncRegistry spreads the served JSON) cannot
136
+ * turn a truthy string into a gate. Absent on every BUILTIN_REGISTRY entry, so
137
+ * this is false for all 24 today.
138
+ */
139
+ export declare function recognizerRequiresPreparation(rec: Pick<Recognizer, 'preparationRequired'>): boolean;
140
+ /**
141
+ * Glob url patterns covering every host in `registry` — what to hand
142
+ * `Fetch.enable` so a card request is ever paused. Deliberately a superset of
143
+ * the recognizers; isCardRequest is the exact check.
144
+ */
145
+ export declare function cardUrlPatterns(registry: Recognizer[]): string[];
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Which outbound requests carry a raw card, and where the card sits inside them.
3
+ *
4
+ * This is the only merchant-specific knowledge in the system, and it is keyed by
5
+ * PSP rather than by merchant: every Shopify store shares one entry, every Stripe
6
+ * site shares another. The list is served from the Agentcard API at runtime so
7
+ * integrators pick up new processors without shipping a release.
8
+ */
9
+ // Pinned fallback generated from the single shared processor catalog.
10
+ import { BUILTIN_REGISTRY } from './builtin-registry.generated.js';
11
+ export { BUILTIN_REGISTRY };
12
+ /** True for a plain-http loopback address (`127.0.0.1` or `localhost`, any port); the only http a sandbox recogniser admits. */
13
+ export function isLoopbackUrl(parsed) {
14
+ return parsed.protocol === 'http:' && (parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost') && !parsed.username && !parsed.password;
15
+ }
16
+ /** The glob patterns that pause every loopback request under a sandbox client; `isCardRequest` is the exact check. */
17
+ export const LOOPBACK_URL_PATTERNS = ['http://127.0.0.1*', 'http://localhost*'];
18
+ /**
19
+ * The literal hostnames an anchored host regex source names (`^api\\.stripe\\.com$` is `api.stripe.com`);
20
+ * a source with any other regex construct names none. A loopback path is judged as if it were on each.
21
+ */
22
+ function literalHosts(sources) {
23
+ const out = [];
24
+ for (const source of sources ?? []) {
25
+ const bare = source.replace(/^\^/, '').replace(/\$$/, '');
26
+ const literal = bare.replace(/\\\./g, '.');
27
+ if (/^[a-z0-9.-]+$/i.test(literal) && !/[\\^$*+?()[\]{}|]/.test(bare.replace(/\\\./g, '')))
28
+ out.push(literal.toLowerCase());
29
+ }
30
+ return out;
31
+ }
32
+ export function findRecognizer(url, registry = BUILTIN_REGISTRY, opts = {}) {
33
+ // Match the way the SERVER does, on the PARSED url — never as a substring of
34
+ // the raw string. `match` is unanchored, so a raw-string test classifies any
35
+ // URL that merely CONTAINS a tokenizer endpoint as a card request:
36
+ // https://evil.com/?next=https://api.stripe.com/v1/tokens
37
+ // The server refuses that (its hosts[] are anchored against URL.hostname), so
38
+ // the request could not be intercepted anyway — but the SDK had already paused
39
+ // it, and a pause it cannot complete becomes an ABORT. That turns an
40
+ // integrator's unrelated POST into a failed request.
41
+ //
42
+ // hosts[] is synced from the API precisely so the client can apply the server's
43
+ // own allowlist. Use it when present, and fall back to matching `match`
44
+ // against "hostname + pathname" (not the query) for a registry served by an
45
+ // older API that predates the field.
46
+ let parsed;
47
+ try {
48
+ parsed = new URL(url);
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ // Decision 178: with sandbox credentials a loopback fixture store is judged by its path, as if it were
54
+ // on each processor's own host; nothing else about the rule changes, and production never gets here.
55
+ if (opts.sandbox && isLoopbackUrl(parsed)) {
56
+ return (registry.find((r) => {
57
+ const anchored = new RegExp(`^(?:${r.match.source})$`, 'i');
58
+ return literalHosts(r.hosts).some((host) => anchored.test(`${host}${parsed.pathname}`));
59
+ }) ?? null);
60
+ }
61
+ if (parsed.protocol !== 'https:' || parsed.port || parsed.username || parsed.password)
62
+ return null;
63
+ const hostPath = `${parsed.hostname.toLowerCase()}${parsed.pathname}`;
64
+ return (registry.find((r) => {
65
+ if (r.hosts && r.hosts.length > 0) {
66
+ const hostAllowed = r.hosts.some((h) => new RegExp(h, 'i').test(parsed.hostname));
67
+ if (!hostAllowed)
68
+ return false;
69
+ }
70
+ // FULLY ANCHORED, exactly as the server does it. Unanchored, any EXTENSION
71
+ // of a valid path is admitted on a legitimate processor host
72
+ // (api.stripe.com/v1/tokens/anything), which the service then refuses —
73
+ // and a pause the SDK cannot complete becomes an abort.
74
+ return new RegExp(`^(?:${r.match.source})$`, 'i').test(hostPath);
75
+ }) ?? null);
76
+ }
77
+ /**
78
+ * True when this processor cannot be taken over on an ordinary authorization:
79
+ * the cardholder must have approved a prepare() first (the `preparationRequired`
80
+ * field above). authorize() applies this before it prompts and refuses with
81
+ * PreparationRequiredError when no preparation is active; the API answers 409
82
+ * preparation_required for the same case. This is the SAME rule payment-core's
83
+ * recognizerRequiresPreparation applies on the server, a strict `=== true`, so a
84
+ * value that rode in over the wire (syncRegistry spreads the served JSON) cannot
85
+ * turn a truthy string into a gate. Absent on every BUILTIN_REGISTRY entry, so
86
+ * this is false for all 24 today.
87
+ */
88
+ export function recognizerRequiresPreparation(rec) {
89
+ return rec.preparationRequired === true;
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Registry -> CDP url patterns
93
+ //
94
+ // `Fetch.enable` takes GLOB url patterns (`*` = any run of characters, `?` =
95
+ // exactly one, `\` escapes), never regexes, so the registry's anchored host
96
+ // regexes have to be widened into globs before a raw-CDP integrator can arm
97
+ // interception with them.
98
+ //
99
+ // These patterns are only a COARSE pre-filter. Every request they pause is
100
+ // re-checked with the real recognizer (VaultClient.isCardRequest) before
101
+ // anything is intercepted, so a pattern that is too BROAD costs one extra
102
+ // continueRequest round-trip, while one that is too NARROW silently drops the
103
+ // card request on the floor and the shopper pays with their own card. Every
104
+ // conversion below therefore WIDENS: anything not expressible as a glob
105
+ // (character classes, groups, quantifiers, lookarounds) becomes `*`.
106
+ // ---------------------------------------------------------------------------
107
+ /**
108
+ * Glob url patterns covering every host in `registry` — what to hand
109
+ * `Fetch.enable` so a card request is ever paused. Deliberately a superset of
110
+ * the recognizers; isCardRequest is the exact check.
111
+ */
112
+ export function cardUrlPatterns(registry) {
113
+ const out = new Set();
114
+ for (const rec of registry ?? []) {
115
+ for (const pattern of recognizerUrlPatterns(rec))
116
+ out.add(pattern);
117
+ }
118
+ return [...out];
119
+ }
120
+ function recognizerUrlPatterns(rec) {
121
+ const hosts = Array.isArray(rec?.hosts) ? rec.hosts.filter((h) => typeof h === 'string') : [];
122
+ if (hosts.length > 0) {
123
+ // Host text ANYWHERE in the URL, not `*://host/*`. Two reasons: a URL with
124
+ // userinfo (`https://u:p@host/…`) still has `host` as its hostname so the
125
+ // registry matches it, and isCardRequest tests `match` unanchored against
126
+ // the raw URL, so a URL merely carrying the host in its query also
127
+ // "matches" client-side. Anchoring the scheme would arm narrower than
128
+ // either check.
129
+ //
130
+ // Lowercased because CDP matches these globs case-SENSITIVELY against the
131
+ // canonical url, and a canonical url's authority is always lowercase (the
132
+ // URL parser folds it before the request is ever issued) — the same
133
+ // normalization resolveRecognizerForUrl does with url.hostname. Do not
134
+ // "fix" this by wildcarding the letters: `*???.??????.???*` matches most of
135
+ // the web and would pause every request on the page.
136
+ return hosts.flatMap((h) => regexSourceToGlobs(h)).map((g) => wrap(g).toLowerCase());
137
+ }
138
+ // No hosts[] — a registry pinned before the field existed, or hand-built.
139
+ // Fall back to widening `match`, which is the predicate isCardRequest
140
+ // actually applies. Path case can matter to a glob (CDP matches
141
+ // case-sensitively) while a hostname is always lowercased by the URL parser,
142
+ // so arm both spellings rather than guess.
143
+ return regexSourceToGlobs(rec?.match?.source ?? '')
144
+ .map(wrap)
145
+ .flatMap((g) => (g === g.toLowerCase() ? [g] : [g, g.toLowerCase()]));
146
+ }
147
+ /** `a|b` at the top level is two independent hostnames, so it is two globs. */
148
+ function regexSourceToGlobs(source) {
149
+ return splitTopLevelAlternation(source).map(branchToGlob);
150
+ }
151
+ function splitTopLevelAlternation(source) {
152
+ const parts = [];
153
+ let depth = 0;
154
+ let inClass = false;
155
+ let start = 0;
156
+ for (let i = 0; i < source.length; i++) {
157
+ const c = source[i];
158
+ if (c === '\\') {
159
+ i++;
160
+ continue;
161
+ }
162
+ if (inClass) {
163
+ if (c === ']')
164
+ inClass = false;
165
+ continue;
166
+ }
167
+ if (c === '[') {
168
+ inClass = true;
169
+ continue;
170
+ }
171
+ if (c === '(') {
172
+ depth++;
173
+ continue;
174
+ }
175
+ if (c === ')') {
176
+ depth = Math.max(0, depth - 1);
177
+ continue;
178
+ }
179
+ if (c === '|' && depth === 0) {
180
+ parts.push(source.slice(start, i));
181
+ start = i + 1;
182
+ }
183
+ }
184
+ parts.push(source.slice(start));
185
+ return parts;
186
+ }
187
+ /**
188
+ * One alternation-free regex source -> one glob. Literal characters survive;
189
+ * every construct that can match more than itself collapses to `*`. An empty
190
+ * result is `*` on purpose: an empty regex matches every hostname, so the glob
191
+ * that mirrors it has to match every URL.
192
+ */
193
+ function branchToGlob(branch) {
194
+ const atoms = [];
195
+ let i = 0;
196
+ while (i < branch.length) {
197
+ const c = branch[i];
198
+ let atom;
199
+ if (c === '^' || c === '$') {
200
+ i += 1;
201
+ continue;
202
+ } // anchors constrain nothing in a glob
203
+ if (c === '\\') {
204
+ const next = branch[i + 1] ?? '';
205
+ i += 2;
206
+ // \d \w \s \b … are classes/assertions; only escaped punctuation (\., \-)
207
+ // is a literal character.
208
+ atom = /[A-Za-z0-9]/.test(next) ? '*' : globEscape(next);
209
+ }
210
+ else if (c === '[') {
211
+ i = skipCharClass(branch, i);
212
+ atom = '*';
213
+ }
214
+ else if (c === '(') {
215
+ i = skipGroup(branch, i); // groups, non-capturing groups, lookarounds
216
+ atom = '*';
217
+ }
218
+ else if (c === '.') {
219
+ i += 1;
220
+ atom = '*';
221
+ }
222
+ else if (c === ')' || c === ']') {
223
+ i += 1; // unbalanced closer: ignore rather than throw
224
+ continue;
225
+ }
226
+ else if (quantifierEnd(branch, i) !== null) {
227
+ // A quantifier with nothing in front of it. Widen whatever preceded it.
228
+ i = quantifierEnd(branch, i);
229
+ if (atoms.length > 0)
230
+ atoms[atoms.length - 1] = '*';
231
+ else
232
+ atoms.push('*');
233
+ continue;
234
+ }
235
+ else {
236
+ i += 1;
237
+ atom = globEscape(c);
238
+ }
239
+ // `x?` / `x*` / `x+` / `x{2,3}`: the atom is optional or repeatable, so the
240
+ // only glob that covers every string it accepts is `*`.
241
+ const after = quantifierEnd(branch, i);
242
+ if (after !== null) {
243
+ i = after;
244
+ atom = '*';
245
+ }
246
+ atoms.push(atom);
247
+ }
248
+ return collapse(atoms).join('');
249
+ }
250
+ /** Index just past a quantifier at `i`, or null when there isn't one. */
251
+ function quantifierEnd(source, i) {
252
+ const c = source[i];
253
+ let end;
254
+ if (c === '*' || c === '+' || c === '?') {
255
+ end = i + 1;
256
+ }
257
+ else if (c === '{') {
258
+ const close = source.indexOf('}', i);
259
+ if (close === -1 || !/^\{\d+(,\d*)?\}$/.test(source.slice(i, close + 1)))
260
+ return null;
261
+ end = close + 1;
262
+ }
263
+ else {
264
+ return null;
265
+ }
266
+ if (source[end] === '?')
267
+ end += 1; // lazy quantifier
268
+ return end;
269
+ }
270
+ function skipCharClass(source, i) {
271
+ let j = i + 1;
272
+ if (source[j] === '^')
273
+ j++;
274
+ if (source[j] === ']')
275
+ j++; // a leading `]` is a literal
276
+ for (; j < source.length; j++) {
277
+ if (source[j] === '\\') {
278
+ j++;
279
+ continue;
280
+ }
281
+ if (source[j] === ']')
282
+ return j + 1;
283
+ }
284
+ return source.length;
285
+ }
286
+ function skipGroup(source, i) {
287
+ let depth = 0;
288
+ let inClass = false;
289
+ for (let j = i; j < source.length; j++) {
290
+ const c = source[j];
291
+ if (c === '\\') {
292
+ j++;
293
+ continue;
294
+ }
295
+ if (inClass) {
296
+ if (c === ']')
297
+ inClass = false;
298
+ continue;
299
+ }
300
+ if (c === '[') {
301
+ inClass = true;
302
+ continue;
303
+ }
304
+ if (c === '(') {
305
+ depth++;
306
+ continue;
307
+ }
308
+ if (c === ')') {
309
+ depth--;
310
+ if (depth === 0)
311
+ return j + 1;
312
+ }
313
+ }
314
+ return source.length;
315
+ }
316
+ /** `*` and `?` are the glob's own wildcards; `\` is its escape. */
317
+ function globEscape(ch) {
318
+ return ch === '*' || ch === '?' || ch === '\\' ? `\\${ch}` : ch;
319
+ }
320
+ /** `**` matches exactly what `*` does; keep patterns readable. */
321
+ function collapse(atoms) {
322
+ const out = [];
323
+ for (const a of atoms) {
324
+ if (a === '*' && out[out.length - 1] === '*')
325
+ continue;
326
+ out.push(a);
327
+ }
328
+ return out;
329
+ }
330
+ /** `…host…` -> `*host*`, without doubling a `*` the conversion already emitted. */
331
+ function wrap(glob) {
332
+ let body = glob;
333
+ while (body.startsWith('*'))
334
+ body = body.slice(1);
335
+ while (body.length > 0 && body.endsWith('*') && !body.endsWith('\\*'))
336
+ body = body.slice(0, -1);
337
+ return body.length > 0 ? `*${body}*` : '*';
338
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The request builders registry: `agentcard.checkout.requests.*`. A paused
3
+ * request is what a purchase pauses on: the processor's card request, with
4
+ * a placeholder card the Vault replaces at approval. The registry ships
5
+ * Agentcard's own builders and takes yours at runtime, so a processor or a
6
+ * request shape Agentcard does not build yet needs no release of this
7
+ * package.
8
+ */
9
+ import type { PausedRequest } from './client.js';
10
+ /** One builder: for a processor and a kind of request, turns its input into the paused request. */
11
+ export interface RequestBuilder<I = Record<string, unknown>> {
12
+ /** The processor as the recognisers name it (`stripe`). */
13
+ processor: string;
14
+ /** The request shape (`payment_intent_confirm`, `card_token`, `payment_method`). */
15
+ kind: string;
16
+ /** One line on what the request is, for `list()`. */
17
+ description?: string;
18
+ build(input: I): PausedRequest;
19
+ }
20
+ /** What `build` takes: the processor, the kind, and the builder's own input beside them. */
21
+ export type BuildInput<I = Record<string, unknown>> = {
22
+ processor: string;
23
+ kind: string;
24
+ } & I;
25
+ /**
26
+ * A real test-mode PaymentIntent your server created for one order. The
27
+ * paused request confirms it with a placeholder card, from your own origin;
28
+ * the Vault replaces the card with the person's at approval.
29
+ */
30
+ export interface MintedIntent {
31
+ id: string;
32
+ client_secret: string;
33
+ publishable_key: string;
34
+ }
35
+ /** The paused Stripe request for an order: `POST /v1/payment_intents/{pi}/confirm` with a placeholder card. */
36
+ export declare function stripePaymentIntentConfirmRequest(input: {
37
+ origin: string;
38
+ intent: MintedIntent;
39
+ }): PausedRequest;
40
+ /** The paused Stripe request that tokenizes a card for your server to charge: `POST /v1/tokens` with a placeholder card. */
41
+ export declare function stripeCardTokenRequest(input: {
42
+ origin: string;
43
+ publishable_key: string;
44
+ }): PausedRequest;
45
+ /** The paused Stripe request that creates a PaymentMethod for your server to attach or confirm with: `POST /v1/payment_methods` with a placeholder card. */
46
+ export declare function stripePaymentMethodRequest(input: {
47
+ origin: string;
48
+ publishable_key: string;
49
+ }): PausedRequest;
50
+ /** The builders this package ships: the Stripe shapes Agentcard completes from a direct create today. */
51
+ export declare const BUILTIN_REQUEST_BUILDERS: readonly RequestBuilder<any>[];
52
+ /** `agentcard.checkout.requests`: the shipped builders plus yours, one registry. */
53
+ export declare class RequestRegistry {
54
+ private readonly builders;
55
+ constructor(builtin?: readonly RequestBuilder<any>[]);
56
+ /** Adds a builder, or replaces the one with the same processor and kind. Refused (`invalid_request_builder`) without a processor, a kind and a `build` function. */
57
+ register<I>(entry: RequestBuilder<I>): void;
58
+ /** Every builder, shipped and registered. */
59
+ list(): RequestBuilder<any>[];
60
+ /** The builder for a processor and kind, or null. */
61
+ get(processor: string, kind: string): RequestBuilder<any> | null;
62
+ /** Builds the paused request. Refused (`unknown_request_kind`) before anything else when no builder names that processor and kind. */
63
+ build<I = Record<string, unknown>>(input: BuildInput<I>): PausedRequest;
64
+ }
65
+ /** @deprecated Use `agentcard.checkout.requests.build({ processor: 'stripe', kind: 'payment_intent_confirm', origin, intent })`. Kept for one release. */
66
+ export declare function stripePaymentIntentConfirm(origin: string, intent: MintedIntent): PausedRequest;
@@ -0,0 +1,76 @@
1
+ import { AgentcardError } from './voice-types.js';
2
+ /** The placeholder card every builder puts in the request; the Vault replaces it at approval. */
3
+ const PLACEHOLDER = { number: '4242424242424242', exp_month: '12', exp_year: '2030', cvc: '123' };
4
+ /** The paused Stripe request for an order: `POST /v1/payment_intents/{pi}/confirm` with a placeholder card. */
5
+ export function stripePaymentIntentConfirmRequest(input) {
6
+ return {
7
+ url: `https://api.stripe.com/v1/payment_intents/${input.intent.id}/confirm`,
8
+ method: 'POST',
9
+ headers: { origin: input.origin, 'content-type': 'application/x-www-form-urlencoded' },
10
+ body: new URLSearchParams({
11
+ client_secret: input.intent.client_secret,
12
+ key: input.intent.publishable_key,
13
+ 'payment_method_data[type]': 'card',
14
+ 'payment_method_data[card][number]': PLACEHOLDER.number,
15
+ 'payment_method_data[card][exp_month]': PLACEHOLDER.exp_month,
16
+ 'payment_method_data[card][exp_year]': PLACEHOLDER.exp_year,
17
+ 'payment_method_data[card][cvc]': PLACEHOLDER.cvc,
18
+ }).toString(),
19
+ };
20
+ }
21
+ /** The paused Stripe request that tokenizes a card for your server to charge: `POST /v1/tokens` with a placeholder card. */
22
+ export function stripeCardTokenRequest(input) {
23
+ return {
24
+ url: 'https://api.stripe.com/v1/tokens',
25
+ method: 'POST',
26
+ headers: { origin: input.origin, 'content-type': 'application/x-www-form-urlencoded' },
27
+ body: new URLSearchParams({ key: input.publishable_key, 'card[number]': PLACEHOLDER.number, 'card[exp_month]': PLACEHOLDER.exp_month, 'card[exp_year]': PLACEHOLDER.exp_year, 'card[cvc]': PLACEHOLDER.cvc }).toString(),
28
+ };
29
+ }
30
+ /** The paused Stripe request that creates a PaymentMethod for your server to attach or confirm with: `POST /v1/payment_methods` with a placeholder card. */
31
+ export function stripePaymentMethodRequest(input) {
32
+ return {
33
+ url: 'https://api.stripe.com/v1/payment_methods',
34
+ method: 'POST',
35
+ headers: { origin: input.origin, 'content-type': 'application/x-www-form-urlencoded' },
36
+ body: new URLSearchParams({ key: input.publishable_key, type: 'card', 'card[number]': PLACEHOLDER.number, 'card[exp_month]': PLACEHOLDER.exp_month, 'card[exp_year]': PLACEHOLDER.exp_year, 'card[cvc]': PLACEHOLDER.cvc }).toString(),
37
+ };
38
+ }
39
+ /** The builders this package ships: the Stripe shapes Agentcard completes from a direct create today. */
40
+ export const BUILTIN_REQUEST_BUILDERS = [
41
+ { processor: 'stripe', kind: 'payment_intent_confirm', description: 'Confirms a PaymentIntent your server created; input { origin, intent: { id, client_secret, publishable_key } }.', build: stripePaymentIntentConfirmRequest },
42
+ { processor: 'stripe', kind: 'card_token', description: 'Tokenizes the card for your server to charge; input { origin, publishable_key }.', build: stripeCardTokenRequest },
43
+ { processor: 'stripe', kind: 'payment_method', description: 'Creates a PaymentMethod for your server to confirm with; input { origin, publishable_key }.', build: stripePaymentMethodRequest },
44
+ ];
45
+ const keyOf = (processor, kind) => `${processor}\u0000${kind}`;
46
+ /** `agentcard.checkout.requests`: the shipped builders plus yours, one registry. */
47
+ export class RequestRegistry {
48
+ builders = new Map();
49
+ constructor(builtin = BUILTIN_REQUEST_BUILDERS) {
50
+ for (const b of builtin)
51
+ this.builders.set(keyOf(b.processor, b.kind), b);
52
+ }
53
+ /** Adds a builder, or replaces the one with the same processor and kind. Refused (`invalid_request_builder`) without a processor, a kind and a `build` function. */
54
+ register(entry) {
55
+ if (!entry || typeof entry.processor !== 'string' || !entry.processor || typeof entry.kind !== 'string' || !entry.kind || typeof entry.build !== 'function') {
56
+ throw new AgentcardError('invalid_request_builder', 'A request builder names processor, kind and build(input).');
57
+ }
58
+ this.builders.set(keyOf(entry.processor, entry.kind), entry);
59
+ }
60
+ /** Every builder, shipped and registered. */
61
+ list() { return [...this.builders.values()]; }
62
+ /** The builder for a processor and kind, or null. */
63
+ get(processor, kind) { return this.builders.get(keyOf(processor, kind)) ?? null; }
64
+ /** Builds the paused request. Refused (`unknown_request_kind`) before anything else when no builder names that processor and kind. */
65
+ build(input) {
66
+ const { processor, kind, ...rest } = input;
67
+ const b = this.get(processor, kind);
68
+ if (!b)
69
+ throw new AgentcardError('unknown_request_kind', `No request builder for ${processor}/${kind}. Registered: ${this.list().map((x) => `${x.processor}/${x.kind}`).join(', ')}. Register one with checkout.requests.register({ processor, kind, build }).`);
70
+ return b.build(rest);
71
+ }
72
+ }
73
+ /** @deprecated Use `agentcard.checkout.requests.build({ processor: 'stripe', kind: 'payment_intent_confirm', origin, intent })`. Kept for one release. */
74
+ export function stripePaymentIntentConfirm(origin, intent) {
75
+ return stripePaymentIntentConfirmRequest({ origin, intent });
76
+ }
@@ -0,0 +1,10 @@
1
+ declare var SPREEDLY_TOKEN_ENDPOINT: string;
2
+ declare function isSpreedlyTokenRequest(url: any, method?: string): boolean;
3
+ declare function isPreparedSpreedlyRequest(body: any): boolean;
4
+ declare function isSpreedlyRequest(request: any): boolean;
5
+ declare function injectSpreedlyCard(body: any, card: any): {
6
+ body: string;
7
+ where: string[];
8
+ };
9
+ declare function sanitizeSpreedlyTokenResponse(body: any): string;
10
+ export { SPREEDLY_TOKEN_ENDPOINT, injectSpreedlyCard, isPreparedSpreedlyRequest, isSpreedlyRequest, isSpreedlyTokenRequest, sanitizeSpreedlyTokenResponse };