@mpgd/platform 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capability-conformance.js +103 -1
- package/dist/commerce-capabilities.d.ts +9 -0
- package/dist/commerce-capabilities.js +24 -0
- package/dist/commerce-conformance.d.ts +518 -0
- package/dist/commerce-conformance.js +2162 -0
- package/dist/commerce-contract.d.ts +222 -0
- package/dist/commerce-contract.js +1 -0
- package/dist/commerce-vectors.d.ts +277 -0
- package/dist/commerce-vectors.js +3089 -0
- package/dist/commerce.d.ts +303 -0
- package/dist/commerce.js +5623 -0
- package/dist/index.d.ts +53 -0
- package/package.json +18 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deriveLegacyCommerceCapabilities } from './commerce-capabilities.js';
|
|
1
2
|
export const platformCapabilityKeys = Object.freeze([
|
|
2
3
|
'nativeIap',
|
|
3
4
|
'nativeAds',
|
|
@@ -22,6 +23,7 @@ const allowedPlatformCapabilityKeys = Object.freeze([
|
|
|
22
23
|
const allowedPlatformCapabilityKeySet = new Set([
|
|
23
24
|
...allowedPlatformCapabilityKeys,
|
|
24
25
|
'providerAvailability',
|
|
26
|
+
'commerce',
|
|
25
27
|
]);
|
|
26
28
|
const optionalPlatformCapabilityKeySet = new Set(optionalPlatformCapabilityKeys);
|
|
27
29
|
const providerFeatureSet = new Set([
|
|
@@ -41,6 +43,19 @@ const providerAvailabilitySet = new Set([
|
|
|
41
43
|
'temporarily-unavailable',
|
|
42
44
|
'available',
|
|
43
45
|
]);
|
|
46
|
+
// The optional commerce v2 block follows the same rules as the boolean
|
|
47
|
+
// snapshot: exact keys, typed values, and a fresh object on every read.
|
|
48
|
+
const commerceCapabilityBlockKeys = Object.freeze({
|
|
49
|
+
purchase: ['consumable', 'durable', 'subscription', 'quantity'],
|
|
50
|
+
catalog: ['products', 'offers', 'localizedPrice'],
|
|
51
|
+
lifecycle: ['availablePurchases', 'pending', 'restore', 'events', 'finish'],
|
|
52
|
+
authority: ['evidence', 'accountBinding'],
|
|
53
|
+
});
|
|
54
|
+
const commerceCapabilityEnums = Object.freeze({
|
|
55
|
+
'lifecycle.finish': ['client', 'server', 'host', 'none'],
|
|
56
|
+
'authority.evidence': ['client-token', 'host-callback', 'adapter-settled', 'server-push'],
|
|
57
|
+
'authority.accountBinding': ['required', 'optional', 'host'],
|
|
58
|
+
});
|
|
44
59
|
/**
|
|
45
60
|
* Runs provider-neutral capability checks against real adapter gateways or
|
|
46
61
|
* target-configured wrappers. Every read must return a complete boolean
|
|
@@ -116,6 +131,55 @@ function assertCapabilitySnapshot(actual, expected) {
|
|
|
116
131
|
}
|
|
117
132
|
assertProviderAvailabilityShape(actual.providerAvailability);
|
|
118
133
|
assertEqual(normalizedAvailability(actual.providerAvailability), normalizedAvailability(expected.providerAvailability), 'provider availability must match the expected provider state');
|
|
134
|
+
assertCommerceCapabilityBlock(actual, expected.commerce);
|
|
135
|
+
}
|
|
136
|
+
function assertCommerceCapabilityBlock(snapshot, expected) {
|
|
137
|
+
const actual = snapshot.commerce;
|
|
138
|
+
if (actual === undefined) {
|
|
139
|
+
assert(expected === undefined, 'commerce capabilities must be present when the fixture expects them');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
assert(expected !== undefined, 'commerce capabilities must be absent when the fixture omits them');
|
|
143
|
+
assert(typeof actual === 'object' && actual !== null && !Array.isArray(actual), 'commerce capabilities must be a non-array record');
|
|
144
|
+
const block = actual;
|
|
145
|
+
const blockKeys = Object.keys(block).sort();
|
|
146
|
+
assertEqual(JSON.stringify(blockKeys), JSON.stringify([...Object.keys(commerceCapabilityBlockKeys), 'availability'].sort()), 'commerce capabilities must contain exactly purchase, catalog, lifecycle, authority, and availability');
|
|
147
|
+
for (const [group, keys] of Object.entries(commerceCapabilityBlockKeys)) {
|
|
148
|
+
const nested = block[group];
|
|
149
|
+
assert(typeof nested === 'object' && nested !== null && !Array.isArray(nested), `commerce ${group} capabilities must be a non-array record`);
|
|
150
|
+
const record = nested;
|
|
151
|
+
assertEqual(JSON.stringify(Object.keys(record).sort()), JSON.stringify([...keys].sort()), `commerce ${group} capabilities must contain exactly ${keys.join(', ')}`);
|
|
152
|
+
for (const key of keys) {
|
|
153
|
+
const path = `${group}.${key}`;
|
|
154
|
+
const allowed = commerceCapabilityEnums[path];
|
|
155
|
+
if (allowed === undefined) {
|
|
156
|
+
assertEqual(typeof record[key], 'boolean', `commerce capability ${path} must be a boolean`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
assert(allowed.includes(record[key]), `commerce capability ${path} must be one of ${allowed.join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
assert(providerAvailabilitySet.has(block.availability), 'commerce availability must be a known readiness state');
|
|
164
|
+
assertEqual(normalizedCommerce(block), normalizedCommerce(expected), 'commerce capabilities must match the expected provider state');
|
|
165
|
+
const legacy = deriveLegacyCommerceCapabilities(block);
|
|
166
|
+
assertEqual(snapshot.nativeIap, legacy.nativeIap, 'nativeIap must equal the value derived from the commerce capabilities');
|
|
167
|
+
assertEqual(snapshot.subscriptionIap ?? false, legacy.subscriptionIap, 'subscriptionIap must equal the value derived from the commerce capabilities');
|
|
168
|
+
}
|
|
169
|
+
function normalizedCommerce(value) {
|
|
170
|
+
if (value === undefined) {
|
|
171
|
+
return 'undefined';
|
|
172
|
+
}
|
|
173
|
+
return JSON.stringify([
|
|
174
|
+
['purchase', sortedEntries(value.purchase)],
|
|
175
|
+
['catalog', sortedEntries(value.catalog)],
|
|
176
|
+
['lifecycle', sortedEntries(value.lifecycle)],
|
|
177
|
+
['authority', sortedEntries(value.authority)],
|
|
178
|
+
['availability', value.availability],
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
function sortedEntries(value) {
|
|
182
|
+
return Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
119
183
|
}
|
|
120
184
|
function assertProviderAvailabilityShape(value) {
|
|
121
185
|
if (value === undefined) {
|
|
@@ -133,6 +197,17 @@ function cloneExpectedCapabilities(value) {
|
|
|
133
197
|
...(value.providerAvailability === undefined
|
|
134
198
|
? {}
|
|
135
199
|
: { providerAvailability: { ...value.providerAvailability } }),
|
|
200
|
+
...(value.commerce === undefined
|
|
201
|
+
? {}
|
|
202
|
+
: {
|
|
203
|
+
commerce: {
|
|
204
|
+
purchase: { ...value.commerce.purchase },
|
|
205
|
+
catalog: { ...value.commerce.catalog },
|
|
206
|
+
lifecycle: { ...value.commerce.lifecycle },
|
|
207
|
+
authority: { ...value.commerce.authority },
|
|
208
|
+
availability: value.commerce.availability,
|
|
209
|
+
},
|
|
210
|
+
}),
|
|
136
211
|
};
|
|
137
212
|
}
|
|
138
213
|
function normalizedAvailability(value) {
|
|
@@ -156,12 +231,39 @@ function tryMutateSnapshot(snapshot) {
|
|
|
156
231
|
// A frozen nested readiness record already satisfies isolation.
|
|
157
232
|
}
|
|
158
233
|
}
|
|
234
|
+
if (snapshot.commerce !== undefined) {
|
|
235
|
+
tryMutateCommerceSnapshot(snapshot.commerce);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Every nested block is probed: a gateway may rebuild the outer object while
|
|
239
|
+
// reusing one mutable nested record across reads.
|
|
240
|
+
function tryMutateCommerceSnapshot(commerce) {
|
|
241
|
+
const probes = [
|
|
242
|
+
[commerce.purchase, 'consumable', !commerce.purchase.consumable],
|
|
243
|
+
[commerce.catalog, 'products', !commerce.catalog.products],
|
|
244
|
+
[commerce.lifecycle, 'events', !commerce.lifecycle.events],
|
|
245
|
+
[
|
|
246
|
+
commerce.authority,
|
|
247
|
+
'accountBinding',
|
|
248
|
+
commerce.authority.accountBinding === 'host' ? 'optional' : 'host',
|
|
249
|
+
],
|
|
250
|
+
[commerce, 'availability', commerce.availability === 'available' ? 'unsupported' : 'available'],
|
|
251
|
+
];
|
|
252
|
+
for (const [target, key, next] of probes) {
|
|
253
|
+
try {
|
|
254
|
+
Reflect.set(target, key, next);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// A frozen nested commerce block already satisfies isolation.
|
|
258
|
+
}
|
|
259
|
+
}
|
|
159
260
|
}
|
|
160
261
|
function hasCapabilityDifference(first, second) {
|
|
161
262
|
return allowedPlatformCapabilityKeys
|
|
162
263
|
.some((key) => (first[key] ?? false) !== (second[key] ?? false))
|
|
163
264
|
|| normalizedAvailability(first.providerAvailability)
|
|
164
|
-
!== normalizedAvailability(second.providerAvailability)
|
|
265
|
+
!== normalizedAvailability(second.providerAvailability)
|
|
266
|
+
|| normalizedCommerce(first.commerce) !== normalizedCommerce(second.commerce);
|
|
165
267
|
}
|
|
166
268
|
function assert(condition, message) {
|
|
167
269
|
if (!condition) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommerceCapabilities } from './index.js';
|
|
2
|
+
export interface LegacyCommerceCapabilities {
|
|
3
|
+
readonly nativeIap: boolean;
|
|
4
|
+
readonly subscriptionIap: boolean;
|
|
5
|
+
}
|
|
6
|
+
/** Transition rule for the boolean `PlatformCapabilities` flags. */
|
|
7
|
+
export declare function deriveLegacyCommerceCapabilities(capabilities: CommerceCapabilities): LegacyCommerceCapabilities;
|
|
8
|
+
/** A fresh all-false snapshot; callers must not share or mutate a returned object. */
|
|
9
|
+
export declare function createUnsupportedCommerceCapabilities(): CommerceCapabilities;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Transition rule for the boolean `PlatformCapabilities` flags. */
|
|
2
|
+
export function deriveLegacyCommerceCapabilities(capabilities) {
|
|
3
|
+
return {
|
|
4
|
+
nativeIap: capabilities.availability === 'available'
|
|
5
|
+
&& (capabilities.purchase.consumable || capabilities.purchase.durable),
|
|
6
|
+
subscriptionIap: capabilities.purchase.subscription,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/** A fresh all-false snapshot; callers must not share or mutate a returned object. */
|
|
10
|
+
export function createUnsupportedCommerceCapabilities() {
|
|
11
|
+
return {
|
|
12
|
+
purchase: { consumable: false, durable: false, subscription: false, quantity: false },
|
|
13
|
+
catalog: { products: false, offers: false, localizedPrice: false },
|
|
14
|
+
lifecycle: {
|
|
15
|
+
availablePurchases: false,
|
|
16
|
+
pending: false,
|
|
17
|
+
restore: false,
|
|
18
|
+
events: false,
|
|
19
|
+
finish: 'none',
|
|
20
|
+
},
|
|
21
|
+
authority: { evidence: 'client-token', accountBinding: 'optional' },
|
|
22
|
+
availability: 'unsupported',
|
|
23
|
+
};
|
|
24
|
+
}
|