@wirexapp/card-sdk-core 0.1.0-beta.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.
- package/CHANGELOG.md +10 -0
- package/README.md +14 -0
- package/dist/collectElement.d.ts +58 -0
- package/dist/collectElement.d.ts.map +1 -0
- package/dist/collectElement.js +77 -0
- package/dist/collectElement.js.map +1 -0
- package/dist/collectGroup.d.ts +39 -0
- package/dist/collectGroup.d.ts.map +1 -0
- package/dist/collectGroup.js +131 -0
- package/dist/collectGroup.js.map +1 -0
- package/dist/frameChannel.d.ts +46 -0
- package/dist/frameChannel.d.ts.map +1 -0
- package/dist/frameChannel.js +180 -0
- package/dist/frameChannel.js.map +1 -0
- package/dist/frameUrl.d.ts +38 -0
- package/dist/frameUrl.d.ts.map +1 -0
- package/dist/frameUrl.js +21 -0
- package/dist/frameUrl.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +298 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +23 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolVersion.d.ts +11 -0
- package/dist/protocolVersion.d.ts.map +1 -0
- package/dist/protocolVersion.js +11 -0
- package/dist/protocolVersion.js.map +1 -0
- package/package.json +39 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @wirexapp/card-sdk-core
|
|
2
|
+
|
|
3
|
+
## 0.1.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1ff075a: Initial public beta of the Wirex PCI card SDK family: collect (top-up + OCT
|
|
8
|
+
destination), own-card reveal, 3DS step-up, React bindings, and the umbrella
|
|
9
|
+
package — all versioned in lockstep. Card data renders only inside
|
|
10
|
+
Wirex-served iframes; the packages are CHD-free loaders.
|
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @wirexapp/card-sdk-core
|
|
2
|
+
|
|
3
|
+
Shared foundation of the Wirex PCI card SDK: the postMessage protocol (single-source
|
|
4
|
+
`protocolVersion.json`), the frame channel (iframe lifecycle + origin/channel-verified
|
|
5
|
+
RPC), frame-URL building, and the collect machinery the profile packages wrap. CHD-free.
|
|
6
|
+
|
|
7
|
+
**Not an integration surface** — install a capability package instead
|
|
8
|
+
(`@wirexapp/card-topup-web`, `@wirexapp/card-oct-web`, `@wirexapp/card-reveal-web`,
|
|
9
|
+
`@wirexapp/card-3ds-web`, `@wirexapp/card-react`) or the umbrella
|
|
10
|
+
(`@wirexapp/card-sdk-web`); this package arrives as their dependency. All shared types
|
|
11
|
+
are re-exported by every capability package.
|
|
12
|
+
|
|
13
|
+
All packages in the family release in lockstep — your lockfile should always show one
|
|
14
|
+
version across them.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PciAppearance, PciCollectChangeState, PciCollectProfile, PciFrameStrings, PciTokenizeOctResult, PciTokenizeTopUpResult } from './protocol.js';
|
|
2
|
+
export interface PciCollectAuth {
|
|
3
|
+
/** Called right before every backend hop so the frame always gets a fresh
|
|
4
|
+
* access token. By design the frame authenticates with the caller's bearer —
|
|
5
|
+
* the permanent model for both Wirex apps and B2B partners (a partner passes
|
|
6
|
+
* its own user's bearer). There is no publishable-key layer. */
|
|
7
|
+
getBearerToken: () => Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export interface PciCollectElementConfig {
|
|
10
|
+
profile: PciCollectProfile;
|
|
11
|
+
auth: PciCollectAuth;
|
|
12
|
+
appearance?: PciAppearance;
|
|
13
|
+
strings?: PciFrameStrings;
|
|
14
|
+
onChange?: (state: PciCollectChangeState) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface TokenizeTopUpParams {
|
|
17
|
+
billingDetails: {
|
|
18
|
+
line1: string;
|
|
19
|
+
line2?: string;
|
|
20
|
+
zipCode: string;
|
|
21
|
+
country: string;
|
|
22
|
+
city: string;
|
|
23
|
+
state?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Saved-card nickname (debug-only input in web one). */
|
|
26
|
+
label?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface TokenizeOctDestinationParams {
|
|
29
|
+
cardholderName: string;
|
|
30
|
+
cardLabel: string;
|
|
31
|
+
isSaved: boolean;
|
|
32
|
+
isThirdParty: boolean;
|
|
33
|
+
userId?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The tokenize surface shared by the single-form element and the per-field
|
|
36
|
+
* group, so host code can treat either interchangeably. */
|
|
37
|
+
export interface PciCollectTokenizer {
|
|
38
|
+
tokenizeTopUp(params: TokenizeTopUpParams): Promise<PciTokenizeTopUpResult>;
|
|
39
|
+
tokenizeOctDestination(params: TokenizeOctDestinationParams): Promise<PciTokenizeOctResult>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Card-entry element. Renders PAN/expiry/CVV/name (profile `topup`) or PAN
|
|
43
|
+
* only (profile `oct-destination`) inside a Wirex-served iframe and tokenizes
|
|
44
|
+
* straight from the frame to the card service. The host only ever receives
|
|
45
|
+
* the resulting token + out-of-PCI-scope metadata (brand, bin, last4, expiry).
|
|
46
|
+
*/
|
|
47
|
+
export declare class PciCollectElement implements PciCollectTokenizer {
|
|
48
|
+
private readonly config;
|
|
49
|
+
private readonly channel;
|
|
50
|
+
constructor(frameUrl: string, config: PciCollectElementConfig);
|
|
51
|
+
mount(container: HTMLElement): Promise<void>;
|
|
52
|
+
/** Profile `topup`: link a new external top-up card. */
|
|
53
|
+
tokenizeTopUp(params: TokenizeTopUpParams): Promise<PciTokenizeTopUpResult>;
|
|
54
|
+
/** Profile `oct-destination`: tokenize a payout destination card (no CVV). */
|
|
55
|
+
tokenizeOctDestination(params: TokenizeOctDestinationParams): Promise<PciTokenizeOctResult>;
|
|
56
|
+
destroy(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=collectElement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectElement.d.ts","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EAEjB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,cAAc;IAC7B;;;qEAGiE;IACjE,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;4DAC4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5E,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC7F;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,mBAAmB;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;gBAE9B,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB;IAQvD,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBlD,wDAAwD;IAClD,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAsBjF,8EAA8E;IACxE,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkBjG,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { PciFrameChannel } from './frameChannel.js';
|
|
2
|
+
/**
|
|
3
|
+
* Card-entry element. Renders PAN/expiry/CVV/name (profile `topup`) or PAN
|
|
4
|
+
* only (profile `oct-destination`) inside a Wirex-served iframe and tokenizes
|
|
5
|
+
* straight from the frame to the card service. The host only ever receives
|
|
6
|
+
* the resulting token + out-of-PCI-scope metadata (brand, bin, last4, expiry).
|
|
7
|
+
*/
|
|
8
|
+
export class PciCollectElement {
|
|
9
|
+
constructor(frameUrl, config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.channel = new PciFrameChannel(frameUrl);
|
|
12
|
+
this.channel.onMessage(message => {
|
|
13
|
+
if (message.type === 'change')
|
|
14
|
+
config.onChange?.(message.state);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async mount(container) {
|
|
18
|
+
await this.channel.mount(container);
|
|
19
|
+
const auth = {
|
|
20
|
+
bearerToken: await this.config.auth.getBearerToken(),
|
|
21
|
+
};
|
|
22
|
+
await this.channel.request({
|
|
23
|
+
type: 'init',
|
|
24
|
+
config: {
|
|
25
|
+
mode: 'collect',
|
|
26
|
+
profile: this.config.profile,
|
|
27
|
+
auth,
|
|
28
|
+
appearance: this.config.appearance,
|
|
29
|
+
strings: this.config.strings,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Profile `topup`: link a new external top-up card. */
|
|
34
|
+
async tokenizeTopUp(params) {
|
|
35
|
+
if (this.config.profile !== 'topup') {
|
|
36
|
+
throw new Error(`tokenizeTopUp is not available for profile "${this.config.profile}"`);
|
|
37
|
+
}
|
|
38
|
+
return this.channel.request({
|
|
39
|
+
type: 'tokenize',
|
|
40
|
+
payload: {
|
|
41
|
+
kind: 'topup',
|
|
42
|
+
bearerToken: await this.config.auth.getBearerToken(),
|
|
43
|
+
billingDetails: {
|
|
44
|
+
address_line1: params.billingDetails.line1,
|
|
45
|
+
address_line2: params.billingDetails.line2,
|
|
46
|
+
postal_code: params.billingDetails.zipCode,
|
|
47
|
+
country: params.billingDetails.country,
|
|
48
|
+
city: params.billingDetails.city,
|
|
49
|
+
state: params.billingDetails.state,
|
|
50
|
+
},
|
|
51
|
+
cardholderLabel: params.label,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Profile `oct-destination`: tokenize a payout destination card (no CVV). */
|
|
56
|
+
async tokenizeOctDestination(params) {
|
|
57
|
+
if (this.config.profile !== 'oct-destination') {
|
|
58
|
+
throw new Error(`tokenizeOctDestination is not available for profile "${this.config.profile}"`);
|
|
59
|
+
}
|
|
60
|
+
return this.channel.request({
|
|
61
|
+
type: 'tokenize',
|
|
62
|
+
payload: {
|
|
63
|
+
kind: 'oct-destination',
|
|
64
|
+
bearerToken: await this.config.auth.getBearerToken(),
|
|
65
|
+
cardholderName: params.cardholderName,
|
|
66
|
+
cardLabel: params.cardLabel,
|
|
67
|
+
isSaved: params.isSaved,
|
|
68
|
+
isThirdParty: params.isThirdParty,
|
|
69
|
+
userId: params.userId,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
destroy() {
|
|
74
|
+
this.channel.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=collectElement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectElement.js","sourceRoot":"","sources":["../src/collectElement.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { PciCollectAuth, PciCollectTokenizer, TokenizeOctDestinationParams, TokenizeTopUpParams } from './collectElement.js';
|
|
2
|
+
import type { PciAppearance, PciCollectChangeState, PciCollectFieldName, PciCollectProfile, PciFrameStrings, PciTokenizeOctResult, PciTokenizeTopUpResult } from './protocol.js';
|
|
3
|
+
export interface PciCollectGroupConfig {
|
|
4
|
+
profile: PciCollectProfile;
|
|
5
|
+
auth: PciCollectAuth;
|
|
6
|
+
appearance?: PciAppearance;
|
|
7
|
+
strings?: PciFrameStrings;
|
|
8
|
+
/** Aggregated validity/brand across all mounted fields (never card values). */
|
|
9
|
+
onChange?: (state: PciCollectChangeState) => void;
|
|
10
|
+
}
|
|
11
|
+
/** Handle for one field's iframe. The host mounts it into its own container. */
|
|
12
|
+
export interface PciCollectField {
|
|
13
|
+
mount(container: HTMLElement): Promise<void>;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-field (split) Collect: one iframe per card field, arranged by the host,
|
|
18
|
+
* combining their values over a same-origin BroadcastChannel at tokenize.
|
|
19
|
+
* Exposes the same `tokenizeTopUp` / `tokenizeOctDestination` surface as the
|
|
20
|
+
* single-form `PciCollectElement`, so callers are agnostic to which they use.
|
|
21
|
+
*/
|
|
22
|
+
export declare class PciCollectGroup implements PciCollectTokenizer {
|
|
23
|
+
private readonly frameUrl;
|
|
24
|
+
private readonly config;
|
|
25
|
+
private readonly groupId;
|
|
26
|
+
private readonly channels;
|
|
27
|
+
private readonly states;
|
|
28
|
+
private brand;
|
|
29
|
+
constructor(frameUrl: string, config: PciCollectGroupConfig);
|
|
30
|
+
/** Create a field element. Mount it into your own container for layout. */
|
|
31
|
+
field(name: PciCollectFieldName): PciCollectField;
|
|
32
|
+
private emitChange;
|
|
33
|
+
private aggregator;
|
|
34
|
+
private otherFields;
|
|
35
|
+
tokenizeTopUp(params: TokenizeTopUpParams): Promise<PciTokenizeTopUpResult>;
|
|
36
|
+
tokenizeOctDestination(params: TokenizeOctDestinationParams): Promise<PciTokenizeOctResult>;
|
|
37
|
+
destroy(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=collectGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectGroup.d.ts","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,aAAa,EAEb,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,eAAe,CAAC;AAUvB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,IAAI,IAAI,CAAC;CACjB;AAOD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmD;IAC5E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkF;IACzG,OAAO,CAAC,KAAK,CAA2B;gBAE5B,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB;IAK3D,2EAA2E;IAC3E,KAAK,CAAC,IAAI,EAAE,mBAAmB,GAAG,eAAe;IAsCjD,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IAIb,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAuB3E,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmBjG,OAAO,IAAI,IAAI;CAIhB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { PciFrameChannel } from './frameChannel.js';
|
|
2
|
+
const FIELDS_BY_PROFILE = {
|
|
3
|
+
topup: ['number', 'expiry', 'cvv', 'name'],
|
|
4
|
+
'oct-destination': ['number'],
|
|
5
|
+
};
|
|
6
|
+
/** The aggregator field — the one that gathers siblings and POSTs. */
|
|
7
|
+
const AGGREGATOR = 'number';
|
|
8
|
+
const randomId = () => typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
9
|
+
? crypto.randomUUID()
|
|
10
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
11
|
+
/**
|
|
12
|
+
* Per-field (split) Collect: one iframe per card field, arranged by the host,
|
|
13
|
+
* combining their values over a same-origin BroadcastChannel at tokenize.
|
|
14
|
+
* Exposes the same `tokenizeTopUp` / `tokenizeOctDestination` surface as the
|
|
15
|
+
* single-form `PciCollectElement`, so callers are agnostic to which they use.
|
|
16
|
+
*/
|
|
17
|
+
export class PciCollectGroup {
|
|
18
|
+
constructor(frameUrl, config) {
|
|
19
|
+
this.groupId = randomId();
|
|
20
|
+
this.channels = new Map();
|
|
21
|
+
this.states = {};
|
|
22
|
+
this.brand = 'unknown';
|
|
23
|
+
this.frameUrl = frameUrl;
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
/** Create a field element. Mount it into your own container for layout. */
|
|
27
|
+
field(name) {
|
|
28
|
+
if (!FIELDS_BY_PROFILE[this.config.profile].includes(name)) {
|
|
29
|
+
throw new Error(`Field "${name}" is not part of the "${this.config.profile}" profile`);
|
|
30
|
+
}
|
|
31
|
+
if (this.channels.has(name)) {
|
|
32
|
+
throw new Error(`Field "${name}" already created for this group`);
|
|
33
|
+
}
|
|
34
|
+
const channel = new PciFrameChannel(this.frameUrl, { group: this.groupId });
|
|
35
|
+
this.channels.set(name, channel);
|
|
36
|
+
channel.onMessage(message => {
|
|
37
|
+
if (message.type !== 'change')
|
|
38
|
+
return;
|
|
39
|
+
Object.assign(this.states, message.state.fields);
|
|
40
|
+
if (name === AGGREGATOR)
|
|
41
|
+
this.brand = message.state.brand;
|
|
42
|
+
this.emitChange();
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
mount: async (container) => {
|
|
46
|
+
await channel.mount(container);
|
|
47
|
+
await channel.request({
|
|
48
|
+
type: 'init',
|
|
49
|
+
config: {
|
|
50
|
+
mode: 'collect-field',
|
|
51
|
+
field: name,
|
|
52
|
+
groupId: this.groupId,
|
|
53
|
+
appearance: this.config.appearance,
|
|
54
|
+
strings: this.config.strings,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
destroy: () => {
|
|
59
|
+
channel.destroy();
|
|
60
|
+
this.channels.delete(name);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
emitChange() {
|
|
65
|
+
const active = FIELDS_BY_PROFILE[this.config.profile];
|
|
66
|
+
const fields = {};
|
|
67
|
+
let complete = true;
|
|
68
|
+
for (const f of active) {
|
|
69
|
+
const s = this.states[f];
|
|
70
|
+
fields[f] = s ?? { valid: false, touched: false };
|
|
71
|
+
if (!s?.valid)
|
|
72
|
+
complete = false;
|
|
73
|
+
}
|
|
74
|
+
this.config.onChange?.({ complete, brand: this.brand, fields });
|
|
75
|
+
}
|
|
76
|
+
aggregator() {
|
|
77
|
+
const channel = this.channels.get(AGGREGATOR);
|
|
78
|
+
if (!channel)
|
|
79
|
+
throw new Error('The card-number field is not mounted');
|
|
80
|
+
return channel;
|
|
81
|
+
}
|
|
82
|
+
otherFields() {
|
|
83
|
+
return FIELDS_BY_PROFILE[this.config.profile].filter(f => f !== AGGREGATOR);
|
|
84
|
+
}
|
|
85
|
+
async tokenizeTopUp(params) {
|
|
86
|
+
if (this.config.profile !== 'topup') {
|
|
87
|
+
throw new Error(`tokenizeTopUp is not available for profile "${this.config.profile}"`);
|
|
88
|
+
}
|
|
89
|
+
return this.aggregator().request({
|
|
90
|
+
type: 'tokenize',
|
|
91
|
+
payload: {
|
|
92
|
+
kind: 'topup',
|
|
93
|
+
bearerToken: await this.config.auth.getBearerToken(),
|
|
94
|
+
expectedFields: this.otherFields(),
|
|
95
|
+
billingDetails: {
|
|
96
|
+
address_line1: params.billingDetails.line1,
|
|
97
|
+
address_line2: params.billingDetails.line2,
|
|
98
|
+
postal_code: params.billingDetails.zipCode,
|
|
99
|
+
country: params.billingDetails.country,
|
|
100
|
+
city: params.billingDetails.city,
|
|
101
|
+
state: params.billingDetails.state,
|
|
102
|
+
},
|
|
103
|
+
cardholderLabel: params.label,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async tokenizeOctDestination(params) {
|
|
108
|
+
if (this.config.profile !== 'oct-destination') {
|
|
109
|
+
throw new Error(`tokenizeOctDestination is not available for profile "${this.config.profile}"`);
|
|
110
|
+
}
|
|
111
|
+
return this.aggregator().request({
|
|
112
|
+
type: 'tokenize',
|
|
113
|
+
payload: {
|
|
114
|
+
kind: 'oct-destination',
|
|
115
|
+
bearerToken: await this.config.auth.getBearerToken(),
|
|
116
|
+
expectedFields: this.otherFields(),
|
|
117
|
+
cardholderName: params.cardholderName,
|
|
118
|
+
cardLabel: params.cardLabel,
|
|
119
|
+
isSaved: params.isSaved,
|
|
120
|
+
isThirdParty: params.isThirdParty,
|
|
121
|
+
userId: params.userId,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
destroy() {
|
|
126
|
+
for (const channel of this.channels.values())
|
|
127
|
+
channel.destroy();
|
|
128
|
+
this.channels.clear();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=collectGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectGroup.js","sourceRoot":"","sources":["../src/collectGroup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type PciFrameError, type PciFrameToHostMessage, type PciHostToFrameMessage } from './protocol.js';
|
|
2
|
+
export declare class PciFrameRequestError extends Error {
|
|
3
|
+
readonly status?: number;
|
|
4
|
+
constructor(error: PciFrameError);
|
|
5
|
+
}
|
|
6
|
+
/** `Omit` over a union collapses to the common members; this distributes. */
|
|
7
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
8
|
+
export interface PciFrameChannelOptions {
|
|
9
|
+
/** Grant `allow="clipboard-write"` — only the reveal frame's in-frame copy
|
|
10
|
+
* buttons need it; collect frames must not (least privilege). */
|
|
11
|
+
allowClipboard?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Owns one PCI iframe: creates it, verifies every incoming message against
|
|
15
|
+
* (source window, origin, channel id), and correlates request/response pairs.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PciFrameChannel {
|
|
18
|
+
private readonly channelId;
|
|
19
|
+
private readonly frameOrigin;
|
|
20
|
+
private readonly frameUrl;
|
|
21
|
+
private iframe;
|
|
22
|
+
private readyPromise;
|
|
23
|
+
private readonly pending;
|
|
24
|
+
private readonly listeners;
|
|
25
|
+
private readonly onWindowMessage;
|
|
26
|
+
private destroyed;
|
|
27
|
+
private readonly extraFragment;
|
|
28
|
+
private readonly allowClipboard;
|
|
29
|
+
private readyTimer;
|
|
30
|
+
private readyListener;
|
|
31
|
+
private readyReject;
|
|
32
|
+
constructor(frameUrl: string, extraFragment?: Record<string, string>, options?: PciFrameChannelOptions);
|
|
33
|
+
private removeReadyListener;
|
|
34
|
+
/** Subscribe to unsolicited frame events (change/resize/reveal-state). */
|
|
35
|
+
onMessage(listener: (message: PciFrameToHostMessage) => void): void;
|
|
36
|
+
/** Creates the iframe inside `container` and resolves once the frame script
|
|
37
|
+
* announced itself with `ready`. */
|
|
38
|
+
mount(container: HTMLElement): Promise<void>;
|
|
39
|
+
/** Fire-and-await request with `requestId` correlation. */
|
|
40
|
+
request<T>(message: DistributiveOmit<PciHostToFrameMessage, 'v' | 'channel' | 'requestId'>): Promise<T>;
|
|
41
|
+
setHeight(height: number): void;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
private handleMessage;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=frameChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameChannel.d.ts","sourceRoot":"","sources":["../src/frameChannel.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AAKvB,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,KAAK,EAAE,aAAa;CAKjC;AAOD,6EAA6E;AAC7E,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;AAQzF,MAAM,WAAW,sBAAsB;IACrC;sEACkE;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuD;IACjF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsD;IACtF,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IAGzC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,aAAa,CAA2D;IAChF,OAAO,CAAC,WAAW,CAAyC;gBAG1D,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAC1C,OAAO,GAAE,sBAA2B;IAQtC,OAAO,CAAC,mBAAmB;IAO3B,0EAA0E;IAC1E,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,GAAG,IAAI;IAInE;yCACqC;IACrC,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD5C,2DAA2D;IAC3D,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,qBAAqB,EAAE,GAAG,GAAG,SAAS,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BvG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,IAAI,IAAI;IAoBf,OAAO,CAAC,aAAa;CA2BtB"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { PCI_PROTOCOL_VERSION, } from './protocol.js';
|
|
2
|
+
const READY_TIMEOUT_MS = 10000;
|
|
3
|
+
const REQUEST_TIMEOUT_MS = 30000;
|
|
4
|
+
export class PciFrameRequestError extends Error {
|
|
5
|
+
constructor(error) {
|
|
6
|
+
super(error.message);
|
|
7
|
+
this.name = 'PciFrameRequestError';
|
|
8
|
+
this.status = error.status;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const randomId = () => typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
12
|
+
? crypto.randomUUID()
|
|
13
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
14
|
+
/**
|
|
15
|
+
* Owns one PCI iframe: creates it, verifies every incoming message against
|
|
16
|
+
* (source window, origin, channel id), and correlates request/response pairs.
|
|
17
|
+
*/
|
|
18
|
+
export class PciFrameChannel {
|
|
19
|
+
constructor(frameUrl, extraFragment = {}, options = {}) {
|
|
20
|
+
this.channelId = randomId();
|
|
21
|
+
this.iframe = null;
|
|
22
|
+
this.readyPromise = null;
|
|
23
|
+
this.pending = new Map();
|
|
24
|
+
this.listeners = [];
|
|
25
|
+
this.onWindowMessage = (event) => this.handleMessage(event);
|
|
26
|
+
this.destroyed = false;
|
|
27
|
+
// Ready-handshake bookkeeping, so destroy() can clean them up.
|
|
28
|
+
this.readyTimer = null;
|
|
29
|
+
this.readyListener = null;
|
|
30
|
+
this.readyReject = null;
|
|
31
|
+
this.frameOrigin = new URL(frameUrl, window.location.href).origin;
|
|
32
|
+
this.frameUrl = frameUrl;
|
|
33
|
+
this.extraFragment = extraFragment;
|
|
34
|
+
this.allowClipboard = options.allowClipboard ?? false;
|
|
35
|
+
}
|
|
36
|
+
removeReadyListener() {
|
|
37
|
+
if (!this.readyListener)
|
|
38
|
+
return;
|
|
39
|
+
const i = this.listeners.indexOf(this.readyListener);
|
|
40
|
+
if (i >= 0)
|
|
41
|
+
this.listeners.splice(i, 1);
|
|
42
|
+
this.readyListener = null;
|
|
43
|
+
}
|
|
44
|
+
/** Subscribe to unsolicited frame events (change/resize/reveal-state). */
|
|
45
|
+
onMessage(listener) {
|
|
46
|
+
this.listeners.push(listener);
|
|
47
|
+
}
|
|
48
|
+
/** Creates the iframe inside `container` and resolves once the frame script
|
|
49
|
+
* announced itself with `ready`. */
|
|
50
|
+
mount(container) {
|
|
51
|
+
if (this.iframe)
|
|
52
|
+
return this.readyPromise ?? Promise.resolve();
|
|
53
|
+
window.addEventListener('message', this.onWindowMessage);
|
|
54
|
+
const iframe = document.createElement('iframe');
|
|
55
|
+
// Channel id + expected parent origin travel in the fragment: not sent to
|
|
56
|
+
// the server, not logged in access logs, readable by the frame script.
|
|
57
|
+
// The frame targets all its postMessages at `parent` and rejects `init`
|
|
58
|
+
// from any other origin.
|
|
59
|
+
const extra = Object.entries(this.extraFragment)
|
|
60
|
+
.map(([k, v]) => `&${k}=${encodeURIComponent(v)}`)
|
|
61
|
+
.join('');
|
|
62
|
+
iframe.src = `${this.frameUrl}#channel=${this.channelId}&parent=${encodeURIComponent(window.location.origin)}${extra}`;
|
|
63
|
+
iframe.title = 'Wirex secure card frame';
|
|
64
|
+
// Clipboard permission for in-frame copy buttons — reveal only (collect
|
|
65
|
+
// frames never write the clipboard).
|
|
66
|
+
if (this.allowClipboard)
|
|
67
|
+
iframe.allow = 'clipboard-write';
|
|
68
|
+
iframe.style.border = '0';
|
|
69
|
+
iframe.style.display = 'block';
|
|
70
|
+
iframe.style.width = '100%';
|
|
71
|
+
iframe.style.height = '0px';
|
|
72
|
+
// Let the host's (dark) background show through the frame; without this
|
|
73
|
+
// Chrome paints an opaque white iframe backdrop.
|
|
74
|
+
iframe.style.background = 'transparent';
|
|
75
|
+
iframe.style.colorScheme = 'normal';
|
|
76
|
+
this.iframe = iframe;
|
|
77
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
78
|
+
this.readyReject = reject;
|
|
79
|
+
this.readyTimer = setTimeout(() => {
|
|
80
|
+
this.removeReadyListener();
|
|
81
|
+
reject(new Error('PCI frame did not become ready in time'));
|
|
82
|
+
}, READY_TIMEOUT_MS);
|
|
83
|
+
const listener = (message) => {
|
|
84
|
+
if (message.type === 'ready') {
|
|
85
|
+
if (this.readyTimer)
|
|
86
|
+
clearTimeout(this.readyTimer);
|
|
87
|
+
this.removeReadyListener();
|
|
88
|
+
this.readyReject = null;
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
this.readyListener = listener;
|
|
93
|
+
this.listeners.push(listener);
|
|
94
|
+
});
|
|
95
|
+
container.appendChild(iframe);
|
|
96
|
+
return this.readyPromise;
|
|
97
|
+
}
|
|
98
|
+
/** Fire-and-await request with `requestId` correlation. */
|
|
99
|
+
request(message) {
|
|
100
|
+
const contentWindow = this.iframe?.contentWindow;
|
|
101
|
+
if (!contentWindow)
|
|
102
|
+
return Promise.reject(new Error('PCI frame is not mounted'));
|
|
103
|
+
const requestId = randomId();
|
|
104
|
+
const promise = new Promise((resolve, reject) => {
|
|
105
|
+
const timer = setTimeout(() => {
|
|
106
|
+
this.pending.delete(requestId);
|
|
107
|
+
reject(new Error('PCI frame request timed out'));
|
|
108
|
+
}, REQUEST_TIMEOUT_MS);
|
|
109
|
+
this.pending.set(requestId, {
|
|
110
|
+
resolve: resolve,
|
|
111
|
+
reject,
|
|
112
|
+
timer,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
const envelope = {
|
|
116
|
+
...message,
|
|
117
|
+
requestId,
|
|
118
|
+
v: PCI_PROTOCOL_VERSION,
|
|
119
|
+
channel: this.channelId,
|
|
120
|
+
};
|
|
121
|
+
contentWindow.postMessage(envelope, this.frameOrigin);
|
|
122
|
+
return promise;
|
|
123
|
+
}
|
|
124
|
+
setHeight(height) {
|
|
125
|
+
if (this.iframe)
|
|
126
|
+
this.iframe.style.height = `${Math.max(0, Math.ceil(height))}px`;
|
|
127
|
+
}
|
|
128
|
+
destroy() {
|
|
129
|
+
if (this.destroyed)
|
|
130
|
+
return;
|
|
131
|
+
this.destroyed = true;
|
|
132
|
+
window.removeEventListener('message', this.onWindowMessage);
|
|
133
|
+
// Settle the ready handshake so a destroyed frame's timeout can't fire and
|
|
134
|
+
// clobber a freshly-mounted frame's state.
|
|
135
|
+
if (this.readyTimer)
|
|
136
|
+
clearTimeout(this.readyTimer);
|
|
137
|
+
this.removeReadyListener();
|
|
138
|
+
this.readyReject?.(new Error('PCI frame destroyed'));
|
|
139
|
+
this.readyReject = null;
|
|
140
|
+
for (const [, pendingRequest] of this.pending) {
|
|
141
|
+
clearTimeout(pendingRequest.timer);
|
|
142
|
+
pendingRequest.reject(new Error('PCI frame destroyed'));
|
|
143
|
+
}
|
|
144
|
+
this.pending.clear();
|
|
145
|
+
this.listeners.length = 0;
|
|
146
|
+
this.iframe?.remove();
|
|
147
|
+
this.iframe = null;
|
|
148
|
+
}
|
|
149
|
+
handleMessage(event) {
|
|
150
|
+
// Provenance checks: owned window + expected origin + our channel id.
|
|
151
|
+
if (!this.iframe || event.source !== this.iframe.contentWindow)
|
|
152
|
+
return;
|
|
153
|
+
if (event.origin !== this.frameOrigin)
|
|
154
|
+
return;
|
|
155
|
+
const message = event.data;
|
|
156
|
+
if (!message || message.v !== PCI_PROTOCOL_VERSION || message.channel !== this.channelId)
|
|
157
|
+
return;
|
|
158
|
+
if (message.type === 'resize') {
|
|
159
|
+
this.setHeight(message.height);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (message.type === 'result') {
|
|
163
|
+
const pendingRequest = this.pending.get(message.requestId);
|
|
164
|
+
if (!pendingRequest)
|
|
165
|
+
return;
|
|
166
|
+
this.pending.delete(message.requestId);
|
|
167
|
+
clearTimeout(pendingRequest.timer);
|
|
168
|
+
if (message.ok) {
|
|
169
|
+
pendingRequest.resolve(message.data);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
pendingRequest.reject(new PciFrameRequestError(message.error));
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
for (const listener of this.listeners)
|
|
177
|
+
listener(message);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=frameChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameChannel.js","sourceRoot":"","sources":["../src/frameChannel.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAEvB,MAAM,gBAAgB,GAAG,KAAM,CAAC;AAChC,MAAM,kBAAkB,GAAG,KAAM,CAAC;AAElC,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAG7C,YAAY,KAAoB;QAC9B,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,GAAW,EAAE,CAC5B,OAAO,MAAM,KAAK,WAAW,IAAI,YAAY,IAAI,MAAM;IACrD,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE;IACrB,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAiB7D;;;GAGG;AACH,MAAM,OAAO,eAAe;IAmB1B,YACE,QAAgB,EAChB,gBAAwC,EAAE,EAC1C,UAAkC,EAAE;QArBrB,cAAS,GAAG,QAAQ,EAAE,CAAC;QAGhC,WAAM,GAA6B,IAAI,CAAC;QACxC,iBAAY,GAAyB,IAAI,CAAC;QACjC,YAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;QAC5C,cAAS,GAAoD,EAAE,CAAC;QAChE,oBAAe,GAAG,CAAC,KAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9E,cAAS,GAAG,KAAK,CAAC;QAK1B,+DAA+D;QACvD,eAAU,GAAyC,IAAI,CAAC;QACxD,kBAAa,GAAsD,IAAI,CAAC;QACxE,gBAAW,GAAoC,IAAI,CAAC;QAO1D,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAClE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IACxD,CAAC;IAEO,mBAAmB;QACzB,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,OAAO;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED,0EAA0E;IAC1E,SAAS,CAAC,QAAkD;QAC1D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED;yCACqC;IACrC,KAAK,CAAC,SAAsB;QAC1B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAE/D,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAEzD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,0EAA0E;QAC1E,uEAAuE;QACvE,wEAAwE;QACxE,yBAAyB;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;aACjD,IAAI,CAAC,EAAE,CAAC,CAAC;QACZ,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,SAAS,WAAW,kBAAkB,CAClF,MAAM,CAAC,QAAQ,CAAC,MAAM,CACvB,GAAG,KAAK,EAAE,CAAC;QACZ,MAAM,CAAC,KAAK,GAAG,yBAAyB,CAAC;QACzC,wEAAwE;QACxE,qCAAqC;QACrC,IAAI,IAAI,CAAC,cAAc;YAAE,MAAM,CAAC,KAAK,GAAG,iBAAiB,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QAC5B,wEAAwE;QACxE,iDAAiD;QACjD,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,aAAa,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;YAC1B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBAChC,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC,CAAC;YAC9D,CAAC,EAAE,gBAAgB,CAAC,CAAC;YACrB,MAAM,QAAQ,GAAG,CAAC,OAA8B,EAAE,EAAE;gBAClD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC7B,IAAI,IAAI,CAAC,UAAU;wBAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACnD,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC;YACF,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,2DAA2D;IAC3D,OAAO,CAAI,OAA+E;QACxF,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC;QACjD,IAAI,CAAC,aAAa;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAEjF,MAAM,SAAS,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC/B,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;YACnD,CAAC,EAAE,kBAAkB,CAAC,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE;gBAC1B,OAAO,EAAE,OAAkC;gBAC3C,MAAM;gBACN,KAAK;aACN,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG;YACf,GAAG,OAAO;YACV,SAAS;YACT,CAAC,EAAE,oBAAoB;YACvB,OAAO,EAAE,IAAI,CAAC,SAAS;SACC,CAAC;QAC3B,aAAa,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;IACpF,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5D,2EAA2E;QAC3E,2CAA2C;QAC3C,IAAI,IAAI,CAAC,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,KAAK,MAAM,CAAC,EAAE,cAAc,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACnC,cAAc,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAEO,aAAa,CAAC,KAAmB;QACvC,sEAAsE;QACtE,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa;YAAE,OAAO;QACvE,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAyC,CAAC;QAChE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,KAAK,oBAAoB,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,SAAS;YAAE,OAAO;QAEjG,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc;gBAAE,OAAO;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACnC,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;gBACf,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,MAAM,CAAC,IAAI,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,OAAO;QACT,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-URL building shared by every capability package (topup / oct / reveal)
|
|
3
|
+
* and the umbrella `createPciSdk` factory.
|
|
4
|
+
*/
|
|
5
|
+
export interface PciSdkConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Origin serving the PCI frames — the dedicated PCI SDK service
|
|
8
|
+
* (`sdk-service/`, prod `https://card-js.wirexapp.tech`). **Required**,
|
|
9
|
+
* and it MUST be a different origin than the host app: that cross-origin
|
|
10
|
+
* boundary is the entire Same-Origin-Policy isolation the SDK relies on. There
|
|
11
|
+
* is deliberately no same-origin fallback — a same-origin frame would let
|
|
12
|
+
* host-page XSS read cardholder data, nullifying the SDK.
|
|
13
|
+
* TODO(pci-sdk): prod split — loader on `js.<domain>`, cookieless frames on
|
|
14
|
+
* `frames.<domain>` (docs/DESIGN.md §5.5.1).
|
|
15
|
+
*/
|
|
16
|
+
frameOrigin: string;
|
|
17
|
+
/** Path prefix of the frame documents on `frameOrigin` (the service serves
|
|
18
|
+
* them at the root, so this is empty by default). */
|
|
19
|
+
framePath?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Client identifier, added to the frame URL as `?client=<id>`. The frame host
|
|
22
|
+
* resolves it to that client's registered parent origins and emits a
|
|
23
|
+
* per-client `frame-ancestors` — so each partner embeds the frame under its
|
|
24
|
+
* own domain without any other partner's origins being exposed. NON-SECRET
|
|
25
|
+
* and NOT auth: it only selects which (closed) allowlist the browser enforces,
|
|
26
|
+
* so spoofing another client's id cannot authorise a new embedding origin.
|
|
27
|
+
* Omit for Wirex's own apps (they resolve to the Wirex first-party domains).
|
|
28
|
+
*/
|
|
29
|
+
clientId?: string;
|
|
30
|
+
}
|
|
31
|
+
export type PciFrameDocument = 'collect' | 'collect-field' | 'reveal';
|
|
32
|
+
export declare const assertFrameOrigin: (config: PciSdkConfig) => void;
|
|
33
|
+
/** Build the URL of one frame document. `client` MUST be a query param, not the
|
|
34
|
+
* URL fragment: the frame host reads it server-side to emit the per-client
|
|
35
|
+
* `frame-ancestors` header, and the fragment is never sent to the server. The
|
|
36
|
+
* channel/parent fragment is appended afterwards by `PciFrameChannel`. */
|
|
37
|
+
export declare const buildFrameUrl: (config: PciSdkConfig, document: PciFrameDocument) => string;
|
|
38
|
+
//# sourceMappingURL=frameUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameUrl.d.ts","sourceRoot":"","sources":["../src/frameUrl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;0DACsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,eAAe,GAAG,QAAQ,CAAC;AAEtE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,YAAY,KAAG,IAOxD,CAAC;AAEF;;;2EAG2E;AAC3E,eAAO,MAAM,aAAa,GAAI,QAAQ,YAAY,EAAE,UAAU,gBAAgB,KAAG,MAKhF,CAAC"}
|
package/dist/frameUrl.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-URL building shared by every capability package (topup / oct / reveal)
|
|
3
|
+
* and the umbrella `createPciSdk` factory.
|
|
4
|
+
*/
|
|
5
|
+
export const assertFrameOrigin = (config) => {
|
|
6
|
+
if (!config.frameOrigin) {
|
|
7
|
+
throw new Error('PCI SDK: `frameOrigin` is required and must be a dedicated origin, different from the host app. ' +
|
|
8
|
+
'The Same-Origin-Policy isolation depends on it; a same-origin frame would expose cardholder data to host-page XSS.');
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/** Build the URL of one frame document. `client` MUST be a query param, not the
|
|
12
|
+
* URL fragment: the frame host reads it server-side to emit the per-client
|
|
13
|
+
* `frame-ancestors` header, and the fragment is never sent to the server. The
|
|
14
|
+
* channel/parent fragment is appended afterwards by `PciFrameChannel`. */
|
|
15
|
+
export const buildFrameUrl = (config, document) => {
|
|
16
|
+
assertFrameOrigin(config);
|
|
17
|
+
const framePath = config.framePath ?? '';
|
|
18
|
+
const query = config.clientId ? `?client=${encodeURIComponent(config.clientId)}` : '';
|
|
19
|
+
return `${config.frameOrigin}${framePath}/${document}.html${query}`;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=frameUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameUrl.js","sourceRoot":"","sources":["../src/frameUrl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAoB,EAAQ,EAAE;IAC9D,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,kGAAkG;YAChG,oHAAoH,CACvH,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF;;;2EAG2E;AAC3E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAoB,EAAE,QAA0B,EAAU,EAAE;IACxF,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACtF,OAAO,GAAG,MAAM,CAAC,WAAW,GAAG,SAAS,IAAI,QAAQ,QAAQ,KAAK,EAAE,CAAC;AACtE,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@wirexapp/card-sdk-core` — shared foundation of the Wirex PCI card SDK:
|
|
3
|
+
* the postMessage protocol (single source of the protocol version), the frame
|
|
4
|
+
* channel (iframe lifecycle + origin/channel-verified RPC), frame-URL building,
|
|
5
|
+
* and the collect machinery both profile packages wrap.
|
|
6
|
+
*
|
|
7
|
+
* Partners install the capability packages (`@wirexapp/card-topup-web`,
|
|
8
|
+
* `@wirexapp/card-oct-web`, `@wirexapp/card-reveal-web`) or the umbrella
|
|
9
|
+
* (`@wirexapp/card-sdk-web`) — this package is their common dependency, not an
|
|
10
|
+
* integration surface of its own.
|
|
11
|
+
*/
|
|
12
|
+
export type { PciCollectAuth, PciCollectElementConfig, PciCollectTokenizer, TokenizeOctDestinationParams, TokenizeTopUpParams, } from './collectElement.js';
|
|
13
|
+
export { PciCollectElement } from './collectElement.js';
|
|
14
|
+
export type { PciCollectField, PciCollectGroupConfig } from './collectGroup.js';
|
|
15
|
+
export { PciCollectGroup } from './collectGroup.js';
|
|
16
|
+
export { PciFrameChannel, PciFrameRequestError } from './frameChannel.js';
|
|
17
|
+
export type { PciFrameDocument, PciSdkConfig } from './frameUrl.js';
|
|
18
|
+
export { assertFrameOrigin, buildFrameUrl } from './frameUrl.js';
|
|
19
|
+
export type { PciAppearance, PciAppearanceVariables, PciBrandBadgeAppearance, PciCardBrand, PciCollectChangeState, PciCollectFieldInitConfig, PciCollectFieldName, PciCollectInitConfig, PciCollectProfile, PciFrameAuth, PciFrameError, PciFrameStrings, PciFrameToHostMessage, PciHostToFrameMessage, PciInitConfig, PciRevealField, PciRevealInitConfig, PciRevealState, PciRevealStatus, PciTokenizeOctRequest, PciTokenizeOctResult, PciTokenizeRequest, PciTokenizeTopUpRequest, PciTokenizeTopUpResult, } from './protocol.js';
|
|
20
|
+
export { PCI_PROTOCOL_VERSION } from './protocol.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,EACV,cAAc,EACd,uBAAuB,EACvB,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,YAAY,EACZ,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@wirexapp/card-sdk-core` — shared foundation of the Wirex PCI card SDK:
|
|
3
|
+
* the postMessage protocol (single source of the protocol version), the frame
|
|
4
|
+
* channel (iframe lifecycle + origin/channel-verified RPC), frame-URL building,
|
|
5
|
+
* and the collect machinery both profile packages wrap.
|
|
6
|
+
*
|
|
7
|
+
* Partners install the capability packages (`@wirexapp/card-topup-web`,
|
|
8
|
+
* `@wirexapp/card-oct-web`, `@wirexapp/card-reveal-web`) or the umbrella
|
|
9
|
+
* (`@wirexapp/card-sdk-web`) — this package is their common dependency, not an
|
|
10
|
+
* integration surface of its own.
|
|
11
|
+
*/
|
|
12
|
+
export { PciCollectElement } from './collectElement.js';
|
|
13
|
+
export { PciCollectGroup } from './collectGroup.js';
|
|
14
|
+
export { PciFrameChannel, PciFrameRequestError } from './frameChannel.js';
|
|
15
|
+
export { assertFrameOrigin, buildFrameUrl } from './frameUrl.js';
|
|
16
|
+
export { PCI_PROTOCOL_VERSION } from './protocol.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE1E,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA2BjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postMessage protocol between the host-side SDK and the PCI frames.
|
|
3
|
+
*
|
|
4
|
+
* The frames live in `apps/web/sdk-service/frames-src/src/*` (TypeScript) and
|
|
5
|
+
* `import type` these same definitions, so the message SHAPES cannot drift. The
|
|
6
|
+
* `PCI_PROTOCOL_VERSION` value is injected into the frame bundle by esbuild from
|
|
7
|
+
* `protocolVersion.json` (a single source); frames reject mismatched versions.
|
|
8
|
+
* Note: esbuild does not type-check, so the shape guarantee is only enforced by
|
|
9
|
+
* the frame service's separate `tsc` (see apps/web/sdk-service/tsconfig.json).
|
|
10
|
+
*
|
|
11
|
+
* Security model: every message carries the per-mount `channel` id the host
|
|
12
|
+
* generated; the host additionally verifies `event.source` is the owned iframe's
|
|
13
|
+
* contentWindow and `event.origin` is the configured frame origin. The frame
|
|
14
|
+
* binds itself to the first `init` sender and its origin. The frames are served
|
|
15
|
+
* cross-origin by the PCI SDK service (`apps/web/sdk-service`), so this is a real
|
|
16
|
+
* origin boundary — host-page scripts cannot read the frame contents.
|
|
17
|
+
*/
|
|
18
|
+
/** Single source of truth for the protocol version, shared with the frame
|
|
19
|
+
* build in `apps/web/sdk-service` (esbuild reads the same JSON and injects it),
|
|
20
|
+
* so the host SDK and the vanilla frames can never drift. */
|
|
21
|
+
export declare const PCI_PROTOCOL_VERSION: number;
|
|
22
|
+
/** Collect profiles — which card fields the frame renders. */
|
|
23
|
+
export type PciCollectProfile = 'topup' | 'oct-destination';
|
|
24
|
+
/** Individual card field, for the per-field (split) Collect frames. */
|
|
25
|
+
export type PciCollectFieldName = 'number' | 'expiry' | 'cvv' | 'name';
|
|
26
|
+
export type PciCardBrand = 'Visa' | 'MasterCard' | 'unknown';
|
|
27
|
+
/** Non-sensitive field state reported to the host while the user types. */
|
|
28
|
+
export interface PciCollectChangeState {
|
|
29
|
+
/** All rendered fields pass validation. */
|
|
30
|
+
complete: boolean;
|
|
31
|
+
brand: PciCardBrand;
|
|
32
|
+
/** Per-field validity; keys depend on the profile. */
|
|
33
|
+
fields: Record<string, {
|
|
34
|
+
valid: boolean;
|
|
35
|
+
touched: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
export type PciRevealStatus = 'loading' | 'ready' | 'error';
|
|
39
|
+
export interface PciRevealState {
|
|
40
|
+
status: PciRevealStatus;
|
|
41
|
+
/** Present when status === 'error'; safe (non-sensitive) message. */
|
|
42
|
+
errorMessage?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Translated UI strings passed into the frame (labels are not sensitive).
|
|
45
|
+
* Built host-side with Lingui — the frame has no i18n runtime.
|
|
46
|
+
* TODO(pci-sdk): replace with locale packs shipped with the frame bundle. */
|
|
47
|
+
export type PciFrameStrings = Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* Whitelisted appearance tokens — the ONLY styling input the frames accept.
|
|
50
|
+
*
|
|
51
|
+
* Modeled on Stripe's Elements `appearance.variables` / Marqeta's style
|
|
52
|
+
* objects: named tokens with per-type validation, never raw CSS. Arbitrary
|
|
53
|
+
* CSS inside a PCI frame is an exfiltration channel (attribute-selector +
|
|
54
|
+
* `url()` beacons can leak field values character by character, custom fonts
|
|
55
|
+
* leak via ligature/unicode-range side channels), so:
|
|
56
|
+
* - no selectors, no stylesheets, no `url()`-capable values;
|
|
57
|
+
* - every token is validated INSIDE the frame against its value type
|
|
58
|
+
* (color / length / font stack) — the host SDK types are convenience only,
|
|
59
|
+
* a compromised host page must not be able to smuggle CSS through;
|
|
60
|
+
* - values are applied via CSSOM custom properties, never string-built
|
|
61
|
+
* `<style>` text, so there is no injection breakout.
|
|
62
|
+
* `fontFamily` resolves against fonts available inside the frame (system /
|
|
63
|
+
* web-safe). TODO(pci-sdk): custom brand fonts need a Stripe-style `fonts`
|
|
64
|
+
* option where the FRAME fetches an allowlisted `@font-face` stylesheet —
|
|
65
|
+
* never host-supplied CSS.
|
|
66
|
+
*/
|
|
67
|
+
export interface PciAppearanceVariables {
|
|
68
|
+
colorText?: string;
|
|
69
|
+
colorLabel?: string;
|
|
70
|
+
colorPlaceholder?: string;
|
|
71
|
+
colorError?: string;
|
|
72
|
+
/** Focus ring / accent (input border on focus, copy buttons). */
|
|
73
|
+
colorFocus?: string;
|
|
74
|
+
/** Input (field pill) background. */
|
|
75
|
+
colorBackground?: string;
|
|
76
|
+
colorBorder?: string;
|
|
77
|
+
/** The frame document's own background. Defaults to `transparent` so the
|
|
78
|
+
* host surface (e.g. a gradient panel) shows through; set an opaque colour
|
|
79
|
+
* to render a solid card behind the fields. */
|
|
80
|
+
surfaceColor?: string;
|
|
81
|
+
/** Comma-separated font stack; each family name is validated. When `fontUrl`
|
|
82
|
+
* is set, the FIRST family here is the custom face's name. */
|
|
83
|
+
fontFamily?: string;
|
|
84
|
+
/** Optional custom brand font: an `https://` URL to a font FILE
|
|
85
|
+
* (woff2/woff/otf/ttf) on a CDN. The frame builds a single `@font-face` from
|
|
86
|
+
* it (never a host-supplied stylesheet), names it after `fontFamily`'s first
|
|
87
|
+
* family, and loads it — subject to the frame host's `font-src` allowlist for
|
|
88
|
+
* that client. NOT raw CSS: only a validated file URL is accepted. */
|
|
89
|
+
fontUrl?: string;
|
|
90
|
+
/** Multiple weight/style faces of the brand font — real weights instead of
|
|
91
|
+
* faux-bolding a single file. Each `url` is an https font FILE; `weight` /
|
|
92
|
+
* `style` become the `@font-face` descriptors. All faces share the first
|
|
93
|
+
* `fontFamily` family name. Takes precedence over `fontUrl`. */
|
|
94
|
+
fontFaces?: Array<{
|
|
95
|
+
url: string;
|
|
96
|
+
weight?: string;
|
|
97
|
+
style?: 'normal' | 'italic';
|
|
98
|
+
}>;
|
|
99
|
+
fontSizeBase?: string;
|
|
100
|
+
fontSizeLabel?: string;
|
|
101
|
+
/** Line height for value / input text (e.g. `24px`). */
|
|
102
|
+
lineHeightBase?: string;
|
|
103
|
+
/** Line height for labels (e.g. `16px`). */
|
|
104
|
+
lineHeightLabel?: string;
|
|
105
|
+
/** Font weight for value / input text (`100`–`900`, `normal`, `bold`; e.g. `600`). */
|
|
106
|
+
fontWeightBase?: string;
|
|
107
|
+
/** Font weight for labels (e.g. `400`). */
|
|
108
|
+
fontWeightLabel?: string;
|
|
109
|
+
borderRadius?: string;
|
|
110
|
+
borderWidth?: string;
|
|
111
|
+
/** Vertical gap between fields/rows. */
|
|
112
|
+
spacingUnit?: string;
|
|
113
|
+
/** Min height of an input control (the field "pill"). */
|
|
114
|
+
controlMinHeight?: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Card-brand indicator (Visa/Mastercard) shown inside the card-number field.
|
|
118
|
+
* Same trust model as `PciAppearanceVariables`: every value is re-validated
|
|
119
|
+
* inside the frame (color / length grammars), the logo artwork itself is
|
|
120
|
+
* bundled with the frame — the host can only pick among fixed presentations,
|
|
121
|
+
* never inject markup.
|
|
122
|
+
*/
|
|
123
|
+
export interface PciBrandBadgeAppearance {
|
|
124
|
+
/** Render the brand indicator at all. Default `true`. */
|
|
125
|
+
show?: boolean;
|
|
126
|
+
/** `logo` — the brand mark (SVG bundled in the frame); `text` — the brand
|
|
127
|
+
* name as plain text (the pre-logo presentation). Default `logo`. */
|
|
128
|
+
style?: 'logo' | 'text';
|
|
129
|
+
/** `color` — original brand colours; `mono` — single-colour mark drawn in
|
|
130
|
+
* the label colour (`colorLabel`). Logo style only. Default `color`. */
|
|
131
|
+
tone?: 'color' | 'mono';
|
|
132
|
+
/** Badge plate colour behind the mark. Default `transparent` (no plate);
|
|
133
|
+
* set e.g. `#FFFFFF` for a white chip like the host app's saved-card rows. */
|
|
134
|
+
background?: string;
|
|
135
|
+
/** Corner radius of the plate — a length or percentage. `50%` (default)
|
|
136
|
+
* gives a circle, `4px` a rounded square. */
|
|
137
|
+
borderRadius?: string;
|
|
138
|
+
/** Plate size (width = height). Default `24px`. */
|
|
139
|
+
size?: string;
|
|
140
|
+
/** Fine-tune position from the default (vertically centered, 16px from the
|
|
141
|
+
* field's right edge). Signed lengths; positive `offsetX` moves right,
|
|
142
|
+
* positive `offsetY` moves down. Default `0px`. */
|
|
143
|
+
offsetX?: string;
|
|
144
|
+
offsetY?: string;
|
|
145
|
+
}
|
|
146
|
+
export interface PciAppearance {
|
|
147
|
+
theme?: 'light' | 'dark';
|
|
148
|
+
variables?: PciAppearanceVariables;
|
|
149
|
+
/** Brand indicator in the card-number field; see `PciBrandBadgeAppearance`. */
|
|
150
|
+
brandBadge?: PciBrandBadgeAppearance;
|
|
151
|
+
}
|
|
152
|
+
/** Auth material the frame needs to reach the backend directly. By design the
|
|
153
|
+
* frame uses the caller's access token (bearer); reveal adds the existing
|
|
154
|
+
* `action_token`. This is the model for Wirex apps and B2B partners alike (the
|
|
155
|
+
* partner supplies its own user's bearer) — no publishable-key / session JWT. */
|
|
156
|
+
/**
|
|
157
|
+
* Per-user auth material passed from the host into a frame. Note there are NO
|
|
158
|
+
* API URLs here: the frame service owns its own endpoints (injected as
|
|
159
|
+
* `window.__WX_ENDPOINTS__`), so the host can never point card-data requests at
|
|
160
|
+
* another origin. Only the user's session credentials — which a cross-origin
|
|
161
|
+
* frame cannot obtain itself — flow in.
|
|
162
|
+
*/
|
|
163
|
+
export interface PciFrameAuth {
|
|
164
|
+
bearerToken: string;
|
|
165
|
+
userEmail?: string;
|
|
166
|
+
userWallet?: string;
|
|
167
|
+
}
|
|
168
|
+
export interface PciCollectInitConfig {
|
|
169
|
+
mode: 'collect';
|
|
170
|
+
profile: PciCollectProfile;
|
|
171
|
+
auth: PciFrameAuth;
|
|
172
|
+
appearance?: PciAppearance;
|
|
173
|
+
strings?: PciFrameStrings;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Per-field (split) Collect frame — one iframe renders a single card field,
|
|
177
|
+
* the host arranges them in its own layout (Stripe split-Elements / VGS /
|
|
178
|
+
* Braintree model). Fields in the same `groupId` combine their values for
|
|
179
|
+
* tokenization over a same-origin BroadcastChannel — the value never reaches
|
|
180
|
+
* the host page. Only the aggregator field (`number`) POSTs; the target URL is
|
|
181
|
+
* the frame service's own `window.__WX_ENDPOINTS__`, not host-supplied. It
|
|
182
|
+
* receives the fresh bearer with the `tokenize` message.
|
|
183
|
+
*/
|
|
184
|
+
export interface PciCollectFieldInitConfig {
|
|
185
|
+
mode: 'collect-field';
|
|
186
|
+
field: PciCollectFieldName;
|
|
187
|
+
/** Shared BroadcastChannel group id; siblings aggregate values under it. */
|
|
188
|
+
groupId: string;
|
|
189
|
+
appearance?: PciAppearance;
|
|
190
|
+
strings?: PciFrameStrings;
|
|
191
|
+
}
|
|
192
|
+
/** A reveal row. Used to render one field per iframe (`fields`). */
|
|
193
|
+
export type PciRevealField = 'cardholder' | 'number' | 'expiry' | 'cvv' | 'pin';
|
|
194
|
+
export interface PciRevealInitConfig {
|
|
195
|
+
mode: 'reveal';
|
|
196
|
+
auth: PciFrameAuth;
|
|
197
|
+
cardId: string;
|
|
198
|
+
/** One-shot step-up token minted by the host (wallet signature); sent with
|
|
199
|
+
* the bearer to the reveal endpoints. This is the intended auth, not a stopgap. */
|
|
200
|
+
actionToken: string;
|
|
201
|
+
fetchDetails: boolean;
|
|
202
|
+
fetchCvv: boolean;
|
|
203
|
+
/** Exact rows to render, in order. When set, drives which values are fetched
|
|
204
|
+
* (number/expiry → `/details`, cvv → `/cvv`, cardholder → none) and overrides
|
|
205
|
+
* `fetchDetails`/`fetchCvv`. Used to put ONE field per iframe. */
|
|
206
|
+
fields?: PciRevealField[];
|
|
207
|
+
/** Shown as a plain row; comes from the (non-sensitive) cards list. */
|
|
208
|
+
cardholderName?: string;
|
|
209
|
+
appearance?: PciAppearance;
|
|
210
|
+
strings?: PciFrameStrings;
|
|
211
|
+
}
|
|
212
|
+
export type PciInitConfig = PciCollectInitConfig | PciCollectFieldInitConfig | PciRevealInitConfig;
|
|
213
|
+
/** Extra non-sensitive data merged into the tokenize call by the frame. */
|
|
214
|
+
export interface PciTokenizeTopUpRequest {
|
|
215
|
+
kind: 'topup';
|
|
216
|
+
/** snake_case fragment of the card-service DTO (non-CHD fields only). */
|
|
217
|
+
billingDetails: {
|
|
218
|
+
address_line1: string;
|
|
219
|
+
address_line2?: string;
|
|
220
|
+
postal_code: string;
|
|
221
|
+
country: string;
|
|
222
|
+
city: string;
|
|
223
|
+
state?: string;
|
|
224
|
+
};
|
|
225
|
+
cardholderLabel?: string;
|
|
226
|
+
}
|
|
227
|
+
export interface PciTokenizeOctRequest {
|
|
228
|
+
kind: 'oct-destination';
|
|
229
|
+
cardholderName: string;
|
|
230
|
+
cardLabel: string;
|
|
231
|
+
isSaved: boolean;
|
|
232
|
+
isThirdParty: boolean;
|
|
233
|
+
userId?: string;
|
|
234
|
+
}
|
|
235
|
+
export type PciTokenizeRequest = (PciTokenizeTopUpRequest | PciTokenizeOctRequest) & {
|
|
236
|
+
/** Fresh bearer, re-fetched at tokenize time (init's copy may be stale). */
|
|
237
|
+
bearerToken: string;
|
|
238
|
+
/** Per-field mode only: the sibling fields the aggregator must gather over
|
|
239
|
+
* the BroadcastChannel before it POSTs (empty for single-form frames and
|
|
240
|
+
* for the OCT profile, where only `number` exists). */
|
|
241
|
+
expectedFields?: PciCollectFieldName[];
|
|
242
|
+
};
|
|
243
|
+
/** What the frame returns to the host — token + out-of-PCI-scope metadata only. */
|
|
244
|
+
export interface PciTokenizeTopUpResult {
|
|
245
|
+
cardId: string;
|
|
246
|
+
brand: PciCardBrand;
|
|
247
|
+
bin: string;
|
|
248
|
+
last4: string;
|
|
249
|
+
expiryMonth: number;
|
|
250
|
+
expiryYear: number;
|
|
251
|
+
}
|
|
252
|
+
export interface PciTokenizeOctResult {
|
|
253
|
+
externalCardId: string;
|
|
254
|
+
brand: PciCardBrand;
|
|
255
|
+
bin: string;
|
|
256
|
+
last4: string;
|
|
257
|
+
}
|
|
258
|
+
export interface PciFrameError {
|
|
259
|
+
message: string;
|
|
260
|
+
status?: number;
|
|
261
|
+
}
|
|
262
|
+
interface Envelope {
|
|
263
|
+
v: typeof PCI_PROTOCOL_VERSION;
|
|
264
|
+
channel: string;
|
|
265
|
+
}
|
|
266
|
+
export type PciHostToFrameMessage = Envelope & ({
|
|
267
|
+
type: 'init';
|
|
268
|
+
requestId: string;
|
|
269
|
+
config: PciInitConfig;
|
|
270
|
+
} | {
|
|
271
|
+
type: 'tokenize';
|
|
272
|
+
requestId: string;
|
|
273
|
+
payload: PciTokenizeRequest;
|
|
274
|
+
});
|
|
275
|
+
export type PciFrameToHostMessage = Envelope & ({
|
|
276
|
+
type: 'ready';
|
|
277
|
+
} | {
|
|
278
|
+
type: 'change';
|
|
279
|
+
state: PciCollectChangeState;
|
|
280
|
+
} | {
|
|
281
|
+
type: 'resize';
|
|
282
|
+
height: number;
|
|
283
|
+
} | {
|
|
284
|
+
type: 'reveal-state';
|
|
285
|
+
state: PciRevealState;
|
|
286
|
+
} | {
|
|
287
|
+
type: 'result';
|
|
288
|
+
requestId: string;
|
|
289
|
+
ok: true;
|
|
290
|
+
data: unknown;
|
|
291
|
+
} | {
|
|
292
|
+
type: 'result';
|
|
293
|
+
requestId: string;
|
|
294
|
+
ok: false;
|
|
295
|
+
error: PciFrameError;
|
|
296
|
+
});
|
|
297
|
+
export {};
|
|
298
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postMessage protocol between the host-side SDK and the PCI frames.
|
|
3
|
+
*
|
|
4
|
+
* The frames live in `apps/web/sdk-service/frames-src/src/*` (TypeScript) and
|
|
5
|
+
* `import type` these same definitions, so the message SHAPES cannot drift. The
|
|
6
|
+
* `PCI_PROTOCOL_VERSION` value is injected into the frame bundle by esbuild from
|
|
7
|
+
* `protocolVersion.json` (a single source); frames reject mismatched versions.
|
|
8
|
+
* Note: esbuild does not type-check, so the shape guarantee is only enforced by
|
|
9
|
+
* the frame service's separate `tsc` (see apps/web/sdk-service/tsconfig.json).
|
|
10
|
+
*
|
|
11
|
+
* Security model: every message carries the per-mount `channel` id the host
|
|
12
|
+
* generated; the host additionally verifies `event.source` is the owned iframe's
|
|
13
|
+
* contentWindow and `event.origin` is the configured frame origin. The frame
|
|
14
|
+
* binds itself to the first `init` sender and its origin. The frames are served
|
|
15
|
+
* cross-origin by the PCI SDK service (`apps/web/sdk-service`), so this is a real
|
|
16
|
+
* origin boundary — host-page scripts cannot read the frame contents.
|
|
17
|
+
*/
|
|
18
|
+
import { version as protocolVersion } from './protocolVersion.js';
|
|
19
|
+
/** Single source of truth for the protocol version, shared with the frame
|
|
20
|
+
* build in `apps/web/sdk-service` (esbuild reads the same JSON and injects it),
|
|
21
|
+
* so the host SDK and the vanilla frames can never drift. */
|
|
22
|
+
export const PCI_PROTOCOL_VERSION = protocolVersion;
|
|
23
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElE;;8DAE8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAW,eAAe,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of the postMessage protocol version, shared by the loader
|
|
3
|
+
* packages (imported here) and the frame bundles (the frame build in
|
|
4
|
+
* `sdk-service/scripts/build-frames.mjs` reads this file and injects the
|
|
5
|
+
* value as `__PCI_PROTOCOL_VERSION__`). Bump on breaking protocol changes.
|
|
6
|
+
*
|
|
7
|
+
* A plain TS module (not JSON) so the published ESM needs no JSON import
|
|
8
|
+
* attributes — plain-Node consumers resolve it like any other module.
|
|
9
|
+
*/
|
|
10
|
+
export declare const version = 1;
|
|
11
|
+
//# sourceMappingURL=protocolVersion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolVersion.d.ts","sourceRoot":"","sources":["../src/protocolVersion.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,IAAI,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of the postMessage protocol version, shared by the loader
|
|
3
|
+
* packages (imported here) and the frame bundles (the frame build in
|
|
4
|
+
* `sdk-service/scripts/build-frames.mjs` reads this file and injects the
|
|
5
|
+
* value as `__PCI_PROTOCOL_VERSION__`). Bump on breaking protocol changes.
|
|
6
|
+
*
|
|
7
|
+
* A plain TS module (not JSON) so the published ESM needs no JSON import
|
|
8
|
+
* attributes — plain-Node consumers resolve it like any other module.
|
|
9
|
+
*/
|
|
10
|
+
export const version = 1;
|
|
11
|
+
//# sourceMappingURL=protocolVersion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolVersion.js","sourceRoot":"","sources":["../src/protocolVersion.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wirexapp/card-sdk-core",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Shared foundation of the Wirex PCI card SDK: postMessage protocol, frame channel, frame-URL building, collect machinery. CHD-free.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"typecheck": "yarn run -T tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "yarn clean && yarn build"
|
|
38
|
+
}
|
|
39
|
+
}
|