@mpgd/platform 0.25.0 → 0.27.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.
@@ -1,4 +1,5 @@
1
- import { type CancelNotificationReminderInput, type LocalNotificationEntry, type LocalNotificationRequest, type NotificationCapabilities, type NotificationConsentRecord, type NotificationDeliveryContent, type NotificationDeliveryOutcome, type NotificationDeliveryProviderEntry, type NotificationDeployment, type NotificationEndpointRegistration, type NotificationNotice, type NotificationNoticeIdentity, type NotificationNoticeSettings, type NotificationPermissionState, type NotificationProvider, type NotificationRuntime, type NotificationTemplateData, type NotificationTopicDeclaration, type NotificationTopicMapping, type SetNotificationReminderInput } from './notifications.js';
1
+ import { type LocalNotificationEntry, type LocalNotificationRequest, type NotificationCapabilities, type NotificationConsentRecord, type NotificationDeliveryContent, type NotificationDeliveryOutcome, type NotificationDeliveryProviderEntry, type NotificationDeployment, type NotificationNotice, type NotificationNoticeIdentity, type NotificationNoticeSettings, type NotificationPermissionState, type NotificationProvider, type NotificationTemplateData, type NotificationTopicDeclaration, type NotificationTopicMapping } from './notifications.js';
2
+ import { type NotificationRuntimeBackend, type NotificationRuntimeController, type NotificationRuntimePersistedRegistration, type NotificationRuntimeReminderSetStatus, type NotificationRuntimeSession } from './notifications-runtime.js';
2
3
  import { notificationConformanceDefaultLocale, type NotificationConformanceDeliverInput, type NotificationConformanceEndpointFixture, type NotificationConformanceHostFixture, type NotificationConformanceInstallationFixture, type NotificationConformanceProfile, type NotificationConformanceProfileLimits, type NotificationConformancePromptResult, type NotificationConformanceRejectionReason, type NotificationConformanceTapInput, type NotificationConformanceVector } from './notifications-vectors.js';
3
4
  /**
4
5
  * Notifications v2 conformance runner (RFC "Conformance vectors") with the
@@ -6,7 +7,8 @@ import { notificationConformanceDefaultLocale, type NotificationConformanceDeliv
6
7
  * with its fixture `NotificationProvider`; a fake backend that keeps sessions,
7
8
  * consent records, endpoints, server reminders, notices, and an idempotency
8
9
  * ledger, and applies the pure policy gate before every provider call; and the
9
- * reference notifications runtime the runner drives as the `game` actor. Pure
10
+ * notifications runtime the runner drives as the `game` actor, which is the
11
+ * production one from `@mpgd/platform/notifications-runtime`. Pure
10
12
  * in-process code: no Phaser, DOM, network, or SDK imports. Nothing here sends
11
13
  * a real notification or grants anything.
12
14
  *
@@ -121,49 +123,25 @@ export declare function notificationConformanceRegistryEntry(input: {
121
123
  readonly limits?: NotificationConformanceProfileLimits;
122
124
  }): NotificationDeliveryProviderEntry | undefined;
123
125
  /** An authenticated game-services session; its installation id is the session's, never a request's. */
