@agent-cards/sdk 0.23.19 → 0.23.21

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 (66) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/PREFLIGHT.md +4 -0
  3. package/README.md +175 -7
  4. package/dist/amadeus.d.ts +4 -2
  5. package/dist/amadeus.js +16 -3
  6. package/dist/buy.d.ts +84 -2
  7. package/dist/buy.js +16 -1
  8. package/dist/card-endpoint-facts.d.ts +2 -1
  9. package/dist/card-endpoint-facts.js +18 -9
  10. package/dist/card-endpoint.d.ts +20 -4
  11. package/dist/card-endpoint.js +38 -4
  12. package/dist/card-fields.generated.js +1444 -51
  13. package/dist/cdp.d.ts +29 -0
  14. package/dist/cdp.js +444 -24
  15. package/dist/client.d.ts +134 -7
  16. package/dist/client.js +350 -25
  17. package/dist/comgate.d.ts +50 -0
  18. package/dist/comgate.js +80 -0
  19. package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
  20. package/dist/eprotect.generated.d.ts +1 -1
  21. package/dist/gmo.generated.js +7 -3
  22. package/dist/hipay.generated.d.ts +56 -0
  23. package/dist/hipay.generated.js +392 -0
  24. package/dist/hold.d.ts +180 -0
  25. package/dist/hold.js +270 -0
  26. package/dist/hosted-entry.d.ts +4 -0
  27. package/dist/hosted-entry.js +32 -0
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +1 -0
  30. package/dist/lifecycle.d.ts +10 -0
  31. package/dist/lifecycle.js +21 -0
  32. package/dist/merchant-card-endpoints.generated.js +102 -11
  33. package/dist/paddle-tokenex.generated.js +7 -3
  34. package/dist/paddle-vgs.generated.d.ts +3 -2
  35. package/dist/paddle-vgs.generated.js +9 -5
  36. package/dist/payrails.generated.js +7 -3
  37. package/dist/pre-claim.d.ts +20 -0
  38. package/dist/pre-claim.js +202 -22
  39. package/dist/preflight-catalog.json +58 -0
  40. package/dist/preflight-schemas.json +4 -0
  41. package/dist/preflight.generated.js +26 -2
  42. package/dist/prepared-processor.d.ts +6 -2
  43. package/dist/prepared-processor.js +18 -2
  44. package/dist/primer.generated.js +1487 -94
  45. package/dist/przelewy24.d.ts +47 -0
  46. package/dist/przelewy24.js +88 -0
  47. package/dist/quickpay.d.ts +44 -0
  48. package/dist/quickpay.js +65 -0
  49. package/dist/registry.d.ts +6 -5
  50. package/dist/registry.js +27 -7
  51. package/dist/shiji-sts.generated.js +7 -3
  52. package/dist/stripe-checkout.generated.js +744 -13
  53. package/dist/tokenex-iframe.d.ts +93 -0
  54. package/dist/tokenex-iframe.generated.d.ts +81 -0
  55. package/dist/tokenex-iframe.generated.js +403 -0
  56. package/dist/tokenex-iframe.js +195 -0
  57. package/dist/transbank.d.ts +45 -0
  58. package/dist/transbank.js +73 -0
  59. package/dist/twoc2p.d.ts +54 -0
  60. package/dist/twoc2p.generated.d.ts +33 -0
  61. package/dist/twoc2p.generated.js +1149 -0
  62. package/dist/twoc2p.js +85 -0
  63. package/dist/voice.d.ts +16 -13
  64. package/dist/voice.js +13 -10
  65. package/examples/preflight/kernel-native/inventory.json +1 -1
  66. package/package.json +3 -3
