@mpgd/platform 0.24.0 → 0.26.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/index.d.ts CHANGED
@@ -164,6 +164,11 @@ export type PromotionRewardResult = {
164
164
  * and receipt finalization remain server responsibilities.
165
165
  */
166
166
  export interface PromotionRewardAdapter {
167
+ /** Read cached/provider-confirmed results only. Must never authorize or start a native payout. */
168
+ recoverReward?(input: {
169
+ readonly campaignId: string;
170
+ readonly idempotencyKey: string;
171
+ }): Promise<PromotionRewardResult>;
167
172
  getAvailability(input: {
168
173
  readonly campaignId: string;
169
174
  }): Promise<PromotionRewardAvailability>;
@@ -203,6 +203,23 @@ export interface NotificationAdvertisingSettings {
203
203
  * each issue's path.
204
204
  */
205
205
  export declare function resolveNotificationAdvertisingSettings(deployment: NotificationDeployment | undefined, path?: string): NotificationAdvertisingSettings;
206
+ /**
207
+ * Whether a deployment text is still a template placeholder rather than a
208
+ * real value. It catches common template markers, not every made-up value:
209
+ *
210
+ * - the starter's `REPLACE-WITH-…` marker;
211
+ * - a whole value that is an unreplaced `__TOKEN__`, `{{token}}`, or
212
+ * `${token}`, or only letters in angle or square brackets (`<phone
213
+ * number>`, `[전화번호]`), but not a bracketed value with a digit or an
214
+ * address, such as `[수신거부 080-123-4567]` or `<help@studio.kr>`;
215
+ * - a reserved `example.com`, `example.org`, or `example.net` address
216
+ * (one followed by another label, such as `example.com.au`, is real);
217
+ * - only `TODO`, `TBD`, `CHANGEME`, `PLACEHOLDER`, or `XXX`.
218
+ *
219
+ * `assertNotificationDeployment` refuses one in the advertising sender and
220
+ * the opt-out line under the Korean rules.
221
+ */
222
+ export declare function isNotificationPlaceholderText(value: string): boolean;
206
223
  /** A per-recipient or app-wide count within a rolling window. */
207
224
  export interface NotificationProviderLimit {
208
225
  readonly count: PositiveInteger;
@@ -185,6 +185,30 @@ export function resolveNotificationAdvertisingSettings(deployment, path = 'deplo
185
185
  issues,
186
186
  };
187
187
  }
188
+ /**
189
+ * Whether a deployment text is still a template placeholder rather than a
190
+ * real value. It catches common template markers, not every made-up value:
191
+ *
192
+ * - the starter's `REPLACE-WITH-…` marker;
193
+ * - a whole value that is an unreplaced `__TOKEN__`, `{{token}}`, or
194
+ * `${token}`, or only letters in angle or square brackets (`<phone
195
+ * number>`, `[전화번호]`), but not a bracketed value with a digit or an
196
+ * address, such as `[수신거부 080-123-4567]` or `<help@studio.kr>`;
197
+ * - a reserved `example.com`, `example.org`, or `example.net` address
198
+ * (one followed by another label, such as `example.com.au`, is real);
199
+ * - only `TODO`, `TBD`, `CHANGEME`, `PLACEHOLDER`, or `XXX`.
200
+ *
201
+ * `assertNotificationDeployment` refuses one in the advertising sender and
202
+ * the opt-out line under the Korean rules.
203
+ */
204
+ export function isNotificationPlaceholderText(value) {
205
+ const text = value.trim();
206
+ return /^replace[-_]with[-_]/iu.test(text)
207
+ || /^(?:__[A-Za-z]\w*__|\{\{[^{}]*\}\}|\$\{[^{}]*\})$/u.test(text)
208
+ || /^(?:<\p{L}[\p{L} _-]*>|\[\p{L}[\p{L} _-]*\])$/u.test(text)
209
+ || /(?:^|[^\w-])example\.(?:com|org|net)(?![\w-]|\.[a-z0-9])/iu.test(text)
210
+ || /^(?:todo|tbd|change[-_ ]?me|placeholder|x{3,})$/iu.test(text);
211
+ }
188
212
  /** A deployment text trimmed, or `undefined` when it is absent or blank. */
189
213
  function deploymentText(value) {
190
214
  const trimmed = value?.trim();
@@ -11,7 +11,7 @@ import { type NotificationConsentRecord, type NotificationDeliveryProviderEntry,
11
11
  export declare const notificationsProtocolVersion: '2.0.0-draft.1';
12
12
  export declare const notificationsProtocol: 'mpgd.notifications.v2';
13
13
  export type { CancelLocalNotificationsInput, LocalNotificationEntry, LocalNotificationRequest, LocalScheduleResult, NotificationCapabilities, NotificationChannel, NotificationEndpointAddress, NotificationEndpointRegistration, NotificationEvent, NotificationLocalCapabilities, NotificationOpenPath, NotificationPermissionAllowance, NotificationPermissionCapabilities, NotificationPermissionOutcome, NotificationPermissionPrompt, NotificationPermissionResult, NotificationPermissionScope, NotificationPermissionSnapshot, NotificationPermissionState, NotificationProvider, NotificationProviderId, NotificationPurpose, NotificationRemoteAddressing, NotificationRemoteCapabilities, NotificationRemoteContent, NotificationRequiredAction, NotificationSettingsResult, NotificationTopicId, NotificationTopicsInput, } from './notifications-contract.js';
14
- export { applyNotificationAdvertisingLabel, checkNotificationNightWindow, checkNotificationQuietHours, createNotificationNoticeIdempotencyKey, createNotificationReminderIdempotencyKey, evaluateLocalNotificationReminder, evaluateNotificationDelivery, evaluateNotificationReminderConsent, fillNotificationTemplate, findNotificationCopy, isNotificationCalendarDate, isNotificationTimestamp, isRecordableNotificationPermissionState, isRootRelativeNotificationRoute, isSameNotificationNotice, isValidNotificationTimeZone, latestNotificationConsent, listNotificationNotices, normalizeNotificationTimestamp, notificationAdvertisingOptOutActionId, notificationAdvertisingPrefix, notificationConsentResultDueBy, notificationDefaultDeploymentTimeZone, notificationDefaultOptOutLine, notificationInputBounds, notificationKeyIssue, notificationNightWindow, notificationNoticeCopyIssues, notificationNoticeLeadDays, notificationNoticeSendFrom, notificationNoticeTopic, notificationNoticeVariantCopy, notificationNoticeVariantFallbacks, notificationNoticeVariants, notificationPermissionConsentKind, notificationPermissionStateOf, notificationProviderCarriesNotices, notificationReconfirmationDueBy, notificationReconfirmationListDays, notificationRegistryMismatch, notificationReservedKeyPrefix, notificationReservedKeyPrefixes, notificationSendFingerprint, notificationSuppressionReasons, notificationTemplateDataIssue, notificationTopicDefinitionIssues, notificationTopicNeedsNoAgreement, readNotificationTemplate, recordableNotificationPermissionStates, referenceNotificationProviders, referenceNotificationTargetProviders, renderNotificationAdvertisement, resolveNotificationAdvertisingSettings, resolveNotificationContent, routeNotificationReminder, toSeoulCalendarDate, type EvaluateLocalNotificationReminderInput, type EvaluateNotificationDeliveryInput, type EvaluateNotificationReminderConsentInput, type LocalNotificationReminderDecision, type NotificationAction, type NotificationAdvertisingRules, type NotificationAdvertisingSender, type NotificationAdvertisingSettings, type NotificationClockTime, type NotificationConsentKind, type NotificationConsentRecord, type NotificationConsentSource, type NotificationConsentState, type NotificationDeferralReason, type NotificationDeliveryContent, type NotificationDeliveryDecision, type NotificationDeliveryHistoryEntry, type NotificationDeliveryOutcome, type NotificationDeliveryProviderEntry, type NotificationDeliveryRefusal, type NotificationDeliveryStatus, type NotificationDeployment, type NotificationMappingField, type NotificationNotice, type NotificationNoticeCopy, type NotificationNoticeIdentity, type NotificationNoticeKind, type NotificationNoticeLocaleCopy, type NotificationNoticeSettings, type NotificationNoticeVariant, type NotificationProviderLimit, type NotificationProviderRegistryEntry, type NotificationQuietHours, type NotificationRecordablePermissionState, type NotificationReminderRoute, type NotificationReminderStatus, type NotificationSendRequest, type NotificationSuppressionReason, type NotificationTemplateData, type NotificationTopicCopy, type NotificationTopicDeclaration, type NotificationTopicDefinition, type NotificationTopicFrequency, type NotificationTopicIssue, type NotificationTopicMapping, type NotificationUnavailableReason, type NotificationWindowCheck, type ReminderResult, type ResolveNotificationContentInput, } from './notifications-policy.js';
14
+ export { applyNotificationAdvertisingLabel, checkNotificationNightWindow, checkNotificationQuietHours, createNotificationNoticeIdempotencyKey, createNotificationReminderIdempotencyKey, evaluateLocalNotificationReminder, evaluateNotificationDelivery, evaluateNotificationReminderConsent, fillNotificationTemplate, findNotificationCopy, isNotificationCalendarDate, isNotificationPlaceholderText, isNotificationTimestamp, isRecordableNotificationPermissionState, isRootRelativeNotificationRoute, isSameNotificationNotice, isValidNotificationTimeZone, latestNotificationConsent, listNotificationNotices, normalizeNotificationTimestamp, notificationAdvertisingOptOutActionId, notificationAdvertisingPrefix, notificationConsentResultDueBy, notificationDefaultDeploymentTimeZone, notificationDefaultOptOutLine, notificationInputBounds, notificationKeyIssue, notificationNightWindow, notificationNoticeCopyIssues, notificationNoticeLeadDays, notificationNoticeSendFrom, notificationNoticeTopic, notificationNoticeVariantCopy, notificationNoticeVariantFallbacks, notificationNoticeVariants, notificationPermissionConsentKind, notificationPermissionStateOf, notificationProviderCarriesNotices, notificationReconfirmationDueBy, notificationReconfirmationListDays, notificationRegistryMismatch, notificationReservedKeyPrefix, notificationReservedKeyPrefixes, notificationSendFingerprint, notificationSuppressionReasons, notificationTemplateDataIssue, notificationTopicDefinitionIssues, notificationTopicNeedsNoAgreement, readNotificationTemplate, recordableNotificationPermissionStates, referenceNotificationProviders, referenceNotificationTargetProviders, renderNotificationAdvertisement, resolveNotificationAdvertisingSettings, resolveNotificationContent, routeNotificationReminder, toSeoulCalendarDate, type EvaluateLocalNotificationReminderInput, type EvaluateNotificationDeliveryInput, type EvaluateNotificationReminderConsentInput, type LocalNotificationReminderDecision, type NotificationAction, type NotificationAdvertisingRules, type NotificationAdvertisingSender, type NotificationAdvertisingSettings, type NotificationClockTime, type NotificationConsentKind, type NotificationConsentRecord, type NotificationConsentSource, type NotificationConsentState, type NotificationDeferralReason, type NotificationDeliveryContent, type NotificationDeliveryDecision, type NotificationDeliveryHistoryEntry, type NotificationDeliveryOutcome, type NotificationDeliveryProviderEntry, type NotificationDeliveryRefusal, type NotificationDeliveryStatus, type NotificationDeployment, type NotificationMappingField, type NotificationNotice, type NotificationNoticeCopy, type NotificationNoticeIdentity, type NotificationNoticeKind, type NotificationNoticeLocaleCopy, type NotificationNoticeSettings, type NotificationNoticeVariant, type NotificationProviderLimit, type NotificationProviderRegistryEntry, type NotificationQuietHours, type NotificationRecordablePermissionState, type NotificationReminderRoute, type NotificationReminderStatus, type NotificationSendRequest, type NotificationSuppressionReason, type NotificationTemplateData, type NotificationTopicCopy, type NotificationTopicDeclaration, type NotificationTopicDefinition, type NotificationTopicFrequency, type NotificationTopicIssue, type NotificationTopicMapping, type NotificationUnavailableReason, type NotificationWindowCheck, type ReminderResult, type ResolveNotificationContentInput, } from './notifications-policy.js';
15
15
  type NonEmptyString = string & tags.MinLength<1>;
16
16
  type NonNegativeInteger = number & tags.Type<'uint32'>;
17
17
  export type NotificationErrorCode =
@@ -214,8 +214,32 @@ export declare const assertNotificationNotice: (input: unknown) => NotificationN
214
214
  */
215
215
  export declare const assertRecordNotificationNoticeInput: (input: unknown) => RecordNotificationNoticeInput;
216
216
  export declare const assertNotificationDeliveryProviderEntry: (input: unknown) => NotificationDeliveryProviderEntry;
217
- /** Validates deployment settings, including a `timeZone` that `Intl` knows, which quiet hours read. */
218
- export declare function assertNotificationDeployment(input: unknown): NotificationDeployment;
217
+ /** Options of `assertNotificationDeployment`. */
218
+ export interface AssertNotificationDeploymentOptions {
219
+ /**
220
+ * Accept placeholders, for a validator that reports them itself
221
+ * (`notificationDeploymentPlaceholders`) next to the catalog's issues. A
222
+ * backend never sets it.
223
+ */
224
+ readonly allowPlaceholders?: boolean;
225
+ }
226
+ /**
227
+ * The fields of a deployment under the Korean rules that still hold a
228
+ * template placeholder (`isNotificationPlaceholderText`): the advertising
229
+ * sender's name and contact and the opt-out line, which every advertising
230
+ * push carries there. Under `none`, or without `advertisingRules`, no push
231
+ * carries them, so none is reported.
232
+ */
233
+ export declare function notificationDeploymentPlaceholders(deployment: NotificationDeployment): readonly ('advertisingSender.name' | 'advertisingSender.contact' | 'advertisingOptOutLine')[];
234
+ /**
235
+ * Validates deployment settings: a `timeZone` that `Intl` knows, which quiet
236
+ * hours read, and under the Korean rules an advertising sender and opt-out
237
+ * line that are not template placeholders
238
+ * (`notificationDeploymentPlaceholders`). A backend built from a refused
239
+ * deployment does not start, so a starter's placeholder contact never
240
+ * reaches an advertising push.
241
+ */
242
+ export declare function assertNotificationDeployment(input: unknown, options?: AssertNotificationDeploymentOptions): NotificationDeployment;
219
243
  /** Validates a topic definition's shape and the rules of `notificationTopicDefinitionIssues`. */
220
244
  export declare function assertNotificationTopicDefinition(input: unknown): NotificationTopicDefinition;
221
245
  export declare const assertNotificationBridgeGetCapabilitiesPayload: (input: unknown) => NotificationBridgeProtocolPayload;
@@ -6,7 +6,7 @@ import * as _accessExpressionAsString_1 from "typia/lib/internal/_accessExpressi
6
6
  const __typia_transform__accessExpressionAsString = _accessExpressionAsString_1._accessExpressionAsString;
7
7
  import typia from 'typia';
8
8
  import { PlatformOperationError, } from './index.js';
9
- import { isValidNotificationTimeZone, notificationPermissionStateOf, notificationRouteError, notificationTimeFieldIssue, notificationTimeFields, notificationTopicDefinitionIssues, } from './notifications-policy.js';
9
+ import { isNotificationPlaceholderText, isValidNotificationTimeZone, notificationPermissionStateOf, notificationRouteError, notificationTimeFieldIssue, notificationTimeFields, notificationTopicDefinitionIssues, } from './notifications-policy.js';
10
10
  /**
11
11
  * Notifications v2 contract module (docs/specs/notifications/notifications-v2-rfc.md).
12
12
  * Pure types, validators, and helpers only: no provider lives here, and
@@ -15,7 +15,7 @@ import { isValidNotificationTimeZone, notificationPermissionStateOf, notificatio
15
15
  */
16
16
  export const notificationsProtocolVersion = '2.0.0-draft.1';
17
17
  export const notificationsProtocol = 'mpgd.notifications.v2';
18
- export { applyNotificationAdvertisingLabel, checkNotificationNightWindow, checkNotificationQuietHours, createNotificationNoticeIdempotencyKey, createNotificationReminderIdempotencyKey, evaluateLocalNotificationReminder, evaluateNotificationDelivery, evaluateNotificationReminderConsent, fillNotificationTemplate, findNotificationCopy, isNotificationCalendarDate, isNotificationTimestamp, isRecordableNotificationPermissionState, isRootRelativeNotificationRoute, isSameNotificationNotice, isValidNotificationTimeZone, latestNotificationConsent, listNotificationNotices, normalizeNotificationTimestamp, notificationAdvertisingOptOutActionId, notificationAdvertisingPrefix, notificationConsentResultDueBy, notificationDefaultDeploymentTimeZone, notificationDefaultOptOutLine, notificationInputBounds, notificationKeyIssue, notificationNightWindow, notificationNoticeCopyIssues, notificationNoticeLeadDays, notificationNoticeSendFrom, notificationNoticeTopic, notificationNoticeVariantCopy, notificationNoticeVariantFallbacks, notificationNoticeVariants, notificationPermissionConsentKind, notificationPermissionStateOf, notificationProviderCarriesNotices, notificationReconfirmationDueBy, notificationReconfirmationListDays, notificationRegistryMismatch, notificationReservedKeyPrefix, notificationReservedKeyPrefixes, notificationSendFingerprint, notificationSuppressionReasons, notificationTemplateDataIssue, notificationTopicDefinitionIssues, notificationTopicNeedsNoAgreement, readNotificationTemplate, recordableNotificationPermissionStates, referenceNotificationProviders, referenceNotificationTargetProviders, renderNotificationAdvertisement, resolveNotificationAdvertisingSettings, resolveNotificationContent, routeNotificationReminder, toSeoulCalendarDate, } from './notifications-policy.js';
18
+ export { applyNotificationAdvertisingLabel, checkNotificationNightWindow, checkNotificationQuietHours, createNotificationNoticeIdempotencyKey, createNotificationReminderIdempotencyKey, evaluateLocalNotificationReminder, evaluateNotificationDelivery, evaluateNotificationReminderConsent, fillNotificationTemplate, findNotificationCopy, isNotificationCalendarDate, isNotificationPlaceholderText, isNotificationTimestamp, isRecordableNotificationPermissionState, isRootRelativeNotificationRoute, isSameNotificationNotice, isValidNotificationTimeZone, latestNotificationConsent, listNotificationNotices, normalizeNotificationTimestamp, notificationAdvertisingOptOutActionId, notificationAdvertisingPrefix, notificationConsentResultDueBy, notificationDefaultDeploymentTimeZone, notificationDefaultOptOutLine, notificationInputBounds, notificationKeyIssue, notificationNightWindow, notificationNoticeCopyIssues, notificationNoticeLeadDays, notificationNoticeSendFrom, notificationNoticeTopic, notificationNoticeVariantCopy, notificationNoticeVariantFallbacks, notificationNoticeVariants, notificationPermissionConsentKind, notificationPermissionStateOf, notificationProviderCarriesNotices, notificationReconfirmationDueBy, notificationReconfirmationListDays, notificationRegistryMismatch, notificationReservedKeyPrefix, notificationReservedKeyPrefixes, notificationSendFingerprint, notificationSuppressionReasons, notificationTemplateDataIssue, notificationTopicDefinitionIssues, notificationTopicNeedsNoAgreement, readNotificationTemplate, recordableNotificationPermissionStates, referenceNotificationProviders, referenceNotificationTargetProviders, renderNotificationAdvertisement, resolveNotificationAdvertisingSettings, resolveNotificationContent, routeNotificationReminder, toSeoulCalendarDate, } from './notifications-policy.js';
19
19
  export const notificationErrorCodes = Object.freeze([
20
20
  'NOTIFICATIONS_UNSUPPORTED',
21
21
  'NOTIFICATIONS_CONFIGURATION_REQUIRED',
@@ -3459,12 +3459,46 @@ const assertNotificationDeploymentShape = (() => {
3459
3459
  return input;
3460
3460
  };
3461
3461
  })();
3462
- /** Validates deployment settings, including a `timeZone` that `Intl` knows, which quiet hours read. */
3463
- export function assertNotificationDeployment(input) {
3462
+ /**
3463
+ * The fields of a deployment under the Korean rules that still hold a
3464
+ * template placeholder (`isNotificationPlaceholderText`): the advertising
3465
+ * sender's name and contact and the opt-out line, which every advertising
3466
+ * push carries there. Under `none`, or without `advertisingRules`, no push
3467
+ * carries them, so none is reported.
3468
+ */
3469
+ export function notificationDeploymentPlaceholders(deployment) {
3470
+ if (deployment.advertisingRules !== 'kr') {
3471
+ return [];
3472
+ }
3473
+ const texts = [
3474
+ ['advertisingSender.name', deployment.advertisingSender?.name],
3475
+ ['advertisingSender.contact', deployment.advertisingSender?.contact],
3476
+ ['advertisingOptOutLine', deployment.advertisingOptOutLine],
3477
+ ];
3478
+ return texts
3479
+ .filter(([, value]) => value !== undefined && isNotificationPlaceholderText(value))
3480
+ .map(([field]) => field);
3481
+ }
3482
+ /**
3483
+ * Validates deployment settings: a `timeZone` that `Intl` knows, which quiet
3484
+ * hours read, and under the Korean rules an advertising sender and opt-out
3485
+ * line that are not template placeholders
3486
+ * (`notificationDeploymentPlaceholders`). A backend built from a refused
3487
+ * deployment does not start, so a starter's placeholder contact never
3488
+ * reaches an advertising push.
3489
+ */
3490
+ export function assertNotificationDeployment(input, options = {}) {
3464
3491
  const deployment = assertNotificationDeploymentShape(input);
3465
3492
  if (deployment.timeZone !== undefined && !isValidNotificationTimeZone(deployment.timeZone)) {
3466
3493
  throw new TypeError(`Unknown time zone in the deployment: ${deployment.timeZone}`);
3467
3494
  }
3495
+ const placeholders = options.allowPlaceholders === true
3496
+ ? []
3497
+ : notificationDeploymentPlaceholders(deployment);
3498
+ if (placeholders.length > 0) {
3499
+ throw new TypeError(`The deployment still has a placeholder in ${placeholders.join(', ')}: `
3500
+ + 'replace it with the real value before the backend starts.');
3501
+ }
3468
3502
  return deployment;
3469
3503
  }
3470
3504
  const assertNotificationTopicDefinitionShape = (() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/platform",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Shared platform gateway, monetization, ads, leaderboard, identity, lifecycle, and storage contracts for mpgd games.",
5
5
  "license": "MIT",
6
6
  "repository": {