124
- export interface NotificationConformanceSession {
125
- readonly id: string;
126
- readonly player: string;
126
+ export interface NotificationConformanceSession extends NotificationRuntimeSession {
127
127
  readonly installationId: string;
128
128
  }
129
129
  /**
130
- * What setting a server reminder answers: stored, the gate step 3 to 5
131
- * refusal, `capacity-exceeded` past a backend's cap on a player's pending
132
- * reminders, or `unavailable` when no provider the player can be reached on
133
- * maps the topic. The fake backend answers only the first six.
130
+ * What setting a server reminder answers (`NotificationRuntimeReminderSetStatus`).
131
+ * The fake backend answers only the first six.
134
132
  */
135
- export type NotificationConformanceReminderSetStatus = 'scheduled' | 'replaced' | 'permission-required' | 'request-missing' | 'preference-off' | 'advertising-consent-missing' | 'capacity-exceeded' | 'unavailable';
133
+ export type NotificationConformanceReminderSetStatus = NotificationRuntimeReminderSetStatus;
136
134
  export type NotificationConformanceNoticeChannel = 'in-game' | 'push' | 'other';
137
- /** The backend surface the runtime calls for the authenticated session. */
138
- export interface NotificationConformanceBackendClient {
135
+ /**
136
+ * The backend surface the runtime calls for the authenticated session
137
+ * (`NotificationRuntimeBackend`), with the fixture session's installation and
138
+ * every notice channel a backend under test records.
139
+ */
140
+ export interface NotificationConformanceBackendClient extends NotificationRuntimeBackend {
139
141
  /** The authenticated session; `undefined` before authentication and after the session ends. */
140
142
  session(): NotificationConformanceSession | undefined;
141
143
  /** Calls `listener` whenever a session starts; returns the unsubscribe. */
142
144
  onAuthenticated(listener: (session: NotificationConformanceSession) => void): () => void;
143
- /** Appends a record for the session's player; the backend stamps `at` with its own clock. */
144
- recordConsent(record: NotificationConsentRecord): Promise<void>;
145
- /** The session player's consent records, oldest first. */
146
- listConsent(): Promise<readonly NotificationConsentRecord[]>;
147
- /**
148
- * Binds a device registration to the session's installation and player, or
149
- * keeps a platform-user registration's locale and zone for the player.
150
- */
151
- registerEndpoint(registration: NotificationEndpointRegistration): Promise<void>;
152
- /**
153
- * Unbinds the session's installation from `player`, the player whose
154
- * session sent the unbind; an installation another player took over since
155
- * stays bound.
156
- */
157
- unregisterEndpoint(input: {
158
- readonly provider: string;
159
- readonly player: string;
160
- }): Promise<void>;
161
- /** Checks gate steps 3 to 5, then stores or replaces the session player's reminder with this key. */
162
- setReminder(input: SetNotificationReminderInput): Promise<NotificationConformanceReminderSetStatus>;
163
- /** Whether a pending reminder with this key, or a waiting send of one, existed. */
164
- cancelReminder(input: CancelNotificationReminderInput): Promise<boolean>;
165
- /** The session player's listed notices; none unless the deployment applies the Korean rules. */
166
- listNotices(): Promise<readonly NotificationNotice[]>;
167
145
  /**
168
146
  * Records a notice given to the session's player, named by its identity
169
147
  * fields, while that notice is listed; recording one that is not listed
@@ -368,31 +346,25 @@ export interface CreateNotificationConformanceBackendInput {
368
346
  * is retried on the next run, and one failing item never stops the others.
369
347
  */
370
348
  export declare function createNotificationConformanceBackend(input: CreateNotificationConformanceBackendInput): NotificationConformanceFakeBackend;
371
- export interface NotificationConformanceRuntime extends NotificationRuntime {
372
- /**
373
- * Subscribes to the provider and to authentication, compares the provider
374
- * with its registry entry, and reconciles pending local reminders. Provider
375
- * events are buffered for the game's first listener.
376
- */
377
- start(): Promise<void>;
378
- /** Drops listeners, buffered events, and both subscriptions. */
379
- stop(): void;
380
- /** Waits for event-driven work (records, registrations, local reminders) and rethrows its first failure. */
381
- runUntilIdle(): Promise<void>;
382
- }
383
- /** What the runtime keeps across restarts. */
349
+ /**
350
+ * The reference runtime the runner drives as the `game` actor: the
351
+ * production runtime (`createNotificationRuntime` in
352
+ * `@mpgd/platform/notifications-runtime`), so every vector exercises it.
353
+ */
354
+ export type NotificationConformanceRuntime = NotificationRuntimeController;
355
+ /** What the runtime keeps across restarts; the runner passes the same object to each restart. */
384
356
  export interface NotificationConformanceRuntimeStorage {
385
357
  /** The last registration the backend acknowledged. */
386
358
  registration?: NotificationConformancePersistedRegistration;
387
359
  /** Set by `unregister()`; `register()` or the next authentication clears it. */
388
360
  unbound?: boolean;
361
+ /** With `unbound`: the session signed in when `unregister()` ran. */
362
+ unboundSession?: {
363
+ readonly player: string;
364
+ readonly session: string;
365
+ };
389
366
  }
390
- export interface NotificationConformancePersistedRegistration {
391
- readonly registration: NotificationEndpointRegistration;
392
- readonly player: string;
393
- readonly session: string;
394
- readonly at: string;
395
- }
367
+ export type NotificationConformancePersistedRegistration = NotificationRuntimePersistedRegistration;
396
368
  export interface CreateNotificationConformanceRuntimeInput {
397
369
  readonly provider: NotificationProvider;
398
370
  readonly backend: NotificationConformanceBackendClient;
@@ -404,7 +376,7 @@ export interface CreateNotificationConformanceRuntimeInput {
404
376
  readonly defaultLocale: string;
405
377
  /** The device locale for local reminder copy; defaults to `defaultLocale`. */
406
378
  readonly locale?: string;
407
- /** Survives restarts; a fresh object when absent. */
379
+ /** Survives restarts; a fresh object when absent. The runtime writes it after each change. */
408
380
  readonly storage?: NotificationConformanceRuntimeStorage;
409
381
  /**
410
382
  * The backend's registry entry for this provider. When the provider's
@@ -414,64 +386,9 @@ export interface CreateNotificationConformanceRuntimeInput {
414
386
  readonly registryEntry?: NotificationDeliveryProviderEntry;
415
387
  }
416
388
  /**
417
- * The reference notifications runtime (RFC "Runtime"): the provider composed
418
- * with the backend for the authenticated session.
419
- *
420
- * - Reads return the provider snapshot. A topic the host reads `unknown`
421
- * takes the latest record, `sources` says which answered, and a
422
- * game-rendered consent that is not opted in reads `prompt` until a
423
- * decision is recorded and `denied` after it. A topic whose mapping needs no
424
- * agreement reads `granted`. A read that observes a changed decided state
425
- * records it, and every read still answers when the backend cannot.
426
- * - Reads, requests, and revocations take each topic once, however often it
427
- * is listed.
428
- * - Requests throw `NOTIFICATIONS_USER_GESTURE_REQUIRED` without a gesture on
429
- * a gesture-only provider. An unsupported provider answers `unavailable`, a
430
- * provider in `action-required` answers that with its `requiredAction`, an
431
- * undeclared or unmapped topic answers `unavailable`, a topic that needs no
432
- * agreement answers `already-granted`, and a topic the host reads `blocked`
433
- * answers `blocked`, all without reaching the host; every other topic
434
- * reaches the prompt.
435
- * - Records: decided states that differ from the latest record of their kind
436
- * and scope, never `dismissed` or `action-required` answers. Device
437
- * providers write none (the endpoint holds the permission), other app-scope
438
- * providers write `delivery-permission`, and topic-scope providers write
439
- * `host-agreement` with the host evidence, plus a `topic-preference` of
440
- * `granted` when the player accepts a `requested` topic's agreement.
441
- * Per-send acceptances are appended every time. A record belongs to the
442
- * player signed in when the call or event began: one observed before
443
- * authentication waits for it (only the latest per kind and scope), and one
444
- * observed for a player waits for that player's session. A player action
445
- * (`setTopicPreference`, `setAdvertisingConsent`) needs an authenticated
446
- * player and throws `NOTIFICATIONS_TEMPORARILY_UNAVAILABLE` without one, as
447
- * does every other backend call (a server reminder, `reminders.cancel`'s
448
- * server part, and `notices`). A backend call belongs to the player signed
449
- * in when it began and is refused when another player signed in since. A
450
- * record the backend would refuse is dropped and its error surfaces.
451
- * - Registrations: the provider's registration goes to the backend at
452
- * authentication, on `register()`, on `endpoint-changed` (a rotated device
453
- * address, or a platform user's changed locale or zone), and when a read
454
- * or request, or a revocation, changes the device's decided permission; and
455
- * at start and on reads once the last one the backend acknowledged is older
456
- * than 7 days. An identical one for the same player and session is skipped
457
- * for 7 days, except on `register()`. `unregister()` unbinds the
458
- * installation from the player signed in when it was called and cancels the
459
- * pending local reminders, even when the unbind fails, and nothing binds it
460
- * again, across restarts, until `register()` or the next authentication.
461
- * - Reminders: one key across both routes, and `cancel` removes it from each
462
- * route that has it. Local ones pass the client gate steps (permission,
463
- * then the topic preference from the backend, then quiet hours, which move
464
- * them or refuse a topic that is not deferrable, then the topic's daily
465
- * cap); server ones are checked and stored by the backend. Turning a topic off cancels its local
466
- * reminders, a known permission other than granted cancels them, and start,
467
- * authentication, and every backend read reconcile them with the catalog
468
- * (a topic that no longer takes the local route loses its reminders), the
469
- * latest preference, and permission.
470
- * - Game listeners run after the runtime's own handling of an event; a
471
- * listener that throws stops neither the others nor the runtime, and its
472
- * error surfaces from `runUntilIdle`, as do failures of background work:
473
- * the first one, or an `AggregateError` that counts them all. `stop()` and
474
- * a later `start()` buffer events for the next first subscriber again.
389
+ * The production runtime over the conformance inputs: `storage` is a plain
390
+ * object the runtime reads when it is created and rewrites after each
391
+ * change, so a restart that passes the same object resumes from it.
475
392
  */
476
393
  export declare function createNotificationConformanceRuntime(input: CreateNotificationConformanceRuntimeInput): NotificationConformanceRuntime;
477
394
  export interface NotificationConformanceHarness {