playcademy 0.16.3 → 0.16.5
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/cli.js +13 -3
- package/dist/constants/src/workers.ts +15 -0
- package/dist/db.d.ts +1 -0
- package/dist/db.js +22 -5
- package/dist/edge-play/src/entry/setup.ts +28 -3
- package/dist/index.d.ts +90 -9
- package/dist/index.js +1252 -896
- package/dist/utils.js +92 -16
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -653,7 +653,7 @@ function setupGlobalErrorHandlers() {
|
|
|
653
653
|
import { execSync as execSync5 } from "child_process";
|
|
654
654
|
import { writeFileSync as writeFileSync10 } from "fs";
|
|
655
655
|
import { resolve as resolve7 } from "path";
|
|
656
|
-
import { confirm as
|
|
656
|
+
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
657
657
|
|
|
658
658
|
// ../utils/src/ansi.ts
|
|
659
659
|
var colors = {
|
|
@@ -2965,7 +2965,7 @@ import { join as join13 } from "path";
|
|
|
2965
2965
|
// package.json with { type: 'json' }
|
|
2966
2966
|
var package_default2 = {
|
|
2967
2967
|
name: "playcademy",
|
|
2968
|
-
version: "0.16.
|
|
2968
|
+
version: "0.16.4",
|
|
2969
2969
|
type: "module",
|
|
2970
2970
|
exports: {
|
|
2971
2971
|
".": {
|
|
@@ -3448,6 +3448,16 @@ function hasLocalCustomRoutes(projectPath, config) {
|
|
|
3448
3448
|
return existsSync13(customRoutesDir);
|
|
3449
3449
|
}
|
|
3450
3450
|
|
|
3451
|
+
// src/lib/deploy/hash.ts
|
|
3452
|
+
init_file_loader();
|
|
3453
|
+
|
|
3454
|
+
// src/lib/secrets/diff.ts
|
|
3455
|
+
import { green as green3, red as red3, yellow as yellow4 } from "colorette";
|
|
3456
|
+
|
|
3457
|
+
// src/lib/secrets/sync.ts
|
|
3458
|
+
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
3459
|
+
import { bold as bold5 } from "colorette";
|
|
3460
|
+
|
|
3451
3461
|
// src/lib/init/bucket.ts
|
|
3452
3462
|
function hasBucketSetup(config) {
|
|
3453
3463
|
return !!config.integrations?.bucket;
|
|
@@ -3725,7 +3735,7 @@ async function runInit(options = {}) {
|
|
|
3725
3735
|
`Configuration file already exists: ${relativePath}`
|
|
3726
3736
|
]);
|
|
3727
3737
|
logger.newLine();
|
|
3728
|
-
const shouldOverwrite = await
|
|
3738
|
+
const shouldOverwrite = await confirm5({
|
|
3729
3739
|
message: "Do you want to overwrite it?",
|
|
3730
3740
|
default: false
|
|
3731
3741
|
});
|
|
@@ -19,3 +19,18 @@ export const WORKER_NAMING = {
|
|
|
19
19
|
/** Suffix for staging worker hostnames (e.g., "bamboo-staging.playcademy.gg") */
|
|
20
20
|
STAGING_SUFFIX: '-staging',
|
|
21
21
|
} as const
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Prefix for user-defined secrets in Cloudflare Worker bindings.
|
|
25
|
+
*
|
|
26
|
+
* Cloudflare bindings are flat, so we namespace user secrets with this prefix
|
|
27
|
+
* to distinguish them from platform bindings (like GAME_ID, PLAYCADEMY_API_KEY).
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* - When setting secrets: `bindings[SECRETS_PREFIX + key] = value`
|
|
31
|
+
* - When reading secrets: strip prefix and expose as `c.env.secrets.KEY`
|
|
32
|
+
*
|
|
33
|
+
* @see reconstructSecrets in edge-play/entry/setup.ts
|
|
34
|
+
* @see prefixSecrets in edge-play/entry/setup.ts
|
|
35
|
+
*/
|
|
36
|
+
export const SECRETS_PREFIX = 'secrets_'
|
package/dist/db.d.ts
CHANGED
|
@@ -63,6 +63,7 @@ interface SeedWorkerBundle {
|
|
|
63
63
|
* - Imports the user's seed function from the specified path
|
|
64
64
|
* - Captures console output (log/warn/error/info) with timestamps
|
|
65
65
|
* - Parses D1/SQLite errors into structured details
|
|
66
|
+
* - Reconstructs secrets from env bindings (secrets_KEY -> c.env.secrets.KEY)
|
|
66
67
|
* - Returns success/error as JSON with logs and timing
|
|
67
68
|
*
|
|
68
69
|
* @param seedFilePath - Absolute path to user's seed file
|
package/dist/db.js
CHANGED
|
@@ -436,13 +436,9 @@ async function getParseD1ErrorSource() {
|
|
|
436
436
|
function createLogCapture(startTime) {
|
|
437
437
|
const logs = [];
|
|
438
438
|
const originalConsole = {
|
|
439
|
-
// eslint-disable-next-line no-console
|
|
440
439
|
log: console.log.bind(console),
|
|
441
|
-
// eslint-disable-next-line no-console
|
|
442
440
|
warn: console.warn.bind(console),
|
|
443
|
-
// eslint-disable-next-line no-console
|
|
444
441
|
error: console.error.bind(console),
|
|
445
|
-
// eslint-disable-next-line no-console
|
|
446
442
|
info: console.info.bind(console)
|
|
447
443
|
};
|
|
448
444
|
const isSilent = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.LOG_SILENT === "true";
|
|
@@ -502,12 +498,26 @@ function buildErrorResponse(error, logs, duration, details) {
|
|
|
502
498
|
duration
|
|
503
499
|
};
|
|
504
500
|
}
|
|
501
|
+
function reconstructSecrets(env) {
|
|
502
|
+
const secrets = {};
|
|
503
|
+
const prefix = "secrets_";
|
|
504
|
+
for (const key of Object.keys(env)) {
|
|
505
|
+
if (key.startsWith(prefix)) {
|
|
506
|
+
const secretKey = key.slice(prefix.length);
|
|
507
|
+
secrets[secretKey] = env[key];
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return secrets;
|
|
511
|
+
}
|
|
505
512
|
function createSeedFetchHandler(seedFn, parseD1ErrorFn) {
|
|
506
513
|
return async function fetch(req, env, ctx) {
|
|
507
514
|
const startTime = Date.now();
|
|
508
515
|
const { logs, restore } = createLogCapture(startTime);
|
|
509
516
|
try {
|
|
510
|
-
const
|
|
517
|
+
const envRecord = env;
|
|
518
|
+
const secrets = reconstructSecrets(envRecord);
|
|
519
|
+
const enrichedEnv = { ...envRecord, secrets };
|
|
520
|
+
const c = { env: enrichedEnv, ctx, req };
|
|
511
521
|
await seedFn(c);
|
|
512
522
|
const duration = Date.now() - startTime;
|
|
513
523
|
restore();
|
|
@@ -529,6 +539,9 @@ async function getBuildSuccessResponseSource() {
|
|
|
529
539
|
async function getBuildErrorResponseSource() {
|
|
530
540
|
return transpileTypeScript(buildErrorResponse.toString());
|
|
531
541
|
}
|
|
542
|
+
async function getReconstructSecretsSource() {
|
|
543
|
+
return transpileTypeScript(reconstructSecrets.toString());
|
|
544
|
+
}
|
|
532
545
|
async function getCreateSeedFetchHandlerSource() {
|
|
533
546
|
return transpileTypeScript(createSeedFetchHandler.toString());
|
|
534
547
|
}
|
|
@@ -540,12 +553,14 @@ async function createSeedWorkerEntry(seedFilePath) {
|
|
|
540
553
|
createLogCaptureSource,
|
|
541
554
|
buildSuccessResponseSource,
|
|
542
555
|
buildErrorResponseSource,
|
|
556
|
+
reconstructSecretsSource,
|
|
543
557
|
createSeedFetchHandlerSource
|
|
544
558
|
] = await Promise.all([
|
|
545
559
|
getParseD1ErrorSource(),
|
|
546
560
|
getCreateLogCaptureSource(),
|
|
547
561
|
getBuildSuccessResponseSource(),
|
|
548
562
|
getBuildErrorResponseSource(),
|
|
563
|
+
getReconstructSecretsSource(),
|
|
549
564
|
getCreateSeedFetchHandlerSource()
|
|
550
565
|
]);
|
|
551
566
|
return `import { seed } from '${seedFilePath}'
|
|
@@ -558,6 +573,8 @@ ${buildSuccessResponseSource}
|
|
|
558
573
|
|
|
559
574
|
${buildErrorResponseSource}
|
|
560
575
|
|
|
576
|
+
${reconstructSecretsSource}
|
|
577
|
+
|
|
561
578
|
${createSeedFetchHandlerSource}
|
|
562
579
|
|
|
563
580
|
export default { fetch: createSeedFetchHandler(seed, parseD1Error) }
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Helper functions for preparing the Worker environment
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { SECRETS_PREFIX } from '@playcademy/constants'
|
|
8
|
+
|
|
7
9
|
import { ENV_VARS } from '../constants'
|
|
8
10
|
|
|
9
11
|
import type { ServerEnv } from '../types'
|
|
@@ -20,17 +22,21 @@ export function populateProcessEnv(env: ServerEnv): void {
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
|
-
* Reconstruct secrets object from flat Cloudflare bindings
|
|
25
|
+
* Reconstruct secrets object from flat Cloudflare bindings.
|
|
24
26
|
*
|
|
25
27
|
* Cloudflare bindings are flat (secrets_KEY_NAME), but we expose them
|
|
26
28
|
* as c.env.secrets.KEY_NAME for better developer ergonomics.
|
|
29
|
+
*
|
|
30
|
+
* @param env - Raw Cloudflare Worker environment bindings
|
|
31
|
+
* @returns Object with secrets keyed by their original names (without prefix)
|
|
27
32
|
*/
|
|
28
33
|
export function reconstructSecrets(env: Record<string, unknown>): Record<string, string> {
|
|
29
34
|
const secrets: Record<string, string> = {}
|
|
35
|
+
const prefixLength = SECRETS_PREFIX.length
|
|
30
36
|
|
|
31
37
|
for (const key in env) {
|
|
32
|
-
if (key.startsWith(
|
|
33
|
-
const secretKey = key.slice(
|
|
38
|
+
if (key.startsWith(SECRETS_PREFIX)) {
|
|
39
|
+
const secretKey = key.slice(prefixLength)
|
|
34
40
|
secrets[secretKey] = env[key] as string
|
|
35
41
|
}
|
|
36
42
|
}
|
|
@@ -38,6 +44,25 @@ export function reconstructSecrets(env: Record<string, unknown>): Record<string,
|
|
|
38
44
|
return secrets
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Add the secrets prefix to a record of secrets.
|
|
49
|
+
*
|
|
50
|
+
* Use this when setting up Cloudflare Worker bindings to namespace
|
|
51
|
+
* user secrets separately from platform bindings.
|
|
52
|
+
*
|
|
53
|
+
* @param secrets - Record of secret key-value pairs
|
|
54
|
+
* @returns Record with prefixed keys (secrets_KEY)
|
|
55
|
+
*/
|
|
56
|
+
export function prefixSecrets(secrets: Record<string, string>): Record<string, string> {
|
|
57
|
+
const prefixed: Record<string, string> = {}
|
|
58
|
+
|
|
59
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
60
|
+
prefixed[SECRETS_PREFIX + key] = value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return prefixed
|
|
64
|
+
}
|
|
65
|
+
|
|
41
66
|
/**
|
|
42
67
|
* Setup process global polyfill for SDK compatibility
|
|
43
68
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -882,6 +882,21 @@ type ExternalGame = BaseGame & {
|
|
|
882
882
|
};
|
|
883
883
|
type Game = HostedGame | ExternalGame;
|
|
884
884
|
|
|
885
|
+
/**
|
|
886
|
+
* Secrets Diff Utilities
|
|
887
|
+
*
|
|
888
|
+
* Computes and displays differences between local and remote secrets
|
|
889
|
+
*/
|
|
890
|
+
/**
|
|
891
|
+
* Represents the diff between local and remote secrets
|
|
892
|
+
*/
|
|
893
|
+
interface SecretsDiff {
|
|
894
|
+
added: string[];
|
|
895
|
+
updated: string[];
|
|
896
|
+
unchanged: string[];
|
|
897
|
+
removed: string[];
|
|
898
|
+
}
|
|
899
|
+
|
|
885
900
|
/**
|
|
886
901
|
* Integration-related types
|
|
887
902
|
*/
|
|
@@ -964,6 +979,7 @@ interface DeployedGameInfo {
|
|
|
964
979
|
schemaSnapshot?: unknown;
|
|
965
980
|
integrationKeys?: string[];
|
|
966
981
|
integrationsHash?: string;
|
|
982
|
+
secretsHashes?: Record<string, string>;
|
|
967
983
|
}
|
|
968
984
|
interface GameStore {
|
|
969
985
|
/** Staging environment game deployments, keyed by absolute project path */
|
|
@@ -1003,6 +1019,8 @@ interface DeploymentContext {
|
|
|
1003
1019
|
previousResources?: string[];
|
|
1004
1020
|
previousIntegrationKeys?: string[];
|
|
1005
1021
|
previousIntegrationsHash?: string;
|
|
1022
|
+
localSecrets?: Record<string, string>;
|
|
1023
|
+
remoteSecretKeys?: string[];
|
|
1006
1024
|
isDryRun: boolean;
|
|
1007
1025
|
deployBackend: boolean;
|
|
1008
1026
|
forceBackend: boolean;
|
|
@@ -1022,6 +1040,7 @@ interface DeploymentChanges {
|
|
|
1022
1040
|
changes: IntegrationConfigChange[];
|
|
1023
1041
|
}>;
|
|
1024
1042
|
schema?: boolean;
|
|
1043
|
+
secrets?: SecretsDiff;
|
|
1025
1044
|
}
|
|
1026
1045
|
/**
|
|
1027
1046
|
* Plan for what needs to be deployed
|
|
@@ -1259,6 +1278,7 @@ interface DeploymentDiffOptions {
|
|
|
1259
1278
|
build?: BuildDiff;
|
|
1260
1279
|
backend?: BackendDiff;
|
|
1261
1280
|
integrations?: IntegrationsDiff;
|
|
1281
|
+
secrets?: SecretsDiff;
|
|
1262
1282
|
}
|
|
1263
1283
|
|
|
1264
1284
|
/**
|
|
@@ -1542,6 +1562,7 @@ interface ProjectDirectoryInfo {
|
|
|
1542
1562
|
*
|
|
1543
1563
|
* Options for secret management CLI commands.
|
|
1544
1564
|
*/
|
|
1565
|
+
|
|
1545
1566
|
/**
|
|
1546
1567
|
* Base options shared by all secret commands.
|
|
1547
1568
|
*/
|
|
@@ -1550,19 +1571,79 @@ interface SecretCommandOptions {
|
|
|
1550
1571
|
env?: string;
|
|
1551
1572
|
}
|
|
1552
1573
|
/**
|
|
1553
|
-
* Options for the `
|
|
1574
|
+
* Options for the `secrets list` command.
|
|
1554
1575
|
*/
|
|
1555
|
-
type
|
|
1576
|
+
type SecretListOptions = SecretCommandOptions;
|
|
1556
1577
|
/**
|
|
1557
|
-
* Options for the `
|
|
1578
|
+
* Options for the `secrets push` command.
|
|
1558
1579
|
*/
|
|
1559
|
-
type
|
|
1580
|
+
type SecretPushOptions = SecretCommandOptions;
|
|
1560
1581
|
/**
|
|
1561
|
-
* Options for
|
|
1582
|
+
* Options for checking secrets sync status.
|
|
1562
1583
|
*/
|
|
1563
|
-
interface
|
|
1564
|
-
/**
|
|
1565
|
-
|
|
1584
|
+
interface SecretsSyncOptions {
|
|
1585
|
+
/** Absolute path to the project workspace */
|
|
1586
|
+
workspace: string;
|
|
1587
|
+
/** Project/game slug */
|
|
1588
|
+
gameSlug: string;
|
|
1589
|
+
/** Target environment (staging or production) */
|
|
1590
|
+
environment: string;
|
|
1591
|
+
/** Authenticated API client */
|
|
1592
|
+
client: PlaycademyClient;
|
|
1593
|
+
/** Cached hashes from last push (for accurate diff) */
|
|
1594
|
+
cachedHashes?: Record<string, string>;
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Options for checking and prompting secrets sync.
|
|
1598
|
+
*/
|
|
1599
|
+
interface CheckSecretsOptions extends SecretsSyncOptions {
|
|
1600
|
+
/**
|
|
1601
|
+
* Context for the sync check - affects messaging
|
|
1602
|
+
* - 'blocking': Used before operations that require secrets (e.g., db seed)
|
|
1603
|
+
* Shows error on decline, returns cancelled state
|
|
1604
|
+
* - 'optional': Used after operations complete (e.g., deploy)
|
|
1605
|
+
* Shows remark on decline, non-blocking
|
|
1606
|
+
*/
|
|
1607
|
+
mode: 'blocking' | 'optional';
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* A missing secret required by an integration.
|
|
1611
|
+
*/
|
|
1612
|
+
interface MissingSecret {
|
|
1613
|
+
/** The secret key that's missing */
|
|
1614
|
+
key: string;
|
|
1615
|
+
/** The integration that requires it */
|
|
1616
|
+
integration: string;
|
|
1617
|
+
/** Documentation URL for the integration */
|
|
1618
|
+
docs: string;
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Result from checking secrets sync status.
|
|
1622
|
+
*/
|
|
1623
|
+
interface SecretsSyncResult {
|
|
1624
|
+
/** Local secrets from .env file */
|
|
1625
|
+
localSecrets: Record<string, string>;
|
|
1626
|
+
/** Whether secrets were synced (or already in sync) */
|
|
1627
|
+
synced: boolean;
|
|
1628
|
+
/** Whether user cancelled the operation */
|
|
1629
|
+
cancelled: boolean;
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Options for applying secret changes to remote.
|
|
1633
|
+
*/
|
|
1634
|
+
interface ApplySecretsOptions {
|
|
1635
|
+
/** The computed diff to apply */
|
|
1636
|
+
diff: SecretsDiff;
|
|
1637
|
+
/** Local secrets with values (needed for setting secrets) */
|
|
1638
|
+
localSecrets: Record<string, string>;
|
|
1639
|
+
/** Game slug for API calls */
|
|
1640
|
+
gameSlug: string;
|
|
1641
|
+
/** Authenticated API client */
|
|
1642
|
+
client: PlaycademyClient;
|
|
1643
|
+
/** Environment name for display */
|
|
1644
|
+
environment: string;
|
|
1645
|
+
/** Workspace path for saving hashes */
|
|
1646
|
+
workspace: string;
|
|
1566
1647
|
}
|
|
1567
1648
|
|
|
1568
|
-
export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, AuthProfile, AuthStore, AuthStrategy, BackendBundle, BackendDeploymentMetadata, BackendDiff, BackendFeatures, BaseKVOptions, BucketBulkOptions, BucketDeleteOptions, BucketGetOptions, BucketListOptions, BucketPutOptions, BuildDiff, BulkCollectionResult, BundleOptions, CallbackServerResult, CollectedFile, ComponentConfig, ComponentResourceConfig, ConfigDiff, CreateApiKeyResponse, CustomRoutesIntegrationOptions, DatabaseIntegrationOptions, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentDiffOptions, DeploymentPlan, DeploymentResult, DevServerOptions, EmbeddedSourcePaths, EngineConfig, EngineType, EnvironmentAuthProfiles, GameStore, IntegrationChangeDetector, IntegrationChangeDetectors, IntegrationConfigChange, IntegrationsConfig, IntegrationsDiff, KVClearOptions, KVDeleteOptions, KVGetOptions, KVInspectOptions, KVListOptions, KVSeedOptions, KVSetOptions, KVStatsOptions, KeyMetadata, KeyStats, LoadConfigResult, LogEntry, LogStreamConfig, LogStreamUrlOptions, LoginCredentials, LoginResponse, OrganizationConfig, PlaycademyConfig, PluginLogger, PreviewOptions, PreviewResponse, ProjectDirectoryInfo, ResourceConfig, ScaffoldResult, SecretCommandOptions,
|
|
1649
|
+
export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, ApplySecretsOptions, AuthProfile, AuthStore, AuthStrategy, BackendBundle, BackendDeploymentMetadata, BackendDiff, BackendFeatures, BaseKVOptions, BucketBulkOptions, BucketDeleteOptions, BucketGetOptions, BucketListOptions, BucketPutOptions, BuildDiff, BulkCollectionResult, BundleOptions, CallbackServerResult, CheckSecretsOptions, CollectedFile, ComponentConfig, ComponentResourceConfig, ConfigDiff, CreateApiKeyResponse, CustomRoutesIntegrationOptions, DatabaseIntegrationOptions, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentDiffOptions, DeploymentPlan, DeploymentResult, DevServerOptions, EmbeddedSourcePaths, EngineConfig, EngineType, EnvironmentAuthProfiles, GameStore, IntegrationChangeDetector, IntegrationChangeDetectors, IntegrationConfigChange, IntegrationsConfig, IntegrationsDiff, KVClearOptions, KVDeleteOptions, KVGetOptions, KVInspectOptions, KVListOptions, KVSeedOptions, KVSetOptions, KVStatsOptions, KeyMetadata, KeyStats, LoadConfigResult, LogEntry, LogStreamConfig, LogStreamUrlOptions, LoginCredentials, LoginResponse, MissingSecret, OrganizationConfig, PlaycademyConfig, PluginLogger, PreviewOptions, PreviewResponse, ProjectDirectoryInfo, ResourceConfig, ScaffoldResult, SecretCommandOptions, SecretListOptions, SecretPushOptions, SecretsSyncOptions, SecretsSyncResult, SignInResponse, SsoCallbackData, Template, TemplateFramework, TemplateHook, TemplateHookOptions, TemplateSource, TimebackBaseConfig, TimebackCourseConfig, TimebackCourseConfigWithOverrides, TimebackGrade, TimebackIntegrationConfig, TimebackSourcedIds, TimebackSubject, TokenType, UpdateExistingGameOptions };
|