package/dist/twoc2p.js ADDED
@@ -0,0 +1,85 @@
1
+ // 2C2P's hosted payment page, the card payment as the SDK sees it (payment-core twoc2p.js has the request's shape,
2
+ // the device's card field and the substitution). The page encrypts the card itself with 2C2P's library under one
3
+ // static key and posts it in one request:
4
+ // POST https://pgwv4.2c2p.com/payment/<build>/Payment (sandbox-pgwv4.2c2p.com for a sandbox token)
5
+ // {"paymentToken":..,"payment":{"code":{"channelCode":"CC"},"data":{..,"securePayToken":"<the card field>",..}}}
6
+ // The agent's page encrypts a placeholder card, so the field carries the placeholder. This build holds that
7
+ // Payment and writes over the field the approved card's own: the field the cardholder's device wrote under the key
8
+ // payment-core pins (never a key from a row, a request or a page). Every other byte goes as the page wrote it (mode
9
+ // cse), so the full card number never reaches the agent's runtime.
10
+ //
11
+ // The generic Adyen swap takes only JWEs, so this lane is dispatched by the Payment URL to substituteTwoC2p, as
12
+ // Paddle TokenEx is to its own swap. 2C2P's field is the card under one static key, with no payment token inside:
13
+ // it would pay any 2C2P merchant's Payment, so the continuation writes it only into the one request the approval
14
+ // was created from, byte for byte.
15
+ //
16
+ // The registry entry ships dark (capability twoc2p_secure_pay). This build declares the capability on syncRegistry,
17
+ // and the API serves the recognizer only to an org Agentcard enabled for twoc2p, so for every other org nothing here
18
+ // fires. Autopilot never pays this processor: the API, the authority and the enclave refuse it.
19
+ import { TWOC2P_FIELD, TWOC2P_FIELD_AT, twoC2pPaymentTarget, substituteTwoC2p } from './twoc2p.generated.js';
20
+ /** The PSP id the API and the recognizer name this lane, re-exported so the client dispatches by it. */
21
+ export { TWOC2P_PSP } from './twoc2p.generated.js';
22
+ /** The dark-launch capability a build declares when it fills a 2C2P Payment with the device's card field. */
23
+ export const TWOC2P_CAPABILITY = 'twoc2p_secure_pay';
24
+ /**
25
+ * Whether an approved 2C2P authorization's served substitution is one this SDK can continue: JSON at payment.data,
26
+ * no sibling to remove, and exactly the one card field, a non-empty string. substituteTwoC2p checks the field
27
+ * against the pinned key for the Payment's environment; this is the shape gate the continuation runs first.
28
+ */
29
+ export function isTwoC2pServedSubstitution(sub) {
30
+ if (!sub || typeof sub !== 'object' || Array.isArray(sub))
31
+ return false;
32
+ const { encoding, at, fields, remove } = sub;
33
+ if (encoding !== 'json' || at !== TWOC2P_FIELD_AT || !fields || typeof fields !== 'object' || Array.isArray(fields))
34
+ return false;
35
+ if (remove !== undefined && !(Array.isArray(remove) && remove.length === 0))
36
+ return false;
37
+ const named = fields;
38
+ return Object.keys(named).length === 1 && typeof named[TWOC2P_FIELD] === 'string' && named[TWOC2P_FIELD].length > 0;
39
+ }
40
+ /**
41
+ * The retirement a refused 2C2P continuation runs (twoC2pContinuation's `retire`), or undefined for a client that
42
+ * cannot cancel: the approval is cancelled as merchant_never_retried, since its field went to no request, and the
43
+ * API stops serving the field. The paused request fails at once, so the cancel is not awaited; when the API does not
44
+ * confirm it, `onEvent` hears approval_retire_unconfirmed with the authorization's id. The API's sweep drops the field
45
+ * once its readable window passes either way.
46
+ */
47
+ export function twoC2pRetire(client, authorizationId, onEvent) {
48
+ const cancel = client?.cancelAuthorization;
49
+ if (typeof cancel !== 'function')
50
+ return undefined;
51
+ return () => {
52
+ void Promise.resolve()
53
+ .then(() => cancel.call(client, authorizationId, 'merchant_never_retried'))
54
+ .catch(() => onEvent?.({ type: 'approval_retire_unconfirmed', detail: { authorizationId, reason: 'authorization_cancel_unconfirmed' } }));
55
+ };
56
+ }
57
+ /**
58
+ * The continuation of an approved 2C2P Payment: the paused body with the device's card field written over the
59
+ * placeholder's (payment-core substituteTwoC2p, which refuses a field that is not one under the pinned key for the
60
+ * Payment's environment, or one already in the paused body).
61
+ *
62
+ * Only into `approved`, the paused Payment the approval was created from: `url` and `body` must equal it byte for
63
+ * byte. 2C2P's key is the same for every merchant, so the field would pay any later Payment of the same shape;
64
+ * binding it to the one request keeps this lane from ever writing one payment's field into another.
65
+ *
66
+ * Null when neither URL is a 2C2P Payment, so every other processor keeps its own swap. Throws for any other body or
67
+ * URL, and for a substitution the shape gate or the swap refuses, in each case before anything continues. A refusal
68
+ * means the device's field went to no request, so `retire` runs first: the caller retires the approval as
69
+ * merchant_never_retried, and the API stops serving the field (cdp.ts cseContinuation).
70
+ */
71
+ export function twoC2pContinuation(url, body, sub, approved, retire) {
72
+ if (twoC2pPaymentTarget(url) === null && twoC2pPaymentTarget(approved.url) === null)
73
+ return null;
74
+ try {
75
+ if (url !== approved.url || body !== approved.body)
76
+ throw new Error('Refusing to substitute: the paused request is not the 2C2P payment the cardholder approved.');
77
+ if (!isTwoC2pServedSubstitution(sub))
78
+ throw new Error('Refusing to substitute: the 2C2P approval is not one card field.');
79
+ return { body: substituteTwoC2p(url, body, sub.fields[TWOC2P_FIELD]) };
80
+ }
81
+ catch (error) {
82
+ retire?.();
83
+ throw error;
84
+ }
85
+ }
package/dist/voice.d.ts CHANGED
@@ -18,18 +18,18 @@ import { type Action, type ActionState, type AppVoiceKeyDescription, type Approv
18
18
  export { stripePaymentIntentConfirm, type MintedIntent } from './requests.js';
19
19
  /**
20
20
  * The voice half of the client's options: named once, per business, when the
21
- * product approves purchases by voice. A client built without it is a
22
- * checkout client; every `voice.*` verb on it throws `missing_approval_posture`.
21
+ * product approves purchases by voice. A client built without it, or with no
22
+ * `approval`, approves on Agentcard's page (`'sheet'`).
23
23
  */
24
24
  export interface VoiceOptions {
25
25
  /**
26
- * Required: how this product approves purchases. One choice per business,
27
- * not per purchase. `'sheet'`: the person approves on Agentcard's page with
28
- * a credential Agentcard verifies. `'in_app'`: hold-to-talk in your app,
26
+ * How this product approves purchases. One choice per business, not per
27
+ * purchase. `'sheet'` (the default): the person approves on Agentcard's page
28
+ * with a credential Agentcard verifies. `'in_app'`: hold-to-talk in your app,
29
29
  * judged on the phone and by Agentcard, signed by a key in your app.
30
- * Sandbox and live behave the same. No default.
30
+ * Sandbox and live behave the same.
31
31
  */
32
- approval: ApprovalPosture;
32
+ approval?: ApprovalPosture;
33
33
  /** Where the Vault sends the person back when a call names no `return_url` of its own. Exactly one of your registered links. */
34
34
  returnUrl?: string;
35
35
  /**
@@ -69,12 +69,15 @@ export declare const CANCEL_REASONS: readonly CancelReason[];
69
69
  * set that header, so a create that names no `checkout_origin` still carries the app's.
70
70
  */
71
71
  export declare function checkoutOriginOf(request: PausedRequest): string | null;
72
- export declare const MISSING_APPROVAL_POSTURE_MESSAGE = "Name how your product approves purchases: voice: { approval: 'sheet' } (the person approves on Agentcard's page with a credential Agentcard verifies) or voice: { approval: 'in_app' } (hold-to-talk in your app, judged on the phone and by Agentcard, signed by a key in your app). There is no default.";
72
+ export declare const MISSING_APPROVAL_POSTURE_MESSAGE = "Name how your product approves purchases: voice: { approval: 'sheet' } (the person approves on Agentcard's page with a credential Agentcard verifies; the default when approval is left out) or voice: { approval: 'in_app' } (hold-to-talk in your app, judged on the phone and by Agentcard, signed by a key in your app).";
73
+ /** The default posture: the person approves on Agentcard's page. */
74
+ export declare const DEFAULT_APPROVAL_POSTURE: ApprovalPosture;
73
75
  /**
74
- * The posture as named. Throws `missing_approval_posture` when `voice` was
75
- * given without a valid posture, or, with `required`, when it was not given.
76
+ * The posture as named, or `'sheet'` when none was: a client built without
77
+ * `voice`, or with `voice` and no `approval`, approves on Agentcard's page.
78
+ * Throws `missing_approval_posture` only for a posture that is neither.
76
79
  */
77
- export declare function approvalPostureOf(options: VoiceOptions | undefined, required: boolean): ApprovalPosture | undefined;
80
+ export declare function approvalPostureOf(options: VoiceOptions | undefined): ApprovalPosture;
78
81
  export { streamAddress } from './voice-stream.generated.js';
79
82
  /**
80
83
  * The sandbox request: a first run's purchase, naming no processor. Under a
@@ -89,8 +92,8 @@ export declare function bundleOf(ch: VoiceChallenge): HostApprovalBundle;
89
92
  export declare class VoiceModule extends ApiModule {
90
93
  private readonly options;
91
94
  private readonly clientIsSandbox;
92
- /** The approval posture the client was constructed with, one per business; `undefined` when the client named none, and then every verb throws `missing_approval_posture`. */
93
- readonly approval: ApprovalPosture | undefined;
95
+ /** The approval posture the client was constructed with, one per business; `'sheet'` when the client named none. */
96
+ readonly approval: ApprovalPosture;
94
97
  /** The grants this client minted, by session id: the verifier and the state, then the exchanged result. Memory only. */
95
98
  private readonly grants;
96
99
  /** Session id by minted `state`, so a return link finds its grant without naming the session. */
package/dist/voice.js CHANGED
@@ -69,18 +69,21 @@ function approvalRequestFields(a) {
69
69
  out.external_user_id = a.external_user_id ?? null;
70
70
  return out;
71
71
  }
72
- export const MISSING_APPROVAL_POSTURE_MESSAGE = "Name how your product approves purchases: voice: { approval: 'sheet' } (the person approves on Agentcard's page with a credential Agentcard verifies) or voice: { approval: 'in_app' } (hold-to-talk in your app, judged on the phone and by Agentcard, signed by a key in your app). There is no default.";
72
+ export const MISSING_APPROVAL_POSTURE_MESSAGE = "Name how your product approves purchases: voice: { approval: 'sheet' } (the person approves on Agentcard's page with a credential Agentcard verifies; the default when approval is left out) or voice: { approval: 'in_app' } (hold-to-talk in your app, judged on the phone and by Agentcard, signed by a key in your app).";
73
+ /** The default posture: the person approves on Agentcard's page. */
74
+ export const DEFAULT_APPROVAL_POSTURE = 'sheet';
73
75
  /**
74
- * The posture as named. Throws `missing_approval_posture` when `voice` was
75
- * given without a valid posture, or, with `required`, when it was not given.
76
+ * The posture as named, or `'sheet'` when none was: a client built without
77
+ * `voice`, or with `voice` and no `approval`, approves on Agentcard's page.
78
+ * Throws `missing_approval_posture` only for a posture that is neither.
76
79
  */
77
- export function approvalPostureOf(options, required) {
80
+ export function approvalPostureOf(options) {
78
81
  const posture = options?.approval;
82
+ if (posture === undefined)
83
+ return DEFAULT_APPROVAL_POSTURE;
79
84
  if (posture === 'sheet' || posture === 'in_app')
80
85
  return posture;
81
- if (options !== undefined || required)
82
- throw new AgentcardError('missing_approval_posture', MISSING_APPROVAL_POSTURE_MESSAGE);
83
- return undefined;
86
+ throw new AgentcardError('missing_approval_posture', MISSING_APPROVAL_POSTURE_MESSAGE);
84
87
  }
85
88
  export { streamAddress } from './voice-stream.generated.js';
86
89
  /**
@@ -212,7 +215,7 @@ function idempotencyKey() {
212
215
  export class VoiceModule extends ApiModule {
213
216
  options;
214
217
  clientIsSandbox;
215
- /** The approval posture the client was constructed with, one per business; `undefined` when the client named none, and then every verb throws `missing_approval_posture`. */
218
+ /** The approval posture the client was constructed with, one per business; `'sheet'` when the client named none. */
216
219
  approval;
217
220
  /** The grants this client minted, by session id: the verifier and the state, then the exchanged result. Memory only. */
218
221
  grants = new Map();
@@ -222,10 +225,10 @@ export class VoiceModule extends ApiModule {
222
225
  super(t);
223
226
  this.options = options;
224
227
  this.clientIsSandbox = clientIsSandbox;
225
- this.approval = approvalPostureOf(options, false);
228
+ this.approval = approvalPostureOf(options);
226
229
  }
227
230
  posture() {
228
- return approvalPostureOf(this.options, true);
231
+ return this.approval;
229
232
  }
230
233
  /** Where the Vault sends the person back when a call names nothing, as the client was constructed. */
231
234
  get returnUrl() { return this.options?.returnUrl; }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "catalog_version": "2026-09-15.1",
4
- "catalog_sha256": "47dd1bc5a749224f66980b42b758f9a19d9ac849e3a8d5230075226a4870fde5",
4
+ "catalog_sha256": "7f65e0f07138296b0cbff7dd1ed4331842b6dd45e2950f08273fcc772c7f63f6",
5
5
  "baseline_version": "kernel-native-docs-2026-09-11.1",
6
6
  "baseline_sha256": "d86722f49633d8223fccae54cd3d485df4375aee06aa156a273cc689b7c8507c",
7
7
  "native_build": null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-cards/sdk",
3
- "version": "0.23.19",
3
+ "version": "0.23.21",
4
4
  "description": "Agentcard's SDK: one client, Agentcard, with two modules. checkout pauses a browser purchase and pays it with the person's own card; voice connects a person to your product once and approves a purchase by voice inside it. Card data never enters your process.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,8 +30,8 @@
30
30
  "_comment_test": "check:built runs first and names the command when a file the build generates (the copies in src/, the preflight contracts in dist/, the Kernel inventory) is missing or stale; verify:pack runs last, on the same dist.",
31
31
  "check:built": "node scripts/generate-payment-core.mjs --check && node scripts/generate-voice-stream.mjs --check && node scripts/generate-preflight-contract.mjs --check && node scripts/generate-kernel-native-inventory.mjs --check",
32
32
  "verify:pack": "node scripts/verify-pack.mjs",
33
- "test": "npm run check:built && node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs redsys.test.mjs paypal-hosted-fields.test.mjs paypal-card-forms.test.mjs cybersource-secure-acceptance.test.mjs ebanx.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs wix-payments.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs gravy.test.mjs paddle-vgs.test.mjs paddle-tokenex.test.mjs lyra.test.mjs redact-url.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs && node scripts/verify-pack.mjs",
34
- "test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs && node amadeus-browser.test.mjs && node adyen-merchant-hosted-browser.test.mjs && node adyen-merchant-frames-browser.test.mjs && node fiserv-browser.test.mjs && node gmo-browser.test.mjs && node cybersource-flex-browser.test.mjs && node juspay-browser.test.mjs && node card-endpoint-browser.test.mjs && node datatrans-browser.test.mjs && node authorize-net-dpm-browser.test.mjs && node pelecard-browser.test.mjs && node aci-browser.test.mjs && node creditguard-browser.test.mjs && node primer-browser.test.mjs && node payrails-browser.test.mjs && node elavon-converge-browser.test.mjs && node eprotect-browser.test.mjs && node wspay-browser.test.mjs && node shiji-sts-browser.test.mjs && node worldline-mycheckout-browser.test.mjs && node paypal-hosted-fields-browser.test.mjs && node paypal-card-forms-browser.test.mjs && node cybersource-secure-acceptance-browser.test.mjs && node gravy-browser.test.mjs && node ebanx-browser.test.mjs",
33
+ "test": "npm run check:built && node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs pre-claim.test.mjs ekashu.test.mjs redsys.test.mjs quickpay.test.mjs paypal-hosted-fields.test.mjs paypal-card-forms.test.mjs cybersource-secure-acceptance.test.mjs ebanx.test.mjs autopilot.test.mjs stripe-checkout.test.mjs stripe-setup.test.mjs stripe-legacy-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs merchant-hosted.test.mjs merchant-total.test.mjs sandbox-declaration.test.mjs minimum-delay.test.mjs paysafe.test.mjs wix-payments.test.mjs hipay.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs amadeus.test.mjs pelecard.test.mjs aci.test.mjs primer.test.mjs payrails.test.mjs elavon-converge.test.mjs voice.test.mjs agentcard.test.mjs buy.test.mjs readme.test.mjs gmo.test.mjs cybersource-flex.test.mjs juspay-gate.test.mjs card-endpoint.test.mjs card-endpoint-fastspring.test.mjs card-endpoint-navigation.test.mjs merchant-session-probe.test.mjs datatrans.test.mjs authorize-net-dpm.test.mjs creditguard.test.mjs eprotect.test.mjs wspay.test.mjs shiji-sts.test.mjs worldline-mycheckout.test.mjs verifone.test.mjs gravy.test.mjs paddle-vgs.test.mjs hold.test.mjs paddle-tokenex.test.mjs lyra.test.mjs gopay.test.mjs twoc2p.test.mjs pay-nl.test.mjs redact-url.test.mjs hosted-entry.test.mjs comgate.test.mjs przelewy24.test.mjs tokenex-iframe.test.mjs transbank.test.mjs sumup.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs browser-tests.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs && node scripts/verify-pack.mjs",
34
+ "test:browser": "node browser-tests.mjs",
35
35
  "check:payment-core": "node scripts/generate-payment-core.mjs --check",
36
36
  "check:voice-stream": "node scripts/generate-voice-stream.mjs --check",
37
37
  "test:preflight": "node --test preflight-package.test.mjs preflight-collector.test.mjs kernel-native-qualification.test.mjs",