@virjay/playscope-cocos 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +314 -2
  3. package/USAGE.md +372 -0
  4. package/VALIDATION.md +93 -0
  5. package/dist/adapters/levelplay/index.d.ts +15 -0
  6. package/dist/adapters/levelplay/index.js +14 -0
  7. package/dist/cocos/Storage.d.ts +16 -0
  8. package/dist/cocos/Storage.js +16 -0
  9. package/dist/cocos/XhrJsonTransport.d.ts +8 -0
  10. package/dist/cocos/XhrJsonTransport.js +65 -0
  11. package/dist/cocos/index.d.ts +48 -0
  12. package/dist/cocos/index.js +134 -0
  13. package/dist/core/ClientOwnership.d.ts +6 -0
  14. package/dist/core/ClientOwnership.js +39 -0
  15. package/dist/core/ClientScope.d.ts +5 -0
  16. package/dist/core/ClientScope.js +34 -0
  17. package/dist/core/ConfigurationSync.d.ts +18 -0
  18. package/dist/core/ConfigurationSync.js +66 -0
  19. package/dist/core/FactEncoding.d.ts +10 -0
  20. package/dist/core/FactEncoding.js +29 -0
  21. package/dist/core/HttpError.d.ts +10 -0
  22. package/dist/core/HttpError.js +17 -0
  23. package/dist/core/OutboxStore.d.ts +31 -0
  24. package/dist/core/OutboxStore.js +228 -0
  25. package/dist/core/PersistentState.d.ts +33 -0
  26. package/dist/core/PersistentState.js +92 -0
  27. package/dist/core/PlayScopeClient.d.ts +65 -0
  28. package/dist/core/PlayScopeClient.js +422 -0
  29. package/dist/core/QueueAdmission.d.ts +4 -0
  30. package/dist/core/QueueAdmission.js +29 -0
  31. package/dist/core/index.d.ts +7 -0
  32. package/dist/core/index.js +27 -0
  33. package/dist/core/revenue.d.ts +33 -0
  34. package/dist/core/revenue.js +23 -0
  35. package/dist/core/types.d.ts +155 -0
  36. package/dist/core/types.js +2 -0
  37. package/dist/core/uuid.d.ts +1 -0
  38. package/dist/core/uuid.js +9 -0
  39. package/dist/esm/adapters/levelplay/index.js +11 -0
  40. package/dist/esm/cocos/Storage.js +12 -0
  41. package/dist/esm/cocos/XhrJsonTransport.js +61 -0
  42. package/dist/esm/cocos/index.js +115 -0
  43. package/dist/esm/core/ClientOwnership.js +34 -0
  44. package/dist/esm/core/ClientScope.js +30 -0
  45. package/dist/esm/core/ConfigurationSync.js +62 -0
  46. package/dist/esm/core/FactEncoding.js +24 -0
  47. package/dist/esm/core/HttpError.js +12 -0
  48. package/dist/esm/core/OutboxStore.js +224 -0
  49. package/dist/esm/core/PersistentState.js +85 -0
  50. package/dist/esm/core/PlayScopeClient.js +415 -0
  51. package/dist/esm/core/QueueAdmission.js +25 -0
  52. package/dist/esm/core/index.js +7 -0
  53. package/dist/esm/core/revenue.js +20 -0
  54. package/dist/esm/core/types.js +1 -0
  55. package/dist/esm/core/uuid.js +6 -0
  56. package/dist/esm/package.json +1 -0
  57. package/dist/esm/protocols/game-v1/index.js +21 -0
  58. package/dist/protocols/game-v1/index.d.ts +8 -0
  59. package/dist/protocols/game-v1/index.js +25 -0
  60. package/doc/README.md +4 -0
  61. package/doc//344/275/277/347/224/250/346/214/207/345/215/227/README.md +585 -0
  62. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/adapters/levelplay/index.d.ts +15 -0
  63. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/cocos/Storage.d.ts +13 -0
  64. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/cocos/XhrJsonTransport.d.ts +8 -0
  65. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/cocos/index.d.ts +38 -0
  66. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/core/PlayScopeClient.d.ts +46 -0
  67. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/core/index.d.ts +3 -0
  68. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/core/revenue.d.ts +25 -0
  69. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/0.1.1-types/core/types.d.ts +81 -0
  70. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/exports.json +89 -0
  71. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231/public-api.d.ts +281 -0
  72. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/client-context.schema.json +103 -0
  73. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/game-ad-properties.json +765 -0
  74. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/game-builtin-properties.json +479 -0
  75. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/game-event.json +278 -0
  76. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/product-events.json +2878 -0
  77. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257/standard-contract.json +183 -0
  78. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/234/215/345/212/241/347/253/257//344/272/247/345/223/201/344/272/213/344/273/266/347/264/242/345/274/225.md +87 -0
  79. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//345/217/202/350/200/203/350/265/204/346/226/231//346/235/245/346/272/220/344/270/216/347/211/210/346/234/254.json +35 -0
  80. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/CoreIntegration.ts +13 -0
  81. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/LevelPlayTelemetry.ts +119 -0
  82. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/PlayScopeExample.ts +46 -0
  83. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/QuestGarden.ts +129 -0
  84. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/README.md +23 -0
  85. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/project-fixture.json +188 -0
  86. package/doc//344/275/277/347/224/250/346/214/207/345/215/227//347/244/272/344/276/213/tsconfig.json +20 -0
  87. package/examples/cocos-minimal/PlayScopeExample.ts +27 -0
  88. package/examples/quest-garden/QuestGarden.ts +129 -0
  89. package/examples/quest-garden/README.md +180 -0
  90. package/examples/quest-garden/project-fixture.json +188 -0
  91. package/examples/quest-garden/tsconfig.json +12 -0
  92. package/package.json +73 -4
