@mpgd/adapter-verse8 0.1.0 → 0.2.1

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/index.js CHANGED
@@ -1,7 +1,27 @@
1
- import { Verse8 } from '@verse8/platform/vanilla';
1
+ import { Verse8Ads, } from '@verse8/ads';
2
+ import { Verse8, VXShop } from '@verse8/platform/vanilla';
2
3
  import { createUnsupportedCapabilities, } from '@mpgd/platform';
4
+ import { verse8AdsRewardEvidenceSchema } from './ads-contract.js';
5
+ export { verse8AdsRewardEvidenceSchema } from './ads-contract.js';
6
+ export function createVerse8CommerceProducts(catalog) {
7
+ return normalizeCommerceProducts(catalog.products.flatMap((product) => {
8
+ const platformProductId = product.platformProductIds.verse8;
9
+ return platformProductId === undefined || product.grant.type === 'resource'
10
+ ? []
11
+ : [
12
+ {
13
+ id: product.id,
14
+ type: product.type,
15
+ platformProductId,
16
+ },
17
+ ];
18
+ }));
19
+ }
3
20
  export function createVerse8PlatformGateway(options = {}) {
4
21
  const authClient = options.authClient ?? createDefaultAuthClient();
22
+ const adsClient = options.adsClient ?? Verse8Ads;
23
+ const adsAvailable = options.resolveAdPlacementId !== undefined;
24
+ const commerce = createVerse8Commerce(options.vxShop);
5
25
  const pauseListeners = new Set();
6
26
  const resumeListeners = new Set();
7
27
  const visibility = options.visibility ?? resolveVisibilitySource();
@@ -17,6 +37,11 @@ export function createVerse8PlatformGateway(options = {}) {
17
37
  async getCapabilities() {
18
38
  return {
19
39
  ...createUnsupportedCapabilities(),
40
+ nativeIap: commerce !== undefined,
41
+ nativeAds: adsAvailable,
42
+ rewardedAds: adsAvailable,
43
+ interstitialAds: adsAvailable,
44
+ cloudSave: options.agent8Storage !== undefined,
20
45
  localizedContent: true,
21
46
  };
22
47
  },
@@ -28,32 +53,84 @@ export function createVerse8PlatformGateway(options = {}) {
28
53
  return resolveVerse8Identity(authClient).session;
29
54
  },
30
55
  },
31
- commerce: {
32
- async getProducts() {
33
- return [];
34
- },
35
- async purchase() {
36
- return {
37
- status: 'failed',
38
- entitlementIds: [],
39
- };
40
- },
41
- async getEntitlements() {
42
- return [];
43
- },
44
- },
56
+ commerce: commerce ?? createUnavailableVerse8Commerce(),
45
57
  ads: {
46
58
  async preload() { },
47
- async showRewarded() {
48
- return {
49
- status: 'unavailable',
50
- rewardGranted: false,
51
- };
59
+ async showRewarded(input) {
60
+ const placementId = options.resolveAdPlacementId?.(input.placementId);
61
+ if (placementId === undefined) {
62
+ return unavailableReward();
63
+ }
64
+ try {
65
+ const result = await adsClient.showRewarded({
66
+ placementId,
67
+ ...(options.adsTimeoutMs === undefined
68
+ ? {}
69
+ : { timeoutMs: options.adsTimeoutMs }),
70
+ meta: {
71
+ logicalPlacementId: input.placementId,
72
+ },
73
+ });
74
+ if (result.status === 'rewarded') {
75
+ return {
76
+ status: 'completed',
77
+ rewardGranted: true,
78
+ ledgerEntryId: result.requestId,
79
+ evidence: {
80
+ schema: verse8AdsRewardEvidenceSchema,
81
+ payload: {
82
+ requestId: result.requestId,
83
+ placementId,
84
+ ...(result.platform === undefined ? {} : { platform: result.platform }),
85
+ },
86
+ },
87
+ };
88
+ }
89
+ if (result.status === 'dismissed') {
90
+ return {
91
+ status: 'skipped',
92
+ rewardGranted: false,
93
+ };
94
+ }
95
+ return result.error.code === 'unsupported_env'
96
+ ? unavailableReward()
97
+ : {
98
+ status: 'failed',
99
+ rewardGranted: false,
100
+ };
101
+ }
102
+ catch {
103
+ return {
104
+ status: 'failed',
105
+ rewardGranted: false,
106
+ };
107
+ }
52
108
  },
53
- async showInterstitial() {
54
- return {
55
- status: 'unavailable',
56
- };
109
+ async showInterstitial(input) {
110
+ const placementId = options.resolveAdPlacementId?.(input.placementId);
111
+ if (placementId === undefined) {
112
+ return { status: 'unavailable' };
113
+ }
114
+ try {
115
+ const result = await adsClient.showInterstitial({
116
+ placementId,
117
+ ...(options.adsTimeoutMs === undefined
118
+ ? {}
119
+ : { timeoutMs: options.adsTimeoutMs }),
120
+ meta: {
121
+ logicalPlacementId: input.placementId,
122
+ },
123
+ });
124
+ if (result.status === 'dismissed') {
125
+ return { status: 'shown' };
126
+ }
127
+ return result.error.code === 'unsupported_env'
128
+ ? { status: 'unavailable' }
129
+ : { status: 'skipped' };
130
+ }
131
+ catch {
132
+ return { status: 'skipped' };
133
+ }
57
134
  },
58
135
  },
59
136
  leaderboard: {
@@ -76,15 +153,21 @@ export function createVerse8PlatformGateway(options = {}) {
76
153
  },
77
154
  storage: {
78
155
  async load(input) {
79
- const storage = options.storage ?? resolveStorage();
80
- const value = storage?.getItem(storageKey(input.key));
156
+ if (options.agent8Storage !== undefined) {
157
+ return options.agent8Storage.load(input);
158
+ }
159
+ const storage = resolveStorage(options.storage);
160
+ const value = storage.getItem(storageKey(authClient, input.key));
81
161
  return value === undefined || value === null
82
162
  ? null
83
163
  : { value: JSON.parse(value) };
84
164
  },
85
165
  async save(input) {
86
- const storage = options.storage ?? resolveStorage();
87
- storage?.setItem(storageKey(input.key), JSON.stringify(input.value));
166
+ if (options.agent8Storage !== undefined) {
167
+ await options.agent8Storage.save(input);
168
+ return;
169
+ }
170
+ resolveStorage(options.storage).setItem(storageKey(authClient, input.key), serializeStorageValue(input.value));
88
171
  },
89
172
  },
90
173
  presentation: {
@@ -99,6 +182,144 @@ export function createVerse8PlatformGateway(options = {}) {
99
182
  },
100
183
  };
101
184
  }
185
+ function createVerse8Commerce(options) {
186
+ if (options === undefined) {
187
+ return undefined;
188
+ }
189
+ if (options.purchaseEventAuthority !== 'agent8-server' || options.products.length === 0) {
190
+ return undefined;
191
+ }
192
+ const products = normalizeCommerceProducts(options.products);
193
+ const client = options.client ?? VXShop;
194
+ let initialized = false;
195
+ const ensureInitialized = () => {
196
+ if (initialized) {
197
+ return;
198
+ }
199
+ if (options.init === undefined) {
200
+ client.init({ autoRefresh: false });
201
+ }
202
+ else {
203
+ client.init({ ...options.init, autoRefresh: false });
204
+ }
205
+ initialized = true;
206
+ };
207
+ const loadEntitlements = async () => {
208
+ try {
209
+ return [...await options.loadEntitlements()];
210
+ }
211
+ catch {
212
+ return [];
213
+ }
214
+ };
215
+ return {
216
+ async getProducts() {
217
+ try {
218
+ ensureInitialized();
219
+ await client.refresh();
220
+ const shopItems = new Map(client.getItems().map((item) => [item.productId, item]));
221
+ return products.flatMap((product) => {
222
+ const item = shopItems.get(product.platformProductId);
223
+ return item === undefined
224
+ ? []
225
+ : [{
226
+ id: product.id,
227
+ type: product.type,
228
+ title: item.name,
229
+ description: item.description,
230
+ price: {
231
+ formatted: `${item.price} VX`,
232
+ currencyCode: 'VX',
233
+ },
234
+ }];
235
+ });
236
+ }
237
+ catch {
238
+ return [];
239
+ }
240
+ },
241
+ async purchase(input) {
242
+ const product = products.find((candidate) => candidate.id === input.productId);
243
+ if (product === undefined) {
244
+ return failedPurchase();
245
+ }
246
+ try {
247
+ if (!(options.canOpenShop ?? canOpenDefaultVerse8Shop)()) {
248
+ return failedPurchase();
249
+ }
250
+ ensureInitialized();
251
+ await client.refresh();
252
+ const item = client.getItem(product.platformProductId);
253
+ if (item === undefined
254
+ || item.purchasable !== true
255
+ || item.purchaseLimitReached === true) {
256
+ return failedPurchase();
257
+ }
258
+ client.buyItem(product.platformProductId);
259
+ return {
260
+ status: 'pending',
261
+ entitlementIds: [],
262
+ };
263
+ }
264
+ catch {
265
+ return failedPurchase();
266
+ }
267
+ },
268
+ async restore() {
269
+ return {
270
+ restoredEntitlements: await loadEntitlements(),
271
+ };
272
+ },
273
+ getEntitlements: loadEntitlements,
274
+ };
275
+ }
276
+ function normalizeCommerceProducts(products) {
277
+ const logicalIds = new Set();
278
+ const platformIds = new Set();
279
+ return products.map((product) => {
280
+ const platformProductId = product.platformProductId.trim();
281
+ if (product.id.trim().length === 0 || platformProductId.length === 0) {
282
+ throw new Error('Verse8 commerce product IDs must be non-empty.');
283
+ }
284
+ if (logicalIds.has(product.id) || platformIds.has(platformProductId)) {
285
+ throw new Error('Verse8 commerce product IDs must be unique.');
286
+ }
287
+ logicalIds.add(product.id);
288
+ platformIds.add(platformProductId);
289
+ return {
290
+ ...product,
291
+ platformProductId,
292
+ };
293
+ });
294
+ }
295
+ function createUnavailableVerse8Commerce() {
296
+ return {
297
+ async getProducts() {
298
+ return [];
299
+ },
300
+ async purchase() {
301
+ return failedPurchase();
302
+ },
303
+ async getEntitlements() {
304
+ return [];
305
+ },
306
+ };
307
+ }
308
+ function failedPurchase() {
309
+ return {
310
+ status: 'failed',
311
+ entitlementIds: [],
312
+ };
313
+ }
314
+ function canOpenDefaultVerse8Shop() {
315
+ return typeof window !== 'undefined' && window.parent !== window;
316
+ }
317
+ function unavailableReward() {
318
+ return {
319
+ status: 'unavailable',
320
+ rewardGranted: false,
321
+ };
322
+ }
102
323
  function createDefaultAuthClient() {
103
324
  return {
104
325
  getUser(options) {
@@ -139,11 +360,31 @@ function createResolvedIdentity(credential, identityLevel, trustLevel) {
139
360
  },
140
361
  };
141
362
  }
142
- function storageKey(key) {
143
- return `mpgd:verse8:${key}`;
363
+ function storageKey(authClient, key) {
364
+ const playerId = resolveVerse8Identity(authClient).player?.playerId;
365
+ const identityNamespace = playerId === undefined ? 'guest' : playerId.toLowerCase();
366
+ return `mpgd:verse8:${identityNamespace}:${key}`;
144
367
  }
145
- function resolveStorage() {
146
- return typeof localStorage === 'undefined' ? undefined : localStorage;
368
+ function resolveStorage(override) {
369
+ if (override !== undefined) {
370
+ return override;
371
+ }
372
+ try {
373
+ if (globalThis.localStorage !== undefined) {
374
+ return globalThis.localStorage;
375
+ }
376
+ }
377
+ catch {
378
+ // Access to browser storage can be denied by the embedding environment.
379
+ }
380
+ throw new Error('Verse8 local storage is unavailable.');
381
+ }
382
+ function serializeStorageValue(value) {
383
+ const serialized = JSON.stringify(value);
384
+ if (typeof serialized !== 'string') {
385
+ throw new Error('Verse8 local storage values must be JSON serializable.');
386
+ }
387
+ return serialized;
147
388
  }
148
389
  function resolveVisibilitySource() {
149
390
  return typeof document === 'undefined' ? undefined : document;
@@ -0,0 +1,40 @@
1
+ import type { GameServicesEvidenceVerifier } from '@mpgd/game-services';
2
+ export declare const defaultVerse8AdsVerifierBaseUrl = "https://ads-verifier.verse8.io";
3
+ export interface Verse8AdsVerifierReward {
4
+ readonly amount: number;
5
+ readonly type: string;
6
+ }
7
+ export interface Verse8AdsVerificationRecord {
8
+ readonly verified: boolean;
9
+ readonly status?: 'verified' | 'consumed' | 'pending';
10
+ readonly reward?: Verse8AdsVerifierReward;
11
+ readonly requestId?: string;
12
+ readonly placementId?: string;
13
+ readonly userId?: string;
14
+ readonly adNetwork?: 'google';
15
+ readonly verifiedAt?: string;
16
+ readonly consumedAt?: string;
17
+ readonly error?: string;
18
+ }
19
+ export interface Verse8AdsVerifierClient {
20
+ consume(input: {
21
+ readonly requestId: string;
22
+ readonly signal: AbortSignal;
23
+ }): Promise<Verse8AdsVerificationRecord>;
24
+ }
25
+ export type Verse8AdsVerifierAuthorization = string | (() => string | Promise<string>);
26
+ export interface CreateVerse8AdsVerifierHttpClientInput {
27
+ readonly authorization: Verse8AdsVerifierAuthorization;
28
+ readonly baseUrl?: string;
29
+ readonly fetch?: typeof fetch;
30
+ }
31
+ export interface CreateVerse8AdsEvidenceVerifierInput {
32
+ readonly client: Verse8AdsVerifierClient;
33
+ }
34
+ export declare class Verse8AdsVerifierHttpError extends Error {
35
+ readonly status: number;
36
+ readonly code: string | undefined;
37
+ constructor(status: number, code?: string);
38
+ }
39
+ export declare function createVerse8AdsVerifierHttpClient(input: CreateVerse8AdsVerifierHttpClientInput): Verse8AdsVerifierClient;
40
+ export declare function createVerse8AdsEvidenceVerifier(input: CreateVerse8AdsEvidenceVerifierInput): GameServicesEvidenceVerifier;
package/dist/server.js ADDED
@@ -0,0 +1,258 @@
1
+ import { verse8AdsRewardEvidenceSchema } from './ads-contract.js';
2
+ export const defaultVerse8AdsVerifierBaseUrl = 'https://ads-verifier.verse8.io';
3
+ const maximumVerifierResponseLength = 65536;
4
+ export class Verse8AdsVerifierHttpError extends Error {
5
+ status;
6
+ code;
7
+ constructor(status, code) {
8
+ super(`Verse8 Ads verifier request failed with HTTP ${status}.`);
9
+ this.name = 'Verse8AdsVerifierHttpError';
10
+ this.status = status;
11
+ this.code = code;
12
+ }
13
+ }
14
+ export function createVerse8AdsVerifierHttpClient(input) {
15
+ const endpoint = verifierEndpoint(input.baseUrl ?? defaultVerse8AdsVerifierBaseUrl);
16
+ const fetcher = input.fetch ?? readGlobalFetch();
17
+ return {
18
+ async consume({ requestId, signal }) {
19
+ requireNonEmptyString(requestId, 'requestId');
20
+ const authorization = await resolveAuthorization(input.authorization);
21
+ const response = await fetcher(endpoint, {
22
+ method: 'POST',
23
+ headers: {
24
+ Accept: 'application/json',
25
+ Authorization: authorization,
26
+ 'Content-Type': 'application/json',
27
+ },
28
+ body: JSON.stringify({ requestId }),
29
+ signal,
30
+ });
31
+ const body = await readBoundedJson(response);
32
+ if (!response.ok && !isVerse8AdsVerificationRecord(body)) {
33
+ throw new Verse8AdsVerifierHttpError(response.status, readErrorCode(body));
34
+ }
35
+ const record = assertVerse8AdsVerificationRecord(body);
36
+ if (!response.ok && record.status !== 'consumed') {
37
+ throw new Verse8AdsVerifierHttpError(response.status, record.error);
38
+ }
39
+ return record;
40
+ },
41
+ };
42
+ }
43
+ export function createVerse8AdsEvidenceVerifier(input) {
44
+ return {
45
+ async verifyPurchase() {
46
+ return rejected('VERSE8_PURCHASE_EVIDENCE_UNSUPPORTED');
47
+ },
48
+ async verifyAdReward(verificationInput) {
49
+ return verifyVerse8AdReward(input.client, verificationInput);
50
+ },
51
+ };
52
+ }
53
+ async function verifyVerse8AdReward(client, input) {
54
+ const { request, platformPlacementId } = input;
55
+ if (request.target !== 'verse8') {
56
+ return rejected('VERSE8_AD_TARGET_REQUIRED');
57
+ }
58
+ if (platformPlacementId === undefined || platformPlacementId.length === 0) {
59
+ return rejected('VERSE8_AD_PLACEMENT_UNCONFIGURED');
60
+ }
61
+ const requestId = request.platformImpressionId;
62
+ if (requestId === undefined) {
63
+ return rejected('VERSE8_AD_REQUEST_ID_REQUIRED');
64
+ }
65
+ if (request.evidence?.schema !== verse8AdsRewardEvidenceSchema) {
66
+ return rejected('VERSE8_AD_EVIDENCE_SCHEMA_INVALID');
67
+ }
68
+ const evidenceRequestId = readEvidenceString(request.evidence.payload, 'requestId');
69
+ const evidencePlacementId = readEvidenceString(request.evidence.payload, 'placementId');
70
+ if (evidenceRequestId !== requestId) {
71
+ return rejected('VERSE8_AD_REQUEST_ID_MISMATCH');
72
+ }
73
+ if (evidencePlacementId !== platformPlacementId) {
74
+ return rejected('VERSE8_AD_PLACEMENT_MISMATCH');
75
+ }
76
+ const record = await client.consume({
77
+ requestId,
78
+ signal: input.signal,
79
+ });
80
+ if (record.status === 'pending') {
81
+ return {
82
+ status: 'pending',
83
+ reason: 'VERSE8_AD_EVIDENCE_PENDING',
84
+ };
85
+ }
86
+ if (record.status === 'consumed') {
87
+ return rejected('VERSE8_AD_EVIDENCE_CONSUMED');
88
+ }
89
+ if (record.status === 'verified' && !record.verified) {
90
+ return rejected('VERSE8_AD_VERIFIER_RESPONSE_INCONSISTENT');
91
+ }
92
+ if (!record.verified) {
93
+ return {
94
+ status: 'pending',
95
+ reason: 'VERSE8_AD_EVIDENCE_PENDING',
96
+ };
97
+ }
98
+ if (record.requestId !== requestId) {
99
+ return rejected('VERSE8_AD_VERIFIED_REQUEST_MISMATCH');
100
+ }
101
+ if (record.placementId !== platformPlacementId) {
102
+ return rejected('VERSE8_AD_VERIFIED_PLACEMENT_MISMATCH');
103
+ }
104
+ if (record.userId === undefined || !sameVerse8User(record.userId, request.playerId)) {
105
+ return rejected('VERSE8_AD_VERIFIED_USER_MISMATCH');
106
+ }
107
+ const verifiedAt = normalizeTimestamp(record.verifiedAt);
108
+ if (verifiedAt === undefined) {
109
+ return rejected('VERSE8_AD_VERIFIED_AT_INVALID');
110
+ }
111
+ return {
112
+ status: 'verified',
113
+ verificationId: `verse8:ad-reward:${requestId}`,
114
+ verifiedAt,
115
+ payload: {
116
+ verse8RequestId: requestId,
117
+ verse8PlacementId: platformPlacementId,
118
+ ...(record.adNetwork === undefined ? {} : { verse8AdNetwork: record.adNetwork }),
119
+ },
120
+ };
121
+ }
122
+ function verifierEndpoint(baseUrl) {
123
+ const url = new URL(baseUrl);
124
+ if (url.protocol !== 'https:') {
125
+ throw new Error('Verse8 Ads verifier baseUrl must use HTTPS.');
126
+ }
127
+ url.hash = '';
128
+ url.search = '';
129
+ url.pathname = `${url.pathname.replace(/\/+$/, '')}/ads/verify`;
130
+ return url.toString();
131
+ }
132
+ function readGlobalFetch() {
133
+ const fetcher = globalThis.fetch;
134
+ if (fetcher === undefined) {
135
+ throw new Error('globalThis.fetch is unavailable. Provide a Verse8 verifier fetch implementation.');
136
+ }
137
+ return fetcher.bind(globalThis);
138
+ }
139
+ async function resolveAuthorization(authorization) {
140
+ const resolved = typeof authorization === 'function' ? await authorization() : authorization;
141
+ requireNonEmptyString(resolved, 'authorization');
142
+ return resolved.trim();
143
+ }
144
+ async function readBoundedJson(response) {
145
+ const body = await readBoundedBody(response);
146
+ try {
147
+ return JSON.parse(body);
148
+ }
149
+ catch {
150
+ throw new Error('Verse8 Ads verifier response must be valid JSON.');
151
+ }
152
+ }
153
+ async function readBoundedBody(response) {
154
+ const declaredLength = response.headers.get('Content-Length');
155
+ if (declaredLength !== null && Number(declaredLength) > maximumVerifierResponseLength) {
156
+ throw new Error('Verse8 Ads verifier response exceeds the maximum size.');
157
+ }
158
+ if (response.body === null) {
159
+ return '';
160
+ }
161
+ const reader = response.body.getReader();
162
+ const decoder = new TextDecoder();
163
+ let byteLength = 0;
164
+ let body = '';
165
+ try {
166
+ while (true) {
167
+ const chunk = await reader.read();
168
+ if (chunk.done) {
169
+ return body + decoder.decode();
170
+ }
171
+ byteLength += chunk.value.byteLength;
172
+ if (byteLength > maximumVerifierResponseLength) {
173
+ await reader.cancel().catch(() => undefined);
174
+ throw new Error('Verse8 Ads verifier response exceeds the maximum size.');
175
+ }
176
+ body += decoder.decode(chunk.value, { stream: true });
177
+ }
178
+ }
179
+ finally {
180
+ reader.releaseLock();
181
+ }
182
+ }
183
+ function assertVerse8AdsVerificationRecord(input) {
184
+ if (!isVerse8AdsVerificationRecord(input)) {
185
+ throw new Error('Verse8 Ads verifier response has an invalid shape.');
186
+ }
187
+ return input;
188
+ }
189
+ function isVerse8AdsVerificationRecord(input) {
190
+ if (!isRecord(input) || typeof input.verified !== 'boolean') {
191
+ return false;
192
+ }
193
+ if (input.status !== undefined
194
+ && input.status !== 'verified'
195
+ && input.status !== 'consumed'
196
+ && input.status !== 'pending') {
197
+ return false;
198
+ }
199
+ if (!isOptionalString(input.requestId)
200
+ || !isOptionalString(input.placementId)
201
+ || !isOptionalString(input.userId)
202
+ || !isOptionalString(input.verifiedAt)
203
+ || !isOptionalString(input.consumedAt)
204
+ || !isOptionalString(input.error)) {
205
+ return false;
206
+ }
207
+ if (input.adNetwork !== undefined && input.adNetwork !== 'google') {
208
+ return false;
209
+ }
210
+ if (input.reward !== undefined) {
211
+ if (!isRecord(input.reward)
212
+ || typeof input.reward.amount !== 'number'
213
+ || !Number.isFinite(input.reward.amount)
214
+ || typeof input.reward.type !== 'string') {
215
+ return false;
216
+ }
217
+ }
218
+ return true;
219
+ }
220
+ function readErrorCode(input) {
221
+ return isRecord(input) && typeof input.error === 'string' ? input.error : undefined;
222
+ }
223
+ function readEvidenceString(payload, key) {
224
+ const value = payload[key];
225
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
226
+ }
227
+ function normalizeTimestamp(value) {
228
+ if (value === undefined) {
229
+ return undefined;
230
+ }
231
+ const timestamp = Date.parse(value);
232
+ return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : undefined;
233
+ }
234
+ function sameVerse8User(left, right) {
235
+ return isHexAccount(left) && isHexAccount(right)
236
+ ? left.toLowerCase() === right.toLowerCase()
237
+ : left === right;
238
+ }
239
+ function isHexAccount(value) {
240
+ return /^0x[0-9a-fA-F]+$/.test(value);
241
+ }
242
+ function rejected(reason) {
243
+ return {
244
+ status: 'rejected',
245
+ reason,
246
+ };
247
+ }
248
+ function isRecord(input) {
249
+ return typeof input === 'object' && input !== null && !Array.isArray(input);
250
+ }
251
+ function isOptionalString(input) {
252
+ return input === undefined || typeof input === 'string';
253
+ }
254
+ function requireNonEmptyString(input, label) {
255
+ if (input.trim().length === 0) {
256
+ throw new Error(`${label} must be a non-empty string.`);
257
+ }
258
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/adapter-verse8",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Verse8 iframe platform adapter for mpgd games.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,14 +27,25 @@
27
27
  ".": {
28
28
  "types": "./dist/index.d.ts",
29
29
  "default": "./dist/index.js"
30
+ },
31
+ "./server": {
32
+ "types": "./dist/server.d.ts",
33
+ "default": "./dist/server.js"
34
+ },
35
+ "./agent8": {
36
+ "types": "./dist/agent8.d.ts",
37
+ "default": "./dist/agent8.js"
30
38
  }
31
39
  },
32
40
  "files": [
33
41
  "dist"
34
42
  ],
35
43
  "dependencies": {
44
+ "@verse8/ads": "0.4.0",
36
45
  "@verse8/platform": "2.1.0",
37
- "@mpgd/platform": "0.6.0"
46
+ "@mpgd/catalog": "0.5.0",
47
+ "@mpgd/platform": "0.7.0",
48
+ "@mpgd/game-services": "0.10.0"
38
49
  },
39
50
  "devDependencies": {
40
51
  "ttsc": "0.18.4",
@@ -51,6 +62,6 @@
51
62
  "lint": "ttsc --noEmit",
52
63
  "format": "ttsc format",
53
64
  "fix": "ttsc fix",
54
- "test": "vitest run"
65
+ "test": "vitest run && node test/server-dist-import.mjs && node test/agent8-dist-import.mjs"
55
66
  }
56
67
  }