@digilogiclabs/platform-core 1.7.0 → 1.8.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,5 +1,269 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ /**
4
+ * Beta Access Management Interface
5
+ *
6
+ * Provides a vendor-agnostic way to manage beta/early-access programs.
7
+ * Handles invite codes, access gating, settings, and analytics across
8
+ * all DLL Platform applications.
9
+ *
10
+ * Features:
11
+ * - Beta mode settings (mode, require code, custom message)
12
+ * - Invite code management (create, validate, consume, revoke)
13
+ * - Multi-use codes with usage tracking and expiration
14
+ * - Profile tagging (beta tester flag, join date)
15
+ * - Analytics (usage stats, per-code reports)
16
+ * - Live toggling (closed beta → open beta → public launch)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { MemoryBeta } from '@digilogiclabs/platform-core';
21
+ *
22
+ * const beta = new MemoryBeta();
23
+ *
24
+ * // Check if beta is active
25
+ * const settings = await beta.getSettings();
26
+ * if (settings.requireInviteCode) {
27
+ * const result = await beta.validateCode('BETA2026');
28
+ * if (result.valid) {
29
+ * await beta.consumeCode('BETA2026', 'user_123');
30
+ * }
31
+ * }
32
+ *
33
+ * // Create invite codes
34
+ * const codes = await beta.createCodes({
35
+ * count: 10,
36
+ * prefix: 'DLL',
37
+ * maxUses: 5,
38
+ * createdBy: 'admin',
39
+ * });
40
+ *
41
+ * // Transition to public launch
42
+ * await beta.updateSettings({ betaMode: false, requireInviteCode: false });
43
+ * ```
44
+ */
45
+ /** Beta program settings */
46
+ interface BetaSettings {
47
+ /** Whether the app is in beta mode (shows beta messaging) */
48
+ betaMode: boolean;
49
+ /** Whether an invite code is required to sign up */
50
+ requireInviteCode: boolean;
51
+ /** Custom message to display during beta */
52
+ betaMessage: string;
53
+ }
54
+ /** Options for updating beta settings */
55
+ interface UpdateBetaSettingsOptions {
56
+ betaMode?: boolean;
57
+ requireInviteCode?: boolean;
58
+ betaMessage?: string;
59
+ updatedBy?: string;
60
+ }
61
+ /** A beta invite code */
62
+ interface BetaInviteCode {
63
+ /** Unique identifier */
64
+ id: string;
65
+ /** The invite code string (normalized to uppercase) */
66
+ code: string;
67
+ /** Maximum number of times this code can be used */
68
+ maxUses: number;
69
+ /** Current number of times this code has been used */
70
+ currentUses: number;
71
+ /** Optional expiration date */
72
+ expiresAt: Date | null;
73
+ /** Who created this code */
74
+ createdBy: string;
75
+ /** Admin notes about this code */
76
+ notes: string;
77
+ /** Whether this code is active (soft delete) */
78
+ isActive: boolean;
79
+ /** When this code was created */
80
+ createdAt: Date;
81
+ }
82
+ /** Options for creating invite codes */
83
+ interface CreateBetaCodesOptions {
84
+ /** Number of codes to generate (max 50) */
85
+ count?: number;
86
+ /** Prefix for generated codes (e.g., 'DLL' → 'DLL-A1B2C3D4') */
87
+ prefix?: string;
88
+ /** Specific code string (for named codes like 'BETA2026') */
89
+ code?: string;
90
+ /** Maximum uses per code (default: 1) */
91
+ maxUses?: number;
92
+ /** Optional expiration date */
93
+ expiresAt?: Date;
94
+ /** Who is creating the codes */
95
+ createdBy: string;
96
+ /** Admin notes */
97
+ notes?: string;
98
+ }
99
+ /** Filter for listing invite codes */
100
+ type BetaCodeStatus = "unused" | "partial" | "exhausted" | "expired" | "revoked";
101
+ interface ListBetaCodesOptions {
102
+ /** Filter by status */
103
+ status?: BetaCodeStatus;
104
+ /** Filter by active state */
105
+ isActive?: boolean;
106
+ /** Pagination offset */
107
+ offset?: number;
108
+ /** Pagination limit */
109
+ limit?: number;
110
+ }
111
+ /** Result of validating a beta code (read-only, does not consume) */
112
+ interface BetaValidationResult {
113
+ /** Whether the code is valid */
114
+ valid: boolean;
115
+ /** Human-readable message */
116
+ message: string;
117
+ /** The normalized code (if valid) */
118
+ code?: string;
119
+ /** Remaining uses (if valid) */
120
+ remainingUses?: number;
121
+ }
122
+ /** Result of consuming a beta code */
123
+ interface BetaConsumeResult {
124
+ /** Whether the code was successfully consumed */
125
+ success: boolean;
126
+ /** Human-readable message */
127
+ message: string;
128
+ }
129
+ /** Beta tester profile metadata */
130
+ interface BetaTester {
131
+ /** User identifier */
132
+ userId: string;
133
+ /** The invite code they used */
134
+ inviteCode: string;
135
+ /** Whether they are flagged as a beta tester */
136
+ isBetaTester: boolean;
137
+ /** When they joined beta */
138
+ betaJoinedAt: Date;
139
+ }
140
+ /** Aggregate beta program statistics */
141
+ interface BetaStats {
142
+ /** Total number of beta testers */
143
+ totalBetaTesters: number;
144
+ /** Total invite codes created */
145
+ totalCodes: number;
146
+ /** Number of active (usable) codes */
147
+ activeCodes: number;
148
+ /** Total uses across all codes */
149
+ totalUses: number;
150
+ /** Total remaining capacity across all codes */
151
+ totalRemaining: number;
152
+ /** Earliest beta signup */
153
+ firstBetaSignup: Date | null;
154
+ /** Most recent beta signup */
155
+ latestBetaSignup: Date | null;
156
+ }
157
+ /** Per-code usage report */
158
+ interface BetaCodeUsageReport {
159
+ /** The invite code */
160
+ code: string;
161
+ /** Admin notes */
162
+ notes: string;
163
+ /** Maximum uses */
164
+ maxUses: number;
165
+ /** Current uses */
166
+ currentUses: number;
167
+ /** Remaining uses */
168
+ remainingUses: number;
169
+ /** Usage percentage (0-100) */
170
+ usagePercent: number;
171
+ /** Whether the code is active */
172
+ isActive: boolean;
173
+ /** Expiration date */
174
+ expiresAt: Date | null;
175
+ /** Creation date */
176
+ createdAt: Date;
177
+ }
178
+ /** Configuration for beta adapters */
179
+ interface BetaConfig {
180
+ /** Default beta mode on initialization */
181
+ defaultBetaMode?: boolean;
182
+ /** Default require invite code on initialization */
183
+ defaultRequireInviteCode?: boolean;
184
+ /** Default beta message */
185
+ defaultBetaMessage?: string;
186
+ /** Code prefix for generated codes (e.g., 'DLL', 'WIAN') */
187
+ codePrefix?: string;
188
+ /** Maximum codes per createCodes call (default: 50) */
189
+ maxCodesPerBatch?: number;
190
+ }
191
+ interface IBeta {
192
+ /** Get current beta program settings */
193
+ getSettings(): Promise<BetaSettings>;
194
+ /** Update beta program settings (live toggle, no redeploy needed) */
195
+ updateSettings(options: UpdateBetaSettingsOptions): Promise<void>;
196
+ /** Create one or more invite codes */
197
+ createCodes(options: CreateBetaCodesOptions): Promise<BetaInviteCode[]>;
198
+ /** List invite codes with optional filtering */
199
+ listCodes(options?: ListBetaCodesOptions): Promise<BetaInviteCode[]>;
200
+ /** Get a specific invite code by its code string */
201
+ getCode(code: string): Promise<BetaInviteCode | null>;
202
+ /** Revoke an invite code (soft delete — sets isActive to false) */
203
+ revokeCode(code: string): Promise<void>;
204
+ /**
205
+ * Validate an invite code (read-only, does not consume).
206
+ * If requireInviteCode is false, always returns { valid: true }.
207
+ */
208
+ validateCode(code: string): Promise<BetaValidationResult>;
209
+ /**
210
+ * Consume an invite code for a user (increments usage, tags user).
211
+ * Should be called after successful signup, not before.
212
+ */
213
+ consumeCode(code: string, userId: string): Promise<BetaConsumeResult>;
214
+ /** Check if a user is a beta tester */
215
+ isBetaTester(userId: string): Promise<boolean>;
216
+ /** Get beta tester details for a user */
217
+ getBetaTester(userId: string): Promise<BetaTester | null>;
218
+ /** List all beta testers */
219
+ listBetaTesters(options?: {
220
+ offset?: number;
221
+ limit?: number;
222
+ }): Promise<BetaTester[]>;
223
+ /** Get aggregate beta program statistics */
224
+ getStats(): Promise<BetaStats>;
225
+ /** Get per-code usage reports */
226
+ getCodeUsageReports(): Promise<BetaCodeUsageReport[]>;
227
+ /** Check beta service health */
228
+ healthCheck(): Promise<boolean>;
229
+ }
230
+ /** Generate a random hex code with prefix (e.g., 'DLL-A1B2C3D4') */
231
+ declare function generateBetaCode(prefix?: string): string;
232
+ /** Normalize a code for comparison (trim + uppercase) */
233
+ declare function normalizeBetaCode(code: string): string;
234
+ /** Generate a unique ID for beta records */
235
+ declare function generateBetaId(): string;
236
+ declare class MemoryBeta implements IBeta {
237
+ private settings;
238
+ private codes;
239
+ private testers;
240
+ private config;
241
+ constructor(config?: BetaConfig);
242
+ getSettings(): Promise<BetaSettings>;
243
+ updateSettings(options: UpdateBetaSettingsOptions): Promise<void>;
244
+ createCodes(options: CreateBetaCodesOptions): Promise<BetaInviteCode[]>;
245
+ listCodes(options?: ListBetaCodesOptions): Promise<BetaInviteCode[]>;
246
+ getCode(code: string): Promise<BetaInviteCode | null>;
247
+ revokeCode(code: string): Promise<void>;
248
+ validateCode(code: string): Promise<BetaValidationResult>;
249
+ consumeCode(code: string, userId: string): Promise<BetaConsumeResult>;
250
+ isBetaTester(userId: string): Promise<boolean>;
251
+ getBetaTester(userId: string): Promise<BetaTester | null>;
252
+ listBetaTesters(options?: {
253
+ offset?: number;
254
+ limit?: number;
255
+ }): Promise<BetaTester[]>;
256
+ getStats(): Promise<BetaStats>;
257
+ getCodeUsageReports(): Promise<BetaCodeUsageReport[]>;
258
+ healthCheck(): Promise<boolean>;
259
+ /** Clear all data (for testing) */
260
+ clear(): void;
261
+ /** Get the number of stored codes */
262
+ get codeCount(): number;
263
+ /** Get the number of beta testers */
264
+ get testerCount(): number;
265
+ }
266
+
3
267
  /**
4
268
  * Security Utilities
5
269
  *
@@ -1279,6 +1543,100 @@ declare function createAuditLogger(options?: OpsAuditLoggerOptions): {
1279
1543
  };
1280
1544
  };
1281
1545
 
1546
+ /**
1547
+ * Beta Client Utilities
1548
+ *
1549
+ * Shared client-side helpers for beta access management.
1550
+ * These run in the browser and talk to your app's beta API endpoints.
1551
+ *
1552
+ * Standardizes the pattern used across DLL, WIAN, and future apps:
1553
+ * - Fetch beta settings from API
1554
+ * - Validate invite codes
1555
+ * - Store/retrieve beta code across OAuth redirect flows (sessionStorage)
1556
+ *
1557
+ * @example
1558
+ * ```typescript
1559
+ * import {
1560
+ * fetchBetaSettings,
1561
+ * validateBetaCode,
1562
+ * storeBetaCode,
1563
+ * getStoredBetaCode,
1564
+ * clearStoredBetaCode,
1565
+ * } from '@digilogiclabs/platform-core/auth';
1566
+ *
1567
+ * // On sign-in page mount
1568
+ * const settings = await fetchBetaSettings();
1569
+ * if (settings.requireInviteCode) {
1570
+ * const result = await validateBetaCode(userInput);
1571
+ * if (result.valid) {
1572
+ * storeBetaCode(userInput); // Survives OAuth redirect
1573
+ * // Proceed with sign-in
1574
+ * }
1575
+ * }
1576
+ * ```
1577
+ */
1578
+
1579
+ interface BetaClientConfig {
1580
+ /** Base URL for API calls (default: '' for same-origin) */
1581
+ baseUrl?: string;
1582
+ /** API endpoint for settings (default: '/api/beta-settings') */
1583
+ settingsEndpoint?: string;
1584
+ /** API endpoint for validation (default: '/api/validate-beta-code') */
1585
+ validateEndpoint?: string;
1586
+ /** sessionStorage key for storing beta code (default: 'beta_code') */
1587
+ storageKey?: string;
1588
+ /** Default settings to use when fetch fails */
1589
+ failSafeDefaults?: Partial<BetaSettings>;
1590
+ }
1591
+ /**
1592
+ * Create a configured beta client with app-specific settings.
1593
+ *
1594
+ * @example
1595
+ * ```typescript
1596
+ * // DLL app
1597
+ * const betaClient = createBetaClient({
1598
+ * storageKey: 'dll_beta_code',
1599
+ * validateEndpoint: '/api/validate-beta-code',
1600
+ * });
1601
+ *
1602
+ * // WIAN app
1603
+ * const betaClient = createBetaClient({
1604
+ * storageKey: 'wian_beta_code',
1605
+ * validateEndpoint: '/api/validate-invite-code',
1606
+ * });
1607
+ * ```
1608
+ */
1609
+ declare function createBetaClient(config?: BetaClientConfig): {
1610
+ fetchSettings: () => Promise<BetaSettings>;
1611
+ validateCode: (code: string) => Promise<BetaValidationResult>;
1612
+ storeCode: (code: string) => void;
1613
+ getStoredCode: () => string | null;
1614
+ clearStoredCode: () => void;
1615
+ };
1616
+ /**
1617
+ * Fetch beta settings from the server.
1618
+ * Returns fail-safe defaults if the fetch fails.
1619
+ */
1620
+ declare function fetchBetaSettings(config?: BetaClientConfig): Promise<BetaSettings>;
1621
+ /**
1622
+ * Validate a beta invite code against the server.
1623
+ * Handles rate limiting (429) gracefully.
1624
+ */
1625
+ declare function validateBetaCode(code: string, config?: BetaClientConfig): Promise<BetaValidationResult>;
1626
+ /**
1627
+ * Store a validated beta code in sessionStorage.
1628
+ * Used to pass the code through OAuth redirect flows.
1629
+ */
1630
+ declare function storeBetaCode(code: string, config?: BetaClientConfig): void;
1631
+ /**
1632
+ * Retrieve stored beta code from sessionStorage.
1633
+ */
1634
+ declare function getStoredBetaCode(config?: BetaClientConfig): string | null;
1635
+ /**
1636
+ * Clear stored beta code from sessionStorage.
1637
+ */
1638
+ declare function clearStoredBetaCode(config?: BetaClientConfig): void;
1639
+
1282
1640
  /**
1283
1641
  * Environment Variable Helpers
1284
1642
  *
@@ -1364,4 +1722,4 @@ declare function checkEnvVars(config: EnvValidationConfig): EnvValidationResult;
1364
1722
  */