@@ -0,0 +1,46 @@
1
+ import type { StoragePort, JsonTransport, TrackContext, PlayScopeOptions, Diagnostics } from './types';
2
+ export declare function telemetryUuid(): string;
3
+ export declare class PlayScopeHttpError extends Error {
4
+ readonly status: number;
5
+ readonly retryAfterMs: number;
6
+ constructor(status: number, retryAfterMs?: number);
7
+ }
8
+ /** Durable API v1 outbox, independent of game schema and rendering engine. */
9
+ export declare class PlayScopeClient {
10
+ private readonly options;
11
+ private readonly storage;
12
+ private readonly transport;
13
+ private readonly now;
14
+ private readonly key;
15
+ private readonly api;
16
+ private state;
17
+ private sessionId;
18
+ private busy;
19
+ private disposed;
20
+ private blocked;
21
+ private retryAt;
22
+ private failures;
23
+ private configAt;
24
+ private batchLimit;
25
+ private lastError_;
26
+ private needsPersistence;
27
+ private identityPublished;
28
+ constructor(options: PlayScopeOptions, storage: StoragePort, transport: JsonTransport, now?: () => number);
29
+ get installId(): string;
30
+ get cache(): unknown;
31
+ saveCache(value: unknown): void;
32
+ get diagnostics(): Diagnostics;
33
+ /** Call after collection is allowed. Existing first-open facts are never recreated. */
34
+ start(name?: string, context?: TrackContext): void;
35
+ newSession(): void;
36
+ track(name: string, properties: Record<string, unknown>, context?: TrackContext): boolean;
37
+ private persist;
38
+ flush(): Promise<void>;
39
+ private makeBatch;
40
+ private ensureToken;
41
+ private authorized;
42
+ private post;
43
+ dispose(): void;
44
+ }
45
+ /** Works on JSB runtimes that don't provide TextEncoder. */
46
+ export declare function utf8Bytes(value: string): number;
@@ -0,0 +1,3 @@
1
+ export * from './types';
2
+ export * from './PlayScopeClient';
3
+ export * from './revenue';
@@ -0,0 +1,25 @@
1
+ import type { PlayScopeClient } from './PlayScopeClient';
2
+ import type { TrackContext } from './types';
3
+ export type AdPlacement = 'revive_video' | 'gameover_interstitial' | 'win_interstitial' | 'pause_interstitial' | 'hint_video' | 'win_double' | 'banner';
4
+ export interface AdRevenueFact {
5
+ /** Unique per exposure, including each banner refresh. Never use just the banner object's ID. */
6
+ exposureId: string;
7
+ placement: AdPlacement;
8
+ businessPlacement?: string;
9
+ format: 'banner' | 'rewarded' | 'interstitial' | 'app_open';
10
+ provider: string;
11
+ network: string;
12
+ adUnitId: string;
13
+ amount: number;
14
+ currency: 'USD';
15
+ precision: string;
16
+ isTest: boolean;
17
+ isMock?: boolean;
18
+ isDemo?: boolean;
19
+ country?: string;
20
+ countrySource?: 'levelplay_sdk' | 'admob_sdk';
21
+ shownAt?: string;
22
+ adConfigRevision?: string;
23
+ }
24
+ /** Unknown/negative income is discarded; a real zero is preserved. No currency conversion is guessed. */
25
+ export declare function trackAdRevenue(client: PlayScopeClient, fact: AdRevenueFact, context?: TrackContext): boolean;
@@ -0,0 +1,81 @@
1
+ /** API v1 ports. No Cocos or game imports are required by the core. */
2
+ export interface StoragePort {
3
+ getString(key: string): string | null;
4
+ setString(key: string, value: string): void;
5
+ flush(): void;
6
+ }
7
+ export interface HttpResponse {
8
+ status: number;
9
+ body: unknown;
10
+ retryAfterMs?: number;
11
+ }
12
+ export interface JsonTransport {
13
+ post(url: string, headers: Record<string, string>, body: unknown): Promise<HttpResponse>;
14
+ dispose?(): void;
15
+ }
16
+ export interface ClientContext {
17
+ appVersion: string;
18
+ buildNumber: string;
19
+ platform: 'android' | 'ios' | 'web' | 'wechat';
20
+ locale: string;
21
+ timezoneOffsetMinutes: number;
22
+ environment?: 'production' | 'test';
23
+ device?: {
24
+ type: 'android_id';
25
+ id: string;
26
+ };
27
+ }
28
+ export interface EventFact {
29
+ eventVersion: 1;
30
+ eventId: string;
31
+ name: string;
32
+ occurredAt: string;
33
+ sessionId: string;
34
+ sequence: number;
35
+ client: ClientContext;
36
+ samplingProbability: number;
37
+ run: unknown;
38
+ properties: Record<string, unknown>;
39
+ }
40
+ export interface TrackContext {
41
+ run?: unknown;
42
+ occurredAt?: string;
43
+ /** Used once at enqueue, never when replaying persisted facts. */
44
+ transform?: (event: EventFact) => EventFact;
45
+ /** Optional bounded, persistent duplicate callback guard. */
46
+ deduplicationKey?: string;
47
+ }
48
+ export interface ConfigurationContext {
49
+ readonly cache: unknown;
50
+ request(path: string, body: unknown): Promise<unknown>;
51
+ saveCache(value: unknown): void;
52
+ }
53
+ export interface PlayScopeOptions {
54
+ baseUrl: string;
55
+ projectId: string;
56
+ projectKey: string;
57
+ client: ClientContext;
58
+ /** Override only for an existing installation's historical storage namespace. */
59
+ storageKey?: string;
60
+ bootstrapClient?: ClientContext;
61
+ onIdentityReady?: (installId: string) => boolean | void;
62
+ /** Host validates its own config schema; the core never interprets game settings. */
63
+ restoreCache?: (saved: unknown) => unknown;
64
+ resolveConfiguration?: (context: ConfigurationContext) => Promise<number>;
65
+ }
66
+ export interface Diagnostics {
67
+ pending: number;
68
+ rejected: number;
69
+ droppedEvents: number;
70
+ lastError: string | null;
71
+ blocked: boolean;
72
+ }
73
+ export interface EventBatchResponse {
74
+ apiVersion: number;
75
+ acceptedEventIds: string[];
76
+ rejectedEvents: {
77
+ eventId: string;
78
+ code: string;
79
+ permanent: boolean;
80
+ }[];
81
+ }
@@ -0,0 +1,89 @@
1
+ {
2
+ "@virjay/playscope-cocos": [
3
+ "AdPlacement",
4
+ "AdRevenueEncoder",
5
+ "AdRevenueFact",
6
+ "ClientContext",
7
+ "ClientScope",
8
+ "ClientStatus",
9
+ "CocosLifecycle",
10
+ "CocosOptions",
11
+ "CocosPlayScope",
12
+ "CocosRuntime",
13
+ "CocosStorage",
14
+ "ConfigurationContext",
15
+ "ConfigurationResult",
16
+ "ConfigurationStatus",
17
+ "Diagnostics",
18
+ "EncodedRevenueEvent",
19
+ "EventBatchResponse",
20
+ "EventFact",
21
+ "EventSink",
22
+ "FlushResult",
23
+ "HttpResponse",
24
+ "InitializationErrorCode",
25
+ "JsonTransport",
26
+ "LocalStorageLike",
27
+ "PlayScopeClient",
28
+ "PlayScopeHttpError",
29
+ "PlayScopeInitializationError",
30
+ "PlayScopeOptions",
31
+ "PlayScopeOwnershipError",
32
+ "QuarantinedEvent",
33
+ "QueuePolicy",
34
+ "StoragePort",
35
+ "TrackContext",
36
+ "TrackResult",
37
+ "XhrJsonTransport",
38
+ "enqueueAdRevenue",
39
+ "init",
40
+ "telemetryUuid",
41
+ "trackAdRevenue",
42
+ "utf8Bytes"
43
+ ],
44
+ "@virjay/playscope-cocos/core": [
45
+ "AdPlacement",
46
+ "AdRevenueEncoder",
47
+ "AdRevenueFact",
48
+ "ClientContext",
49
+ "ClientScope",
50
+ "ClientStatus",
51
+ "ConfigurationContext",
52
+ "ConfigurationResult",
53
+ "ConfigurationStatus",
54
+ "Diagnostics",
55
+ "EncodedRevenueEvent",
56
+ "EventBatchResponse",
57
+ "EventFact",
58
+ "EventSink",
59
+ "FlushResult",
60
+ "HttpResponse",
61
+ "InitializationErrorCode",
62
+ "JsonTransport",
63
+ "PlayScopeClient",
64
+ "PlayScopeHttpError",
65
+ "PlayScopeInitializationError",
66
+ "PlayScopeOptions",
67
+ "PlayScopeOwnershipError",
68
+ "QuarantinedEvent",
69
+ "QueuePolicy",
70
+ "StoragePort",
71
+ "TrackContext",
72
+ "TrackResult",
73
+ "enqueueAdRevenue",
74
+ "telemetryUuid",
75
+ "trackAdRevenue",
76
+ "utf8Bytes"
77
+ ],
78
+ "@virjay/playscope-cocos/adapters/levelplay": [
79
+ "LevelPlayExposure",
80
+ "LevelPlayImpression",
81
+ "RevenueSink",
82
+ "forwardLevelPlayRevenue"
83
+ ],
84
+ "@virjay/playscope-cocos/protocols/game-v1": [
85
+ "GameV1AdPlacement",
86
+ "encodeGameV1AdRevenue",
87
+ "trackAdRevenue"
88
+ ]
89
+ }
@@ -0,0 +1,281 @@
1
+ // Documentation snapshot, 2026-11-12. Types only; not an SDK implementation.
2
+
3
+ /** API v1 ports. No Cocos or game imports are required by the core. */
4
+ export interface StoragePort {
5
+ /** Shared object identity for adapters wrapping the same underlying store in this JS runtime. */
6
+ readonly ownershipScope?: object;
7
+ /** null means absent. Read failures must throw; never convert them to null. */
8
+ getString(key: string): string | null;
9
+ /** Replace one key atomically; flush + read-back must expose that complete value or fail. */
10
+ setString(key: string, value: string): void;
11
+ /** Required only for segmented persistence; deletion failures must throw. */
12
+ removeString?(key: string): void;
13
+ flush(): void;
14
+ }
15
+ export interface HttpResponse { status: number; body: unknown; retryAfterMs?: number }
16
+ export interface JsonTransport {
17
+ post(url: string, headers: Record<string, string>, body: unknown): Promise<HttpResponse>;
18
+ dispose?(): void;
19
+ }
20
+ export interface ClientContext {
21
+ appVersion: string;
22
+ buildNumber: string;
23
+ platform: 'android' | 'ios' | 'web' | 'wechat';
24
+ locale: string;
25
+ timezoneOffsetMinutes: number;
26
+ environment?: 'production' | 'test';
27
+ device?: { type: 'android_id'; id: string };
28
+ }
29
+ /** Storage/delivery ownership, distinct from gameplay configuration and report attribution. */
30
+ export interface ClientScope {
31
+ baseUrl: string;
32
+ projectId: string;
33
+ environment: 'production' | 'test';
34
+ }
35
+ export interface EventFact {
36
+ eventVersion: 1;
37
+ eventId: string;
38
+ name: string;
39
+ occurredAt: string;
40
+ sessionId: string;
41
+ sequence: number;
42
+ client: ClientContext;
43
+ samplingProbability: number;
44
+ run: unknown;
45
+ properties: Record<string, unknown>;
46
+ }
47
+ export interface TrackContext {
48
+ run?: unknown;
49
+ occurredAt?: string;
50
+ /** Used once at enqueue, never when replaying persisted facts. */
51
+ transform?: (event: EventFact) => EventFact;
52
+ /** Optional bounded, persistent duplicate callback guard. */
53
+ deduplicationKey?: string;
54
+ }
55
+ export interface ConfigurationContext {
56
+ readonly cache: unknown;
57
+ request(path: string, body: unknown): Promise<unknown>;
58
+ saveCache(value: unknown): void;
59
+ }
60
+ export interface PlayScopeOptions {
61
+ baseUrl: string;
62
+ projectId: string;
63
+ projectKey: string;
64
+ client: ClientContext;
65
+ /** Override only for an existing installation's historical storage namespace. */
66
+ storageKey?: string;
67
+ bootstrapClient?: ClientContext;
68
+ onIdentityReady?: (installId: string) => boolean | void;
69
+ queuePolicy?: QueuePolicy;
70
+ /** Snapshot remains the compatibility default. Segmented data stays segmented on subsequent opens. */
71
+ persistence?: 'snapshot' | 'segmented';
72
+ /** Explicit provenance assertion when adopting an existing state with no scope metadata. */
73
+ legacyScope?: ClientScope;
74
+ /** Host validates its own config schema; the core never interprets game settings. */
75
+ restoreCache?: (saved: unknown) => unknown;
76
+ resolveConfiguration?: (context: ConfigurationContext) => Promise<number>;
77
+ }
78
+ export interface QueuePolicy {
79
+ /** Pending fact capacity. Defaults to 2000; does not evict an existing queue. */
80
+ readonly maxEvents?: number;
81
+ /** UTF-8 JSON bytes retained in pending + quarantine, excluding config/identity. Default 4 MiB. */
82
+ readonly maxBytes?: number;
83
+ /** Queue length at which this input event name is refused; reserve capacity for other events. */
84
+ readonly eventLimits?: Readonly<Record<string, number>>;
85
+ }
86
+ export interface Diagnostics {
87
+ pending: number;
88
+ rejected: number;
89
+ droppedEvents: number;
90
+ lastError: string | null;
91
+ blocked: boolean;
92
+ quarantined: number;
93
+ pendingBytes: number;
94
+ quarantinedBytes: number;
95
+ maxQueueBytes: number;
96
+ persistence: 'snapshot' | 'segmented';
97
+ cleanupPending: boolean;
98
+ }
99
+ /** Original local fact retained for inspection; never uploaded automatically again. */
100
+ export interface QuarantinedEvent {
101
+ event: EventFact;
102
+ reason: 'event_too_large' | 'http_413';
103
+ quarantinedAt: string;
104
+ }
105
+ export interface EventBatchResponse {
106
+ apiVersion: number;
107
+ acceptedEventIds: string[];
108
+ rejectedEvents: { eventId: string; code: string; permanent: boolean }[];
109
+ }
110
+
111
+ /** Accepted facts may still be memory-only when storage is temporarily unavailable. */
112
+ export type TrackResult =
113
+ | { status: 'accepted'; eventId: string; durability: 'persisted' | 'memory' }
114
+ | { status: 'duplicate' | 'queue_full' | 'queue_bytes_full' | 'event_too_large' | 'invalid_event' | 'collection_disabled' | 'disposed' };
115
+ export interface FlushResult {
116
+ status: 'completed' | 'backoff' | 'blocked' | 'storage_unavailable' | 'failed' | 'disposed' | 'collection_disabled';
117
+ /** Counts only facts explicitly ACKed in this operation; quarantine is separate. */
118
+ accepted: number;
119
+ rejected: number;
120
+ quarantined: number;
121
+ pending: number;
122
+ /** True only after the current local state has passed the storage read-back check. */
123
+ durable: boolean;
124
+ retryAt: number;
125
+ error: string | null;
126
+ }
127
+ export interface ConfigurationStatus {
128
+ state: 'disabled' | 'idle' | 'syncing' | 'backoff' | 'disposed';
129
+ nextAttemptAt: number;
130
+ lastError: string | null;
131
+ }
132
+ export interface ConfigurationResult {
133
+ status: 'refreshed' | 'not_due' | 'failed' | 'disabled' | 'disposed' | 'storage_unavailable';
134
+ nextAttemptAt: number;
135
+ error: string | null;
136
+ }
137
+ export interface ClientStatus {
138
+ collection: 'enabled' | 'disabled' | 'disposed';
139
+ delivery: 'idle' | 'uploading' | 'backoff' | 'blocked' | 'storage_unavailable' | 'disposed' | 'disabled';
140
+ pendingDurability: 'persisted' | 'memory';
141
+ retryAt: number;
142
+ configuration: ConfigurationStatus;
143
+ }
144
+
145
+ export type AdPlacement = string;
146
+ export interface AdRevenueFact {
147
+ /** Unique per exposure, including each banner refresh. Never use just the banner object's ID. */
148
+ exposureId: string;
149
+ placement: AdPlacement;
150
+ businessPlacement?: string;
151
+ format: 'banner' | 'rewarded' | 'interstitial' | 'app_open';
152
+ provider: string;
153
+ network: string;
154
+ adUnitId: string;
155
+ amount: number;
156
+ currency: 'USD';
157
+ precision: string;
158
+ isTest: boolean;
159
+ isMock?: boolean;
160
+ isDemo?: boolean;
161
+ country?: string;
162
+ countrySource?: string;
163
+ shownAt?: string;
164
+ adConfigRevision?: string;
165
+ }
166
+ export interface EncodedRevenueEvent { name: string; properties: Record<string, unknown> }
167
+ export type AdRevenueEncoder = (fact: Readonly<AdRevenueFact>) => EncodedRevenueEvent | null;
168
+ export interface EventSink {
169
+ track(name: string, properties: Record<string, unknown>, context?: TrackContext): boolean;
170
+ }
171
+ /** Validate the income fact; the explicit encoder owns event names and wire properties. */
172
+
173
+ export interface LevelPlayImpression {
174
+ revenue?: number | null;
175
+ precision?: string | null;
176
+ adNetwork?: string | null;
177
+ mediationAdUnitId?: string | null;
178
+ country?: string | null;
179
+ }
180
+ export type LevelPlayExposure = Omit<AdRevenueFact, 'amount' | 'currency' | 'precision' | 'provider' | 'network' | 'adUnitId' | 'country' | 'countrySource'>;
181
+ export interface RevenueSink { trackAdRevenue(fact: AdRevenueFact, context?: TrackContext): boolean }
182
+ /** Does not initialize ads or replace the game's native listener. Host supplies the original exposure context. */
183
+
184
+ export interface CocosLifecycle {
185
+ onHide(callback: () => void): () => void;
186
+ onShow(callback: () => void): () => void;
187
+ }
188
+ export interface CocosRuntime {
189
+ storage: LocalStorageLike;
190
+ lifecycle?: CocosLifecycle;
191
+ transport?: JsonTransport;
192
+ now?: () => number;
193
+ }
194
+ export interface CocosOptions extends PlayScopeOptions {
195
+ /** Host decides consent/collection policy before calling init. */
196
+ collectionEnabled: boolean;
197
+ /** Defaults to the existing game-v1 revenue protocol. Does not alter already queued facts. */
198
+ adRevenueEncoder?: AdRevenueEncoder;
199
+ }
200
+ /** Host owns event semantics; this wrapper only schedules flush and releases its subscriptions. */
201
+
202
+ export interface LocalStorageLike {
203
+ getItem(key: string): string | null;
204
+ setItem(key: string, value: string): void;
205
+ removeItem?(key: string): void;
206
+ }
207
+ /** Inject sys.localStorage; failures reach the outbox and prevent unsafe uploads. */
208
+
209
+
210
+ export declare class PlayScopeClient {
211
+ constructor(options: PlayScopeOptions, storage: StoragePort, transport: JsonTransport, now?: () => number);
212
+ get installId(): string;
213
+ get cache(): unknown;
214
+ saveCache(value: unknown): boolean;
215
+ getQuarantinedEvents(): QuarantinedEvent[];
216
+ get diagnostics(): Diagnostics;
217
+ get status(): ClientStatus;
218
+ start(name?: string, context?: TrackContext): void;
219
+ newSession(): void;
220
+ track(name: string, properties: Record<string, unknown>, context?: TrackContext): boolean;
221
+ trackWithResult(name: string, properties: Record<string, unknown>, context?: TrackContext): TrackResult;
222
+ flush(): Promise<void>;
223
+ flushWithResult(): Promise<FlushResult>;
224
+ refreshConfiguration(): Promise<ConfigurationResult>;
225
+ retryUploads(): Promise<FlushResult>;
226
+ dispose(): void;
227
+ }
228
+ export declare class CocosPlayScope {
229
+ constructor(options: CocosOptions, runtime: CocosRuntime);
230
+ start(): void;
231
+ get installId(): string | null;
232
+ getDiagnostics(): Diagnostics;
233
+ getQuarantinedEvents(): QuarantinedEvent[];
234
+ get status(): ClientStatus;
235
+ track(name: string, properties?: Record<string, unknown>, context?: TrackContext): boolean;
236
+ trackWithResult(name: string, properties?: Record<string, unknown>, context?: TrackContext): TrackResult;
237
+ trackAdRevenue(fact: AdRevenueFact, context?: TrackContext): boolean;
238
+ newSession(): void;
239
+ update(dt: number): void;
240
+ flush(): Promise<void>;
241
+ flushWithResult(): Promise<FlushResult>;
242
+ refreshConfiguration(): Promise<ConfigurationResult>;
243
+ retryUploads(): Promise<FlushResult>;
244
+ dispose(): void;
245
+ }
246
+ export declare function init(options: CocosOptions, runtime: CocosRuntime): CocosPlayScope;
247
+ export declare class CocosStorage implements StoragePort {
248
+ constructor(storage: LocalStorageLike);
249
+ get ownershipScope(): object;
250
+ readonly removeString?: (key: string) => void;
251
+ getString(key: string): string | null;
252
+ setString(key: string, value: string): void;
253
+ flush(): void;
254
+ }
255
+ export declare class XhrJsonTransport implements JsonTransport {
256
+ post(url: string, headers: Record<string, string>, body: unknown): Promise<HttpResponse>;
257
+ dispose(): void;
258
+ }
259
+ export declare function telemetryUuid(): string;
260
+ export declare function utf8Bytes(value: string): number;
261
+ export declare function enqueueAdRevenue(client: EventSink, fact: AdRevenueFact, context: TrackContext, encode: AdRevenueEncoder): boolean;
262
+ export declare function trackAdRevenue(client: EventSink, fact: AdRevenueFact, context?: TrackContext): boolean;
263
+ export type GameV1AdPlacement = 'revive_video' | 'gameover_interstitial' | 'win_interstitial' | 'pause_interstitial' | 'hint_video' | 'win_double' | 'banner';
264
+ export declare function encodeGameV1AdRevenue(fact: Readonly<AdRevenueFact>): EncodedRevenueEvent | null;
265
+ export declare function forwardLevelPlayRevenue(sink: RevenueSink, impression: LevelPlayImpression, exposure: LevelPlayExposure, context?: TrackContext): boolean;
266
+ export type InitializationErrorCode = 'storage_unavailable' | 'state_corrupt' | 'state_version_unsupported' | 'cache_restore_failed' | 'storage_capability_missing' | 'scope_mismatch' | 'scope_migration_required';
267
+ export declare class PlayScopeInitializationError extends Error {
268
+ constructor(code: InitializationErrorCode);
269
+ readonly code: InitializationErrorCode;
270
+ readonly retryable: boolean;
271
+ }
272
+ export declare class PlayScopeOwnershipError extends Error {
273
+ constructor();
274
+ readonly code: 'duplicate_client';
275
+ }
276
+ export declare class PlayScopeHttpError extends Error {
277
+ constructor(status: number, retryAfterMs?: number, path?: string);
278
+ readonly status: number;
279
+ readonly retryAfterMs: number;
280
+ readonly path?: string;
281
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "appVersion": {
5
+ "type": "string",
6
+ "maxLength": 256
7
+ },
8
+ "buildNumber": {
9
+ "type": "string",
10
+ "maxLength": 256
11
+ },
12
+ "platform": {
13
+ "anyOf": [
14
+ {
15
+ "type": "string",
16
+ "const": "android",
17
+ "maxLength": 256
18
+ },
19
+ {
20
+ "type": "string",
21
+ "const": "ios",
22
+ "maxLength": 256
23
+ },
24
+ {
25
+ "type": "string",
26
+ "const": "web",
27
+ "maxLength": 256
28
+ },
29
+ {
30
+ "type": "string",
31
+ "const": "wechat",
32
+ "maxLength": 256
33
+ }
34
+ ]
35
+ },
36
+ "environment": {
37
+ "anyOf": [
38
+ {
39
+ "type": "string",
40
+ "const": "production",
41
+ "maxLength": 256
42
+ },
43
+ {
44
+ "type": "string",
45
+ "const": "test",
46
+ "maxLength": 256
47
+ }
48
+ ]
49
+ },
50
+ "locale": {
51
+ "type": "string",
52
+ "maxLength": 256
53
+ },
54
+ "timezoneOffsetMinutes": {
55
+ "type": "integer",
56
+ "minimum": -840,
57
+ "maximum": 840
58
+ },
59
+ "device": {
60
+ "type": "object",
61
+ "properties": {
62
+ "type": {
63
+ "type": "string",
64
+ "const": "android_id",
65
+ "maxLength": 256
66
+ },
67
+ "id": {
68
+ "type": "string",
69
+ "pattern": "^[0-9a-f]{16}$",
70
+ "not": {
71
+ "enum": [
72
+ "0000000000000000",
73
+ "9774d56d682e549c"
74
+ ]
75
+ }
76
+ }
77
+ },
78
+ "required": [
79
+ "type",
80
+ "id"
81
+ ],
82
+ "additionalProperties": false
83
+ }
84
+ },
85
+ "required": [
86
+ "appVersion",
87
+ "buildNumber",
88
+ "platform",
89
+ "environment",
90
+ "locale",
91
+ "timezoneOffsetMinutes"
92
+ ],
93
+ "additionalProperties": false,
94
+ "dependentSchemas": {
95
+ "device": {
96
+ "properties": {
97
+ "platform": {
98
+ "const": "android"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }