@agent-cards/checkout 0.19.0 → 0.22.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 (106) hide show
  1. package/README.md +6 -753
  2. package/cdp.d.ts +1 -0
  3. package/cdp.js +2 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +2 -0
  6. package/package.json +33 -33
  7. package/playwright.d.ts +1 -0
  8. package/playwright.js +2 -0
  9. package/preflight.d.ts +1 -0
  10. package/preflight.js +2 -0
  11. package/CHANGELOG.md +0 -132
  12. package/PREFLIGHT.md +0 -312
  13. package/dist/adyen-merchant-hosted.generated.d.ts +0 -277
  14. package/dist/adyen-merchant-hosted.generated.js +0 -1902
  15. package/dist/adyen.generated.d.ts +0 -24
  16. package/dist/adyen.generated.js +0 -64
  17. package/dist/attachment.d.ts +0 -11
  18. package/dist/attachment.js +0 -50
  19. package/dist/braintree.d.ts +0 -2
  20. package/dist/braintree.generated.d.ts +0 -10
  21. package/dist/braintree.generated.js +0 -302
  22. package/dist/braintree.js +0 -2
  23. package/dist/builtin-registry.generated.d.ts +0 -2
  24. package/dist/builtin-registry.generated.js +0 -1
  25. package/dist/card-fields.generated.d.ts +0 -3
  26. package/dist/card-fields.generated.js +0 -46
  27. package/dist/cdp.d.ts +0 -192
  28. package/dist/cdp.js +0 -2393
  29. package/dist/checkout-com.generated.d.ts +0 -4
  30. package/dist/checkout-com.generated.js +0 -183
  31. package/dist/client.d.ts +0 -849
  32. package/dist/client.js +0 -1754
  33. package/dist/cse-body.d.ts +0 -25
  34. package/dist/cse-body.js +0 -41
  35. package/dist/fiserv.d.ts +0 -65
  36. package/dist/fiserv.generated.d.ts +0 -73
  37. package/dist/fiserv.generated.js +0 -830
  38. package/dist/fiserv.js +0 -104
  39. package/dist/hosted-form.d.ts +0 -44
  40. package/dist/hosted-form.js +0 -78
  41. package/dist/index.d.ts +0 -18
  42. package/dist/index.js +0 -10
  43. package/dist/lifecycle.d.ts +0 -179
  44. package/dist/lifecycle.js +0 -395
  45. package/dist/mercado-checkout.d.ts +0 -20
  46. package/dist/mercado-checkout.generated.d.ts +0 -52
  47. package/dist/mercado-checkout.generated.js +0 -198
  48. package/dist/mercado-checkout.js +0 -108
  49. package/dist/merchant-handoff.d.ts +0 -54
  50. package/dist/merchant-handoff.js +0 -100
  51. package/dist/merchant-hosted.d.ts +0 -140
  52. package/dist/merchant-hosted.js +0 -170
  53. package/dist/merchant-total-watch.d.ts +0 -115
  54. package/dist/merchant-total-watch.js +0 -268
  55. package/dist/merchant-total.d.ts +0 -257
  56. package/dist/merchant-total.js +0 -383
  57. package/dist/owned-shop.generated.d.ts +0 -24
  58. package/dist/owned-shop.generated.js +0 -108
  59. package/dist/paysafe.generated.d.ts +0 -12
  60. package/dist/paysafe.generated.js +0 -87
  61. package/dist/playwright.d.ts +0 -3
  62. package/dist/playwright.js +0 -3
  63. package/dist/pre-claim.d.ts +0 -123
  64. package/dist/pre-claim.js +0 -386
  65. package/dist/preflight-capabilities.generated.d.ts +0 -1253
  66. package/dist/preflight-capabilities.generated.js +0 -1929
  67. package/dist/preflight-catalog.json +0 -4727
  68. package/dist/preflight-playwright.d.ts +0 -34
  69. package/dist/preflight-playwright.js +0 -355
  70. package/dist/preflight-schemas.json +0 -1122
  71. package/dist/preflight.d.ts +0 -1
  72. package/dist/preflight.generated.d.ts +0 -1965
  73. package/dist/preflight.generated.js +0 -570
  74. package/dist/preflight.js +0 -2
  75. package/dist/preparation.d.ts +0 -38
  76. package/dist/preparation.js +0 -191
  77. package/dist/prepared-processor.d.ts +0 -43
  78. package/dist/prepared-processor.js +0 -172
  79. package/dist/recurly.generated.d.ts +0 -1
  80. package/dist/recurly.generated.js +0 -87
  81. package/dist/registry.d.ts +0 -121
  82. package/dist/registry.js +0 -310
  83. package/dist/spreedly.generated.d.ts +0 -10
  84. package/dist/spreedly.generated.js +0 -332
  85. package/dist/stripe-checkout.d.ts +0 -81
  86. package/dist/stripe-checkout.generated.d.ts +0 -82
  87. package/dist/stripe-checkout.generated.js +0 -1093
  88. package/dist/stripe-checkout.js +0 -140
  89. package/dist/substitute.d.ts +0 -38
  90. package/dist/substitute.js +0 -23
  91. package/dist/substitutions.generated.d.ts +0 -11
  92. package/dist/substitutions.generated.js +0 -818
  93. package/examples/existing-browser.mjs +0 -63
  94. package/examples/preflight/classify-direct.mjs +0 -21
  95. package/examples/preflight/classify-kernel.mjs +0 -30
  96. package/examples/preflight/inspect-browser.mjs +0 -44
  97. package/examples/preflight/kernel-native/README.md +0 -112
  98. package/examples/preflight/kernel-native/documented-adapters.json +0 -113
  99. package/examples/preflight/kernel-native/inventory.json +0 -233
  100. package/examples/preflight/kernel-native/qualification.mjs +0 -182
  101. package/examples/preflight/kernel-profile.empty.json +0 -11
  102. package/examples/preflight/mollie-hosted.observations.json +0 -23
  103. package/examples/preflight/mollie-hosted.result.json +0 -103
  104. package/examples/preflight/stripe-script.direct.result.json +0 -92
  105. package/examples/preflight/stripe-script.observations.json +0 -16
  106. package/examples/preflight/stripe-script.result.json +0 -87