1365
1723
  declare function getEnvSummary(keys: string[]): Record<string, boolean>;
1366
1724
 
1367
- export { type SecuritySession as $, ApiError as A, parseKeycloakRoles as B, CommonApiErrors as C, hasRole as D, type EnvValidationConfig as E, hasAnyRole as F, hasAllRoles as G, isTokenExpired as H, buildTokenRefreshParams as I, getTokenEndpoint as J, type KeycloakConfig as K, getEndSessionEndpoint as L, refreshKeycloakToken as M, type KeycloakCallbacksConfig as N, type KeycloakJwtFields as O, type AuthCookiesConfig as P, buildAuthCookies as Q, type RateLimitStore as R, type RedirectCallbackConfig as S, type TokenRefreshResult as T, buildRedirectCallback as U, buildKeycloakCallbacks as V, type AuthMethod as W, type RouteAuditConfig as X, type RateLimitPreset as Y, StandardRateLimitPresets as Z, type ApiSecurityConfig as _, type RateLimitRule as a, type ApiSecurityContext as a0, resolveRateLimitIdentifier as a1, extractClientIp as a2, buildRateLimitHeaders as a3, buildErrorBody as a4, WrapperPresets as a5, EmailSchema as a6, PasswordSchema as a7, SlugSchema as a8, PhoneSchema as a9, checkRateLimit as aA, getRateLimitStatus as aB, resetRateLimitForKey as aC, buildRateLimitResponseHeaders as aD, resolveIdentifier as aE, type OpsAuditActor as aF, type OpsAuditResource as aG, type OpsAuditEvent as aH, type OpsAuditRecord as aI, type OpsAuditLoggerOptions as aJ, type AuditRequest as aK, StandardAuditActions as aL, type StandardAuditActionType as aM, extractAuditIp as aN, extractAuditUserAgent as aO, extractAuditRequestId as aP, createAuditActor as aQ, createAuditLogger as aR, defangUrl as aS, sanitizeForEmail as aT, URL_PROTOCOL_PATTERN as aU, URL_DOMAIN_PATTERN as aV, HTML_TAG_PATTERN as aW, PG_ERROR_MAP as aX, PersonNameSchema as aa, createSafeTextSchema as ab, PaginationSchema as ac, DateRangeSchema as ad, SearchQuerySchema as ae, LoginSchema as af, SignupSchema as ag, type EmailInput as ah, type PaginationInput as ai, type DateRangeInput as aj, type SearchQueryInput as ak, type LoginInput as al, type SignupInput as am, type DeploymentStage as an, type FlagValue as ao, type FlagDefinition as ap, type FlagDefinitions as aq, type ResolvedFlags as ar, type AllowlistConfig as as, detectStage as at, createFeatureFlags as au, buildAllowlist as av, isAllowlisted as aw, type RateLimitCheckResult as ax, CommonRateLimits as ay, createMemoryRateLimitStore as az, type RateLimitOptions as b, constantTimeEqual as c, containsUrls as d, escapeHtml as e, containsHtml as f, sanitizeApiError as g, getCorrelationId as h, classifyError as i, isApiError as j, ApiErrorCode as k, buildPagination as l, type ApiErrorCodeType as m, type ApiSuccessResponse as n, type ApiPaginatedResponse as o, getRequiredEnv as p, getOptionalEnv as q, getBoolEnv as r, stripHtml as s, getIntEnv as t, checkEnvVars as u, validateEnvVars as v, getEnvSummary as w, type EnvValidationResult as x, type KeycloakTokenSet as y, KEYCLOAK_DEFAULT_ROLES as z };
1725
+ export { type SecuritySession as $, ApiError as A, parseKeycloakRoles as B, CommonApiErrors as C, hasRole as D, type EnvValidationConfig as E, hasAnyRole as F, hasAllRoles as G, isTokenExpired as H, buildTokenRefreshParams as I, getTokenEndpoint as J, type KeycloakConfig as K, getEndSessionEndpoint as L, refreshKeycloakToken as M, type KeycloakCallbacksConfig as N, type KeycloakJwtFields as O, type AuthCookiesConfig as P, buildAuthCookies as Q, type RateLimitStore as R, type RedirectCallbackConfig as S, type TokenRefreshResult as T, buildRedirectCallback as U, buildKeycloakCallbacks as V, type AuthMethod as W, type RouteAuditConfig as X, type RateLimitPreset as Y, StandardRateLimitPresets as Z, type ApiSecurityConfig as _, type RateLimitRule as a, type BetaSettings as a$, type ApiSecurityContext as a0, resolveRateLimitIdentifier as a1, extractClientIp as a2, buildRateLimitHeaders as a3, buildErrorBody as a4, WrapperPresets as a5, EmailSchema as a6, PasswordSchema as a7, SlugSchema as a8, PhoneSchema as a9, checkRateLimit as aA, getRateLimitStatus as aB, resetRateLimitForKey as aC, buildRateLimitResponseHeaders as aD, resolveIdentifier as aE, type OpsAuditActor as aF, type OpsAuditResource as aG, type OpsAuditEvent as aH, type OpsAuditRecord as aI, type OpsAuditLoggerOptions as aJ, type AuditRequest as aK, StandardAuditActions as aL, type StandardAuditActionType as aM, extractAuditIp as aN, extractAuditUserAgent as aO, extractAuditRequestId as aP, createAuditActor as aQ, createAuditLogger as aR, type BetaClientConfig as aS, createBetaClient as aT, fetchBetaSettings as aU, validateBetaCode as aV, storeBetaCode as aW, getStoredBetaCode as aX, clearStoredBetaCode as aY, type IBeta as aZ, type BetaConfig as a_, PersonNameSchema as aa, createSafeTextSchema as ab, PaginationSchema as ac, DateRangeSchema as ad, SearchQuerySchema as ae, LoginSchema as af, SignupSchema as ag, type EmailInput as ah, type PaginationInput as ai, type DateRangeInput as aj, type SearchQueryInput as ak, type LoginInput as al, type SignupInput as am, type DeploymentStage as an, type FlagValue as ao, type FlagDefinition as ap, type FlagDefinitions as aq, type ResolvedFlags as ar, type AllowlistConfig as as, detectStage as at, createFeatureFlags as au, buildAllowlist as av, isAllowlisted as aw, type RateLimitCheckResult as ax, CommonRateLimits as ay, createMemoryRateLimitStore as az, type RateLimitOptions as b, type UpdateBetaSettingsOptions as b0, type CreateBetaCodesOptions as b1, type BetaInviteCode as b2, type ListBetaCodesOptions as b3, type BetaValidationResult as b4, type BetaConsumeResult as b5, type BetaTester as b6, type BetaStats as b7, type BetaCodeUsageReport as b8, MemoryBeta as b9, generateBetaCode as ba, normalizeBetaCode as bb, generateBetaId as bc, type BetaCodeStatus as bd, defangUrl as be, sanitizeForEmail as bf, URL_PROTOCOL_PATTERN as bg, URL_DOMAIN_PATTERN as bh, HTML_TAG_PATTERN as bi, PG_ERROR_MAP as bj, constantTimeEqual as c, containsUrls as d, escapeHtml as e, containsHtml as f, sanitizeApiError as g, getCorrelationId as h, classifyError as i, isApiError as j, ApiErrorCode as k, buildPagination as l, type ApiErrorCodeType as m, type ApiSuccessResponse as n, type ApiPaginatedResponse as o, getRequiredEnv as p, getOptionalEnv as q, getBoolEnv as r, stripHtml as s, getIntEnv as t, checkEnvVars as u, validateEnvVars as v, getEnvSummary as w, type EnvValidationResult as x, type KeycloakTokenSet as y, KEYCLOAK_DEFAULT_ROLES as z };