@@ -1,34 +0,0 @@
1
- import { type CheckoutSignalSnapshot, type CheckoutPreflightOptions, type CheckoutSupportResult } from './preflight.js';
2
- /** The public Playwright methods used by inspection; no Playwright dependency is required. */
3
- export interface CheckoutInspectionFrame {
4
- url(): string;
5
- parentFrame(): CheckoutInspectionFrame | null;
6
- isDetached(): boolean;
7
- evaluate<Result, Argument>(fn: (argument: Argument) => Result | Promise<Result>, argument: Argument): Promise<Result>;
8
- frameElement(): Promise<{
9
- evaluate<Result>(fn: (element: Element) => Result | Promise<Result>): Promise<Result>;
10
- dispose(): Promise<void>;
11
- }>;
12
- }
13
- export interface CheckoutInspectionPage {
14
- frames(): CheckoutInspectionFrame[];
15
- on(event: 'framenavigated' | 'frameattached' | 'framedetached', listener: (frame: CheckoutInspectionFrame) => void): unknown;
16
- off(event: 'framenavigated' | 'frameattached' | 'framedetached', listener: (frame: CheckoutInspectionFrame) => void): unknown;
17
- }
18
- export interface CheckoutCollectionOptions {
19
- /** Total inspection deadline, including all frames. Default 1,500 ms; maximum 10,000 ms. */
20
- timeoutMs?: number;
21
- /** Maximum frame count. Default and maximum 64. */
22
- maxFrames?: number;
23
- /** Maximum document and asset observations. Default and maximum 2,048. */
24
- maxSignals?: number;
25
- /** Maximum transient observation and returned snapshot size. Default and maximum 262,144 bytes; minimum 512 bytes. */
26
- maxSnapshotBytes?: number;
27
- }
28
- export type InspectCheckoutOptions = CheckoutPreflightOptions & {
29
- collection?: CheckoutCollectionOptions;
30
- };
31
- /** Read only frame, external asset and form locations; return reviewed rule IDs, never raw URLs. */
32
- export declare function collectCheckoutSignals(page: CheckoutInspectionPage, options?: CheckoutCollectionOptions): Promise<CheckoutSignalSnapshot>;
33
- /** Inspect once without preparing or submitting a checkout. */
34
- export declare function inspectCheckout(page: CheckoutInspectionPage, options?: InspectCheckoutOptions): Promise<CheckoutSupportResult>;
@@ -1,355 +0,0 @@
1
- import { normalizeCheckoutSignals, assessCheckoutSupport, cardEntryAttributable, } from './preflight.js';
2
- const EVENTS = ['framenavigated', 'frameattached', 'framedetached'];
3
- const DEADLINE = Symbol('checkout inspection deadline');
4
- const DEFAULTS = { timeoutMs: 1_500, maxFrames: 64, maxSignals: 2_048, maxSnapshotBytes: 262_144 };
5
- /** Frames at one nesting depth are read together. A remote CDP connection pays per round trip, not per frame. */
6
- const CONCURRENCY = 8;
7
- const encoder = new TextEncoder();
8
- function bounded(value, fallback, maximum, minimum = 1) {
9
- if (value === undefined)
10
- return fallback;
11
- if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
12
- throw new TypeError('Checkout inspection limits must be positive integers within the documented bounds.');
13
- }
14
- return value;
15
- }
16
- async function beforeDeadline(operation, deadline) {
17
- const remaining = deadline - performance.now();
18
- if (remaining <= 0)
19
- throw DEADLINE;
20
- let timer;
21
- try {
22
- return await Promise.race([
23
- Promise.resolve().then(operation),
24
- new Promise((_resolve, reject) => { timer = setTimeout(() => reject(DEADLINE), remaining); }),
25
- ]);
26
- }
27
- finally {
28
- if (timer !== undefined)
29
- clearTimeout(timer);
30
- }
31
- }
32
- function httpOrigin(url) {
33
- if (!url || !/^https?:/i.test(url))
34
- return null;
35
- try {
36
- return new URL(url).origin;
37
- }
38
- catch {
39
- return null;
40
- }
41
- }
42
- /** Run `worker` over `items` with bounded concurrency; results keep the input order. */
43
- async function inBatches(items, size, worker) {
44
- const results = new Array(items.length);
45
- let next = 0;
46
- await Promise.all(Array.from({ length: Math.min(size, items.length) }, async () => {
47
- while (next < items.length) {
48
- const index = next++;
49
- results[index] = await worker(items[index]);
50
- }
51
- }));
52
- return results;
53
- }
54
- /** Read only frame, external asset and form locations; return reviewed rule IDs, never raw URLs. */
55
- export async function collectCheckoutSignals(page, options = {}) {
56
- const limits = {
57
- timeoutMs: bounded(options.timeoutMs, DEFAULTS.timeoutMs, 10_000),
58
- maxFrames: bounded(options.maxFrames, DEFAULTS.maxFrames, DEFAULTS.maxFrames),
59
- maxSignals: bounded(options.maxSignals, DEFAULTS.maxSignals, DEFAULTS.maxSignals),
60
- maxSnapshotBytes: bounded(options.maxSnapshotBytes, DEFAULTS.maxSnapshotBytes, DEFAULTS.maxSnapshotBytes, 512),
61
- };
62
- const deadline = performance.now() + limits.timeoutMs;
63
- const reasons = new Set();
64
- const signals = [];
65
- const registered = [];
66
- let transientBytes = 0;
67
- let truncated = false;
68
- let stopped = false;
69
- const changed = () => { reasons.add('navigation_changed'); };
70
- const limit = (reason) => { reasons.add(reason); truncated = true; };
71
- const append = (signal) => {
72
- if (signals.length >= limits.maxSignals) {
73
- limit('signal_limit');
74
- return false;
75
- }
76
- // Raw merchant URLs exist only during this call and never enter errors or the return value.
77
- const bytes = encoder.encode(JSON.stringify(signal)).length;
78
- if (transientBytes + bytes > limits.maxSnapshotBytes) {
79
- limit('snapshot_limit');
80
- return false;
81
- }
82
- transientBytes += bytes;
83
- signals.push(signal);
84
- return true;
85
- };
86
- const stop = () => {
87
- if (stopped)
88
- return true;
89
- if (performance.now() >= deadline) {
90
- reasons.add('collection_timeout');
91
- stopped = true;
92
- }
93
- else if (signals.length >= limits.maxSignals) {
94
- limit('signal_limit');
95
- stopped = true;
96
- }
97
- else if (truncated && reasons.has('snapshot_limit'))
98
- stopped = true;
99
- return stopped;
100
- };
101
- // Hostnames of visible nested card-entry frames outside the top document's origin. They stay
102
- // local to this call; only the attribution verdict reaches the observation.
103
- const cardEntryHosts = new Set();
104
- try {
105
- for (const event of EVENTS) {
106
- page.on(event, changed);
107
- registered.push(event);
108
- }
109
- const observedFrames = page.frames();
110
- const roots = observedFrames.filter(frame => frame.parentFrame() === null);
111
- if (roots.length !== 1)
112
- reasons.add('frame_unavailable');
113
- // Frame zero always denotes the main document, regardless of adapter enumeration order.
114
- const frames = roots.length === 1 ? [roots[0], ...observedFrames.filter(frame => frame !== roots[0])] : observedFrames;
115
- const initialUrls = new Map(frames.map(frame => [frame, frame.url()]));
116
- const ids = new Map(frames.map((frame, index) => [frame, index]));
117
- if (frames.length > limits.maxFrames)
118
- limit('frame_limit');
119
- const topOrigin = frames.length ? httpOrigin(initialUrls.get(frames[0])) : null;
120
- const visibility = new Map();
121
- const depth = (frame) => {
122
- const visited = new Set();
123
- while (frame.parentFrame() && !visited.has(frame)) {
124
- visited.add(frame);
125
- frame = frame.parentFrame();
126
- }
127
- return visited.size;
128
- };
129
- // Parents settle before their children so visibility can propagate; siblings share round trips.
130
- const levels = new Map();
131
- for (const frame of frames.slice(0, limits.maxFrames)) {
132
- const level = depth(frame);
133
- levels.set(level, [...(levels.get(level) ?? []), frame]);
134
- }
135
- const readFrame = async (frame) => {
136
- if (stop())
137
- return null;
138
- const parent = frame.parentFrame();
139
- const frame_id = ids.get(frame);
140
- const parent_frame_id = parent ? ids.get(parent) ?? null : null;
141
- let visible = parent ? visibility.get(parent) ?? null : true;
142
- try {
143
- if (frame.isDetached()) {
144
- reasons.add('frame_unavailable');
145
- return null;
146
- }
147
- let released;
148
- if (parent) {
149
- if (!ids.has(parent))
150
- reasons.add('frame_unavailable');
151
- let acquisitionTimedOut = false;
152
- const acquisition = Promise.resolve().then(() => frame.frameElement());
153
- void acquisition.then(element => {
154
- if (acquisitionTimedOut)
155
- void Promise.resolve().then(() => element.dispose()).catch(() => { });
156
- }, () => { });
157
- let element;
158
- try {
159
- element = await beforeDeadline(() => acquisition, deadline);
160
- }
161
- catch (error) {
162
- acquisitionTimedOut = true;
163
- throw error;
164
- }
165
- try {
166
- const ownVisibility = await beforeDeadline(() => element.evaluate(node => {
167
- if (!node.isConnected)
168
- return null;
169
- for (let current = node; current;) {
170
- const style = getComputedStyle(current);
171
- if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse' || style.opacity === '0')
172
- return false;
173
- const root = current.getRootNode();
174
- current = current.parentElement ?? (root instanceof ShadowRoot ? root.host : null);
175
- }
176
- const rect = node.getBoundingClientRect();
177
- return rect.width > 0 && rect.height > 0;
178
- }), deadline);
179
- visible = visible === false || ownVisibility === false ? false : visible === null || ownVisibility === null ? null : true;
180
- }
181
- finally {
182
- // Disposal only releases our automation handle and overlaps the document read below.
183
- // A stalled disposal still cannot extend the inspection deadline.
184
- released = beforeDeadline(() => element.dispose(), deadline).then(() => undefined, error => error);
185
- }
186
- }
187
- visibility.set(frame, visible);
188
- const data = await beforeDeadline(() => frame.evaluate(({ remaining, byteBudget }) => {
189
- const collected = [];
190
- let bytes = 0;
191
- let signalLimit = false;
192
- let byteLimit = false;
193
- const shown = (element) => {
194
- if (!element.isConnected)
195
- return false;
196
- for (let current = element; current;) {
197
- const style = getComputedStyle(current);
198
- if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse' || style.opacity === '0')
199
- return false;
200
- const root = current.getRootNode();
201
- current = current.parentElement ?? (root instanceof ShadowRoot ? root.host : null);
202
- }
203
- const rect = element.getBoundingClientRect();
204
- return rect.width > 0 && rect.height > 0;
205
- };
206
- const elements = document.querySelectorAll('script[src], iframe[src], form[action]');
207
- for (let index = 0; index < elements.length; index++) {
208
- if (index >= remaining) {
209
- signalLimit = true;
210
- break;
211
- }
212
- const element = elements[index];
213
- const kind = element.tagName === 'SCRIPT' ? 'script' : element.tagName === 'IFRAME' ? 'iframe' : 'form';
214
- const source = element.getAttribute(kind === 'form' ? 'action' : 'src');
215
- if (!source)
216
- continue;
217
- if (source.length > byteBudget - bytes) {
218
- byteLimit = true;
219
- break;
220
- }
221
- let url;
222
- try {
223
- url = new URL(source, document.baseURI).href;
224
- }
225
- catch {
226
- continue;
227
- }
228
- bytes += url.length * 3 + 128;
229
- if (bytes > byteBudget) {
230
- byteLimit = true;
231
- break;
232
- }
233
- collected.push({ kind, url, visible: kind === 'script' ? null : shown(element) });
234
- }
235
- // Card-entry detection reads field metadata only: the autocomplete token or the
236
- // control's name and id. Values, labels and page text are never read.
237
- let cardEntry = false;
238
- const inputs = document.querySelectorAll('input');
239
- for (let index = 0; index < inputs.length && index < 256 && !cardEntry; index++) {
240
- const input = inputs[index];
241
- const type = (input.getAttribute('type') || 'text').toLowerCase();
242
- if (['hidden', 'submit', 'button', 'checkbox', 'radio', 'image', 'reset', 'file', 'range', 'color'].includes(type))
243
- continue;
244
- const autocomplete = (input.getAttribute('autocomplete') || '').trim().toLowerCase();
245
- const label = `${input.getAttribute('name') || ''} ${input.getAttribute('id') || ''}`.toLowerCase().replace(/[^a-z0-9]+/g, '');
246
- const cardField = /^cc-(?:number|csc|exp|exp-month|exp-year)$/.test(autocomplete)
247
- || !label.includes('gift') && /cardnumber|cardnum|cardno|ccnumber|ccnum|ccno|cvv|cvc|csc|securitycode|cardcode/.test(label);
248
- if (cardField && shown(input))
249
- cardEntry = true;
250
- }
251
- const documentUrl = document.URL;
252
- if (documentUrl.length * 3 > byteBudget)
253
- byteLimit = true;
254
- return { documentUrl: byteLimit && documentUrl.length * 3 > byteBudget ? '' : documentUrl, collected, signalLimit, byteLimit, cardEntry };
255
- }, { remaining: Math.max(0, limits.maxSignals - signals.length - 1), byteBudget: Math.max(0, limits.maxSnapshotBytes - transientBytes) }), deadline);
256
- const releaseError = released ? await released : undefined;
257
- if (releaseError !== undefined)
258
- throw releaseError;
259
- if (frame.isDetached())
260
- reasons.add('frame_unavailable');
261
- // Committed URLs are compared with committed URLs. The document URL is compared only for a
262
- // frame that committed an http(s) document: a script-created frame written through
263
- // document.open() inherits the merchant URL without navigating anywhere.
264
- const initial = initialUrls.get(frame) ?? '';
265
- if (frame.url() !== initial || (/^https?:/i.test(initial) && data.documentUrl !== '' && data.documentUrl !== initial))
266
- reasons.add('navigation_changed');
267
- return { frame_id, parent_frame_id, nested: parent !== null, visible, data };
268
- }
269
- catch (error) {
270
- if (error === DEADLINE) {
271
- reasons.add('collection_timeout');
272
- stopped = true;
273
- return null;
274
- }
275
- // Browser errors can include complete merchant URLs. Return a bounded reason instead.
276
- reasons.add('frame_unavailable');
277
- return null;
278
- }
279
- };
280
- for (const level of [...levels.keys()].sort((a, b) => a - b)) {
281
- if (stop())
282
- break;
283
- const outcomes = await inBatches(levels.get(level), CONCURRENCY, readFrame);
284
- // Append in frame order so identical pages produce identical observations.
285
- for (const outcome of outcomes) {
286
- if (!outcome)
287
- continue;
288
- const { data, frame_id, parent_frame_id, nested, visible } = outcome;
289
- append({ kind: nested ? 'frame' : 'document', url: data.documentUrl, frame_id, parent_frame_id, visible });
290
- for (const signal of data.collected) {
291
- if (!append({ ...signal, frame_id, parent_frame_id, visible: visible === false ? false : signal.kind === 'script' ? visible : visible === null ? null : signal.visible }))
292
- break;
293
- }
294
- if (data.signalLimit)
295
- limit('signal_limit');
296
- if (data.byteLimit)
297
- limit('snapshot_limit');
298
- if (nested && visible === true && data.cardEntry === true) {
299
- const origin = httpOrigin(data.documentUrl);
300
- if (origin && origin !== topOrigin)
301
- cardEntryHosts.add(new URL(origin).hostname);
302
- }
303
- }
304
- }
305
- if (performance.now() >= deadline)
306
- reasons.add('collection_timeout');
307
- const finalFrames = page.frames();
308
- if (frames.length !== finalFrames.length || finalFrames.some(frame => !ids.has(frame) || frame.url() !== initialUrls.get(frame))) {
309
- reasons.add('navigation_changed');
310
- }
311
- }
312
- catch {
313
- reasons.add('frame_unavailable');
314
- }
315
- finally {
316
- for (const event of registered) {
317
- try {
318
- page.off(event, changed);
319
- }
320
- catch {
321
- reasons.add('collection_incomplete');
322
- }
323
- }
324
- }
325
- const observation = () => ({ complete: reasons.size === 0, truncated, reasons: [...reasons] });
326
- let snapshot = normalizeCheckoutSignals({ signals, observation: observation() });
327
- // Visible card controls on a site no evidenced processor owns mean the card form belongs to
328
- // something the catalog has not reviewed. The observation is then incomplete, so a co-loaded
329
- // SDK script stays a candidate instead of becoming the selected processor. A snapshot past the
330
- // byte budget is already incomplete and is bounded below instead.
331
- if (encoder.encode(JSON.stringify(snapshot)).length <= limits.maxSnapshotBytes
332
- && [...cardEntryHosts].some(host => !cardEntryAttributable(snapshot, host))) {
333
- reasons.add('card_entry_unrecognized');
334
- snapshot = normalizeCheckoutSignals({ signals, observation: observation() });
335
- }
336
- if (encoder.encode(JSON.stringify(snapshot)).length > limits.maxSnapshotBytes) {
337
- limit('snapshot_limit');
338
- const boundedSnapshot = normalizeCheckoutSignals({ signals: [], observation: { complete: false, truncated, reasons: [...reasons] } });
339
- let bytes = encoder.encode(JSON.stringify(boundedSnapshot)).length;
340
- for (const signal of snapshot.signals) {
341
- const addedBytes = encoder.encode(JSON.stringify(signal)).length + (boundedSnapshot.signals.length ? 1 : 0);
342
- if (bytes + addedBytes > limits.maxSnapshotBytes)
343
- break;
344
- boundedSnapshot.signals.push(signal);
345
- bytes += addedBytes;
346
- }
347
- snapshot = boundedSnapshot;
348
- }
349
- return snapshot;
350
- }
351
- /** Inspect once without preparing or submitting a checkout. */
352
- export async function inspectCheckout(page, options = {}) {
353
- const { collection, ...assessment } = options;
354
- return assessCheckoutSupport(await collectCheckoutSignals(page, collection), assessment);
355
- }