playcademy 0.14.2 → 0.14.4-alpha.1
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/constants.d.ts +61 -1
- package/dist/constants.js +148 -0
- package/dist/db.js +118 -0
- package/dist/edge-play/src/entry/middleware.ts +102 -5
- package/dist/edge-play/src/entry/session.ts +44 -0
- package/dist/edge-play/src/entry.ts +33 -1
- package/dist/edge-play/src/index.ts +3 -1
- package/dist/edge-play/src/register-routes.ts +0 -4
- package/dist/edge-play/src/types.ts +7 -5
- package/dist/index.d.ts +122 -90
- package/dist/index.js +2658 -1751
- package/dist/templates/api/sample-protected.ts.template +34 -0
- package/dist/templates/auth/auth-catch-all.ts.template +18 -0
- package/dist/templates/auth/auth-schema.ts.template +62 -0
- package/dist/templates/auth/auth.ts.template +55 -0
- package/dist/templates/config/integrations-config.js.template +1 -1
- package/dist/templates/playcademy-env.d.ts.template +12 -3
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +760 -1029
- package/dist/version.d.ts +3 -0
- package/dist/version.js +82 -0
- package/package.json +6 -2
package/dist/constants.d.ts
CHANGED
|
@@ -8,6 +8,61 @@ export { GAME_WORKER_DOMAINS, PLAYCADEMY_BASE_URLS, PLAYCADEMY_DOMAINS } from '@
|
|
|
8
8
|
* Used when integrations.customRoutes.directory is not specified in config
|
|
9
9
|
*/
|
|
10
10
|
declare const DEFAULT_API_ROUTES_DIRECTORY = "server/api";
|
|
11
|
+
/**
|
|
12
|
+
* Server root directory (fixed location)
|
|
13
|
+
*/
|
|
14
|
+
declare const SERVER_ROOT_DIRECTORY = "server";
|
|
15
|
+
/**
|
|
16
|
+
* Server library/utilities directory (fixed location)
|
|
17
|
+
*/
|
|
18
|
+
declare const SERVER_LIB_DIRECTORY = "server/lib";
|
|
19
|
+
/**
|
|
20
|
+
* Auth routes subdirectory name within API directory (fixed structure)
|
|
21
|
+
*/
|
|
22
|
+
declare const AUTH_API_SUBDIRECTORY = "auth";
|
|
23
|
+
/**
|
|
24
|
+
* Sample routes subdirectory name within API directory (fixed structure)
|
|
25
|
+
*/
|
|
26
|
+
declare const SAMPLE_API_SUBDIRECTORY = "sample";
|
|
27
|
+
/**
|
|
28
|
+
* Sample route filenames
|
|
29
|
+
*/
|
|
30
|
+
declare const SAMPLE_CUSTOM_FILENAME = "custom.ts";
|
|
31
|
+
declare const SAMPLE_DATABASE_FILENAME = "database.ts";
|
|
32
|
+
declare const SAMPLE_KV_FILENAME = "kv.ts";
|
|
33
|
+
declare const SAMPLE_BUCKET_FILENAME = "bucket.ts";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Auth-related constants and dependency versions
|
|
37
|
+
*
|
|
38
|
+
* Versions are read from workspace catalog at build time
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Better Auth version to install in user projects
|
|
42
|
+
* Read from workspace catalog at CLI build time
|
|
43
|
+
*/
|
|
44
|
+
declare const BETTER_AUTH_VERSION: string;
|
|
45
|
+
/**
|
|
46
|
+
* @playcademy/auth version to install in user projects
|
|
47
|
+
*/
|
|
48
|
+
declare const PLAYCADEMY_AUTH_VERSION = "latest";
|
|
49
|
+
/**
|
|
50
|
+
* Auth provider display names
|
|
51
|
+
*/
|
|
52
|
+
declare const AUTH_PROVIDER_NAMES: Record<string, string>;
|
|
53
|
+
/**
|
|
54
|
+
* OAuth callback URL pattern
|
|
55
|
+
* Replace {gameUrl} with actual game URL and {provider} with provider name
|
|
56
|
+
*/
|
|
57
|
+
declare const OAUTH_CALLBACK_URL_PATTERN = "{gameUrl}/api/auth/callback/{provider}";
|
|
58
|
+
/**
|
|
59
|
+
* Placeholder game URL for setup instructions
|
|
60
|
+
*/
|
|
61
|
+
declare const PLACEHOLDER_GAME_URL = "https://your-game.playcademy.gg";
|
|
62
|
+
/**
|
|
63
|
+
* Auth configuration filename (fixed structure)
|
|
64
|
+
*/
|
|
65
|
+
declare const AUTH_CONFIG_FILE = "auth.ts";
|
|
11
66
|
|
|
12
67
|
/**
|
|
13
68
|
* Bucket-related constants
|
|
@@ -66,6 +121,11 @@ declare const MINIFLARE_D1_DIRECTORY = "miniflare-D1DatabaseObject";
|
|
|
66
121
|
* This matches the pattern used by Vite, Bun, and other modern tools.
|
|
67
122
|
*/
|
|
68
123
|
declare const ENV_FILES: readonly [".env", ".env.development", ".env.local"];
|
|
124
|
+
/**
|
|
125
|
+
* Environment example file (template for required environment variables)
|
|
126
|
+
* Safe to commit to version control with placeholder values
|
|
127
|
+
*/
|
|
128
|
+
declare const ENV_EXAMPLE_FILE = ".env.example";
|
|
69
129
|
/**
|
|
70
130
|
* TypeScript config files to check (in priority order)
|
|
71
131
|
*
|
|
@@ -163,4 +223,4 @@ declare const DEFAULT_PORTS: {
|
|
|
163
223
|
*/
|
|
164
224
|
declare const CONFIG_FILE_NAMES: string[];
|
|
165
225
|
|
|
166
|
-
export { BUCKET_ALWAYS_SKIP, CALLBACK_PATH, CALLBACK_PORT, CLI_DEFAULT_OUTPUTS, CLI_DIRECTORIES, CLI_FILES, CLI_USER_DIRECTORIES, CLOUDFLARE_BINDINGS, CLOUDFLARE_COMPATIBILITY_DATE, CONFIG_FILE_NAMES, DEFAULT_API_ROUTES_DIRECTORY, DEFAULT_DATABASE_DIRECTORY, DEFAULT_PORTS, DEFAULT_SEED_FILE_NAME, ENV_FILES, MINIFLARE_D1_DIRECTORY, SCHEMA_INDEX_FILE, SCHEMA_SUBDIRECTORY, SSO_AUTH_TIMEOUT_MS, TSCONFIG_FILES, WORKSPACE_NAME };
|
|
226
|
+
export { AUTH_API_SUBDIRECTORY, AUTH_CONFIG_FILE, AUTH_PROVIDER_NAMES, BETTER_AUTH_VERSION, BUCKET_ALWAYS_SKIP, CALLBACK_PATH, CALLBACK_PORT, CLI_DEFAULT_OUTPUTS, CLI_DIRECTORIES, CLI_FILES, CLI_USER_DIRECTORIES, CLOUDFLARE_BINDINGS, CLOUDFLARE_COMPATIBILITY_DATE, CONFIG_FILE_NAMES, DEFAULT_API_ROUTES_DIRECTORY, DEFAULT_DATABASE_DIRECTORY, DEFAULT_PORTS, DEFAULT_SEED_FILE_NAME, ENV_EXAMPLE_FILE, ENV_FILES, MINIFLARE_D1_DIRECTORY, OAUTH_CALLBACK_URL_PATTERN, PLACEHOLDER_GAME_URL, PLAYCADEMY_AUTH_VERSION, SAMPLE_API_SUBDIRECTORY, SAMPLE_BUCKET_FILENAME, SAMPLE_CUSTOM_FILENAME, SAMPLE_DATABASE_FILENAME, SAMPLE_KV_FILENAME, SCHEMA_INDEX_FILE, SCHEMA_SUBDIRECTORY, SERVER_LIB_DIRECTORY, SERVER_ROOT_DIRECTORY, SSO_AUTH_TIMEOUT_MS, TSCONFIG_FILES, WORKSPACE_NAME };
|
package/dist/constants.js
CHANGED
|
@@ -1,5 +1,137 @@
|
|
|
1
1
|
// src/constants/api.ts
|
|
2
2
|
var DEFAULT_API_ROUTES_DIRECTORY = "server/api";
|
|
3
|
+
var SERVER_ROOT_DIRECTORY = "server";
|
|
4
|
+
var SERVER_LIB_DIRECTORY = "server/lib";
|
|
5
|
+
var AUTH_API_SUBDIRECTORY = "auth";
|
|
6
|
+
var SAMPLE_API_SUBDIRECTORY = "sample";
|
|
7
|
+
var SAMPLE_CUSTOM_FILENAME = "custom.ts";
|
|
8
|
+
var SAMPLE_DATABASE_FILENAME = "database.ts";
|
|
9
|
+
var SAMPLE_KV_FILENAME = "kv.ts";
|
|
10
|
+
var SAMPLE_BUCKET_FILENAME = "bucket.ts";
|
|
11
|
+
|
|
12
|
+
// ../../package.json
|
|
13
|
+
var package_default = {
|
|
14
|
+
name: "playcademy",
|
|
15
|
+
devDependencies: {
|
|
16
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
17
|
+
"@eslint/js": "^9.24.0",
|
|
18
|
+
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
|
19
|
+
"@pulumi/pulumi": "^3.195.0",
|
|
20
|
+
"@types/bun": "latest",
|
|
21
|
+
commander: "^14.0.0",
|
|
22
|
+
eslint: "^9.24.0",
|
|
23
|
+
globals: "^16.0.0",
|
|
24
|
+
husky: "^9.1.7",
|
|
25
|
+
jscodeshift: "^17.3.0",
|
|
26
|
+
"lint-staged": "^15.5.1",
|
|
27
|
+
prettier: "3.5.3",
|
|
28
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
29
|
+
rimraf: "^6.0.1",
|
|
30
|
+
sharp: "^0.34.2",
|
|
31
|
+
typedoc: "^0.28.5",
|
|
32
|
+
"typedoc-plugin-markdown": "^4.7.0",
|
|
33
|
+
"typedoc-vitepress-theme": "^1.1.2",
|
|
34
|
+
"typescript-eslint": "^8.30.1",
|
|
35
|
+
"yocto-spinner": "^0.2.2"
|
|
36
|
+
},
|
|
37
|
+
peerDependencies: {
|
|
38
|
+
typescript: "^5"
|
|
39
|
+
},
|
|
40
|
+
private: true,
|
|
41
|
+
scripts: {
|
|
42
|
+
build: "bun run scripts/build.ts",
|
|
43
|
+
"build:types": "bun tsc -b packages/data",
|
|
44
|
+
clean: "bun scripts/clean.ts && bun i",
|
|
45
|
+
"docs:commit": "bun scripts/docs-commit.ts",
|
|
46
|
+
dev: "bunx sst dev",
|
|
47
|
+
"dev:reset": "sst shell -- bun run scripts/dev-reset.ts",
|
|
48
|
+
"db:reseed": "sst shell -- bun run scripts/dev-reseed-db.ts",
|
|
49
|
+
"db:sync": "sst shell -- bun run scripts/dev-sync-db.ts",
|
|
50
|
+
"db:studio": "sst shell -- bun run --filter @playcademy/data studio",
|
|
51
|
+
"upload-games": "sst shell -- bun run scripts/upload-games.ts",
|
|
52
|
+
"upload-items": "sst shell -- bun run scripts/upload-items.ts",
|
|
53
|
+
"upload-sprites": "sst shell -- bun run scripts/upload-sprites.ts",
|
|
54
|
+
"upload-static-assets": "sst shell -- bun run scripts/upload-static-assets.ts",
|
|
55
|
+
"upload:all": "sst shell -- bun run scripts/upload-all.ts",
|
|
56
|
+
"sync-engine-assets": "bun scripts/sync-engine-assets.ts",
|
|
57
|
+
"sync-vite-templates": "bun scripts/sync-vite-templates.ts",
|
|
58
|
+
"sync-godot-template": "bun scripts/sync-godot-template.ts",
|
|
59
|
+
"sync:all": "bun scripts/sync-all.ts",
|
|
60
|
+
"setup-cloudflare": "bun scripts/setup-cloudflare-dispatch.ts",
|
|
61
|
+
"list-s3-bucket": "sst shell -- bun scripts/list-s3-bucket.ts",
|
|
62
|
+
doctor: "bunx sst shell -- bun scripts/doctor.ts",
|
|
63
|
+
format: "bun run --filter '*' format",
|
|
64
|
+
lint: "bun run --filter '*' lint",
|
|
65
|
+
prepare: "husky",
|
|
66
|
+
sort: "bunx sort-package-json **/package.json",
|
|
67
|
+
test: "bun test",
|
|
68
|
+
"test:unit": "bun scripts/test-unit.ts",
|
|
69
|
+
"test:integration": "bun scripts/test-integration.ts",
|
|
70
|
+
"test:watch": "bun test --watch",
|
|
71
|
+
"docker:relay": "bun run scripts/docker-relay.ts",
|
|
72
|
+
"debug:leaderboard-notifications": "bunx sst shell -- bun scripts/debug/leaderboard-notifications.ts",
|
|
73
|
+
"timeback:caliper": "sst shell -- bun scripts/caliper-cli.ts",
|
|
74
|
+
notify: "NODE_ENV=productionbunx sst shell -- bun scripts/notifications-cli.ts"
|
|
75
|
+
},
|
|
76
|
+
type: "module",
|
|
77
|
+
workspaces: {
|
|
78
|
+
packages: [
|
|
79
|
+
"apps/*",
|
|
80
|
+
"packages/*",
|
|
81
|
+
"games/*",
|
|
82
|
+
"templates/*"
|
|
83
|
+
],
|
|
84
|
+
catalog: {
|
|
85
|
+
sst: "^3.14.11",
|
|
86
|
+
dedent: "^1.6.0",
|
|
87
|
+
typescript: "^5.7.2",
|
|
88
|
+
vite: "^6.3.5",
|
|
89
|
+
eslint: "^9.24.0",
|
|
90
|
+
prettier: "3.5.3",
|
|
91
|
+
"@types/bun": "latest",
|
|
92
|
+
jose: "^5.2.3",
|
|
93
|
+
zod: "^3.25.53",
|
|
94
|
+
"better-auth": "1.3.33"
|
|
95
|
+
},
|
|
96
|
+
catalogs: {
|
|
97
|
+
svelte: {
|
|
98
|
+
svelte: "^5.23.1",
|
|
99
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
100
|
+
"@sveltejs/kit": "^2.16.0",
|
|
101
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
102
|
+
"svelte-check": "^4.2.1",
|
|
103
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
104
|
+
"eslint-plugin-svelte": "^3.0.0"
|
|
105
|
+
},
|
|
106
|
+
database: {
|
|
107
|
+
"drizzle-orm": "^0.42.0",
|
|
108
|
+
"drizzle-kit": "^0.31.0",
|
|
109
|
+
"drizzle-zod": "^0.7.1"
|
|
110
|
+
},
|
|
111
|
+
aws: {
|
|
112
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
113
|
+
"@aws-sdk/s3-request-presigner": "^3.787.0"
|
|
114
|
+
},
|
|
115
|
+
linting: {
|
|
116
|
+
"typescript-eslint": "^8.30.1",
|
|
117
|
+
"@eslint/js": "^9.24.0",
|
|
118
|
+
"eslint-config-prettier": "^10.0.1"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/constants/auth.ts
|
|
125
|
+
var BETTER_AUTH_VERSION = package_default.workspaces.catalog["better-auth"];
|
|
126
|
+
var PLAYCADEMY_AUTH_VERSION = "latest";
|
|
127
|
+
var AUTH_PROVIDER_NAMES = {
|
|
128
|
+
email: "Email/password",
|
|
129
|
+
github: "GitHub",
|
|
130
|
+
google: "Google"
|
|
131
|
+
};
|
|
132
|
+
var OAUTH_CALLBACK_URL_PATTERN = "{gameUrl}/api/auth/callback/{provider}";
|
|
133
|
+
var PLACEHOLDER_GAME_URL = "https://your-game.playcademy.gg";
|
|
134
|
+
var AUTH_CONFIG_FILE = "auth.ts";
|
|
3
135
|
|
|
4
136
|
// src/constants/config.ts
|
|
5
137
|
var ENV_FILES = [
|
|
@@ -10,6 +142,7 @@ var ENV_FILES = [
|
|
|
10
142
|
".env.local"
|
|
11
143
|
// Overrides all (highest priority)
|
|
12
144
|
];
|
|
145
|
+
var ENV_EXAMPLE_FILE = ".env.example";
|
|
13
146
|
var TSCONFIG_FILES = [
|
|
14
147
|
"tsconfig.app.json",
|
|
15
148
|
// Modern tooling (try first)
|
|
@@ -137,6 +270,10 @@ var BADGES = {
|
|
|
137
270
|
FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
|
|
138
271
|
};
|
|
139
272
|
export {
|
|
273
|
+
AUTH_API_SUBDIRECTORY,
|
|
274
|
+
AUTH_CONFIG_FILE,
|
|
275
|
+
AUTH_PROVIDER_NAMES,
|
|
276
|
+
BETTER_AUTH_VERSION,
|
|
140
277
|
BUCKET_ALWAYS_SKIP,
|
|
141
278
|
CALLBACK_PATH,
|
|
142
279
|
CALLBACK_PORT,
|
|
@@ -151,13 +288,24 @@ export {
|
|
|
151
288
|
DEFAULT_DATABASE_DIRECTORY,
|
|
152
289
|
DEFAULT_PORTS,
|
|
153
290
|
DEFAULT_SEED_FILE_NAME,
|
|
291
|
+
ENV_EXAMPLE_FILE,
|
|
154
292
|
ENV_FILES,
|
|
155
293
|
GAME_WORKER_DOMAINS,
|
|
156
294
|
MINIFLARE_D1_DIRECTORY,
|
|
295
|
+
OAUTH_CALLBACK_URL_PATTERN,
|
|
296
|
+
PLACEHOLDER_GAME_URL,
|
|
297
|
+
PLAYCADEMY_AUTH_VERSION,
|
|
157
298
|
PLAYCADEMY_BASE_URLS,
|
|
158
299
|
PLAYCADEMY_DOMAINS,
|
|
300
|
+
SAMPLE_API_SUBDIRECTORY,
|
|
301
|
+
SAMPLE_BUCKET_FILENAME,
|
|
302
|
+
SAMPLE_CUSTOM_FILENAME,
|
|
303
|
+
SAMPLE_DATABASE_FILENAME,
|
|
304
|
+
SAMPLE_KV_FILENAME,
|
|
159
305
|
SCHEMA_INDEX_FILE,
|
|
160
306
|
SCHEMA_SUBDIRECTORY,
|
|
307
|
+
SERVER_LIB_DIRECTORY,
|
|
308
|
+
SERVER_ROOT_DIRECTORY,
|
|
161
309
|
SSO_AUTH_TIMEOUT_MS,
|
|
162
310
|
TSCONFIG_FILES,
|
|
163
311
|
WORKSPACE_NAME
|
package/dist/db.js
CHANGED
|
@@ -22,6 +22,121 @@ var init_file_loader = __esm({
|
|
|
22
22
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from "fs";
|
|
23
23
|
import { join as join2 } from "path";
|
|
24
24
|
|
|
25
|
+
// ../../package.json
|
|
26
|
+
var package_default = {
|
|
27
|
+
name: "playcademy",
|
|
28
|
+
devDependencies: {
|
|
29
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
30
|
+
"@eslint/js": "^9.24.0",
|
|
31
|
+
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
|
32
|
+
"@pulumi/pulumi": "^3.195.0",
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
commander: "^14.0.0",
|
|
35
|
+
eslint: "^9.24.0",
|
|
36
|
+
globals: "^16.0.0",
|
|
37
|
+
husky: "^9.1.7",
|
|
38
|
+
jscodeshift: "^17.3.0",
|
|
39
|
+
"lint-staged": "^15.5.1",
|
|
40
|
+
prettier: "3.5.3",
|
|
41
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
42
|
+
rimraf: "^6.0.1",
|
|
43
|
+
sharp: "^0.34.2",
|
|
44
|
+
typedoc: "^0.28.5",
|
|
45
|
+
"typedoc-plugin-markdown": "^4.7.0",
|
|
46
|
+
"typedoc-vitepress-theme": "^1.1.2",
|
|
47
|
+
"typescript-eslint": "^8.30.1",
|
|
48
|
+
"yocto-spinner": "^0.2.2"
|
|
49
|
+
},
|
|
50
|
+
peerDependencies: {
|
|
51
|
+
typescript: "^5"
|
|
52
|
+
},
|
|
53
|
+
private: true,
|
|
54
|
+
scripts: {
|
|
55
|
+
build: "bun run scripts/build.ts",
|
|
56
|
+
"build:types": "bun tsc -b packages/data",
|
|
57
|
+
clean: "bun scripts/clean.ts && bun i",
|
|
58
|
+
"docs:commit": "bun scripts/docs-commit.ts",
|
|
59
|
+
dev: "bunx sst dev",
|
|
60
|
+
"dev:reset": "sst shell -- bun run scripts/dev-reset.ts",
|
|
61
|
+
"db:reseed": "sst shell -- bun run scripts/dev-reseed-db.ts",
|
|
62
|
+
"db:sync": "sst shell -- bun run scripts/dev-sync-db.ts",
|
|
63
|
+
"db:studio": "sst shell -- bun run --filter @playcademy/data studio",
|
|
64
|
+
"upload-games": "sst shell -- bun run scripts/upload-games.ts",
|
|
65
|
+
"upload-items": "sst shell -- bun run scripts/upload-items.ts",
|
|
66
|
+
"upload-sprites": "sst shell -- bun run scripts/upload-sprites.ts",
|
|
67
|
+
"upload-static-assets": "sst shell -- bun run scripts/upload-static-assets.ts",
|
|
68
|
+
"upload:all": "sst shell -- bun run scripts/upload-all.ts",
|
|
69
|
+
"sync-engine-assets": "bun scripts/sync-engine-assets.ts",
|
|
70
|
+
"sync-vite-templates": "bun scripts/sync-vite-templates.ts",
|
|
71
|
+
"sync-godot-template": "bun scripts/sync-godot-template.ts",
|
|
72
|
+
"sync:all": "bun scripts/sync-all.ts",
|
|
73
|
+
"setup-cloudflare": "bun scripts/setup-cloudflare-dispatch.ts",
|
|
74
|
+
"list-s3-bucket": "sst shell -- bun scripts/list-s3-bucket.ts",
|
|
75
|
+
doctor: "bunx sst shell -- bun scripts/doctor.ts",
|
|
76
|
+
format: "bun run --filter '*' format",
|
|
77
|
+
lint: "bun run --filter '*' lint",
|
|
78
|
+
prepare: "husky",
|
|
79
|
+
sort: "bunx sort-package-json **/package.json",
|
|
80
|
+
test: "bun test",
|
|
81
|
+
"test:unit": "bun scripts/test-unit.ts",
|
|
82
|
+
"test:integration": "bun scripts/test-integration.ts",
|
|
83
|
+
"test:watch": "bun test --watch",
|
|
84
|
+
"docker:relay": "bun run scripts/docker-relay.ts",
|
|
85
|
+
"debug:leaderboard-notifications": "bunx sst shell -- bun scripts/debug/leaderboard-notifications.ts",
|
|
86
|
+
"timeback:caliper": "sst shell -- bun scripts/caliper-cli.ts",
|
|
87
|
+
notify: "NODE_ENV=productionbunx sst shell -- bun scripts/notifications-cli.ts"
|
|
88
|
+
},
|
|
89
|
+
type: "module",
|
|
90
|
+
workspaces: {
|
|
91
|
+
packages: [
|
|
92
|
+
"apps/*",
|
|
93
|
+
"packages/*",
|
|
94
|
+
"games/*",
|
|
95
|
+
"templates/*"
|
|
96
|
+
],
|
|
97
|
+
catalog: {
|
|
98
|
+
sst: "^3.14.11",
|
|
99
|
+
dedent: "^1.6.0",
|
|
100
|
+
typescript: "^5.7.2",
|
|
101
|
+
vite: "^6.3.5",
|
|
102
|
+
eslint: "^9.24.0",
|
|
103
|
+
prettier: "3.5.3",
|
|
104
|
+
"@types/bun": "latest",
|
|
105
|
+
jose: "^5.2.3",
|
|
106
|
+
zod: "^3.25.53",
|
|
107
|
+
"better-auth": "1.3.33"
|
|
108
|
+
},
|
|
109
|
+
catalogs: {
|
|
110
|
+
svelte: {
|
|
111
|
+
svelte: "^5.23.1",
|
|
112
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
113
|
+
"@sveltejs/kit": "^2.16.0",
|
|
114
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
115
|
+
"svelte-check": "^4.2.1",
|
|
116
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
117
|
+
"eslint-plugin-svelte": "^3.0.0"
|
|
118
|
+
},
|
|
119
|
+
database: {
|
|
120
|
+
"drizzle-orm": "^0.42.0",
|
|
121
|
+
"drizzle-kit": "^0.31.0",
|
|
122
|
+
"drizzle-zod": "^0.7.1"
|
|
123
|
+
},
|
|
124
|
+
aws: {
|
|
125
|
+
"@aws-sdk/client-s3": "^3.787.0",
|
|
126
|
+
"@aws-sdk/s3-request-presigner": "^3.787.0"
|
|
127
|
+
},
|
|
128
|
+
linting: {
|
|
129
|
+
"typescript-eslint": "^8.30.1",
|
|
130
|
+
"@eslint/js": "^9.24.0",
|
|
131
|
+
"eslint-config-prettier": "^10.0.1"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/constants/auth.ts
|
|
138
|
+
var BETTER_AUTH_VERSION = package_default.workspaces.catalog["better-auth"];
|
|
139
|
+
|
|
25
140
|
// src/constants/config.ts
|
|
26
141
|
var ENV_FILES = [
|
|
27
142
|
".env",
|
|
@@ -690,6 +805,9 @@ var logger = {
|
|
|
690
805
|
}
|
|
691
806
|
};
|
|
692
807
|
|
|
808
|
+
// src/lib/secrets/env.ts
|
|
809
|
+
init_file_loader();
|
|
810
|
+
|
|
693
811
|
// src/lib/config/loader.ts
|
|
694
812
|
init_file_loader();
|
|
695
813
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { cors } from 'hono/cors'
|
|
8
8
|
|
|
9
|
-
import { PlaycademyClient } from '@playcademy/sdk/server'
|
|
9
|
+
import { PlaycademyClient, verifyGameToken } from '@playcademy/sdk/server'
|
|
10
10
|
|
|
11
11
|
import { populateProcessEnv, reconstructSecrets } from './setup'
|
|
12
12
|
|
|
@@ -17,19 +17,23 @@ import type { RuntimeConfig } from './types'
|
|
|
17
17
|
/**
|
|
18
18
|
* Register CORS middleware
|
|
19
19
|
*
|
|
20
|
+
* Allows cross-origin requests with credentials support.
|
|
21
|
+
* This is required for authentication systems like Better Auth that use cookies.
|
|
22
|
+
*
|
|
20
23
|
* TODO: Harden CORS in production - restrict to trusted origins:
|
|
21
|
-
* - Game's
|
|
24
|
+
* - Game's deploymentUrl (for hosted games)
|
|
22
25
|
* - Game's externalUrl (for external games)
|
|
23
|
-
* - Platform
|
|
24
|
-
* This would require passing game metadata through env bindings during deployment
|
|
26
|
+
* - Platform domains (hub.playcademy.com, hub.dev.playcademy.net)
|
|
25
27
|
*/
|
|
26
28
|
export function registerCors(app: Hono<HonoEnv>): void {
|
|
27
29
|
app.use(
|
|
28
30
|
'*',
|
|
29
31
|
cors({
|
|
30
|
-
origin:
|
|
32
|
+
origin: origin => origin, // Echo back the origin (permissive but allows credentials)
|
|
33
|
+
credentials: true, // Required for cookies/sessions (e.g., Better Auth)
|
|
31
34
|
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
|
32
35
|
allowHeaders: ['Content-Type', 'Authorization'],
|
|
36
|
+
// exposeHeaders: ['set-auth-token'],
|
|
33
37
|
}),
|
|
34
38
|
)
|
|
35
39
|
}
|
|
@@ -72,3 +76,96 @@ export function registerSdkInit(app: Hono<HonoEnv>, config: RuntimeConfig): void
|
|
|
72
76
|
await next()
|
|
73
77
|
})
|
|
74
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Register Playcademy user middleware
|
|
82
|
+
*
|
|
83
|
+
* Verifies platform game tokens and populates c.get('playcademyUser')
|
|
84
|
+
* if a valid token is present in the Authorization header
|
|
85
|
+
*/
|
|
86
|
+
export function registerPlaycademyUser(app: Hono<HonoEnv>): void {
|
|
87
|
+
app.use('*', async (c, next) => {
|
|
88
|
+
const authHeader = c.req.header('Authorization')
|
|
89
|
+
|
|
90
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
91
|
+
const token = authHeader.slice(7)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const result = await verifyGameToken(token)
|
|
95
|
+
|
|
96
|
+
c.set('playcademyUser', result.user)
|
|
97
|
+
} catch {
|
|
98
|
+
// Invalid/expired token - that's fine, just don't set user
|
|
99
|
+
// Routes can decide if they require authentication
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await next()
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Register API 404 handler
|
|
109
|
+
*
|
|
110
|
+
* Returns a helpful JSON response for unmatched API routes.
|
|
111
|
+
* Should be registered after all API routes but before asset fallback.
|
|
112
|
+
*/
|
|
113
|
+
export function registerApiNotFoundHandler(app: Hono<HonoEnv>): void {
|
|
114
|
+
app.all('/api/*', async c => {
|
|
115
|
+
return c.json(
|
|
116
|
+
{
|
|
117
|
+
error: 'Not Found',
|
|
118
|
+
message: `Route ${c.req.method} ${c.req.path} not found`,
|
|
119
|
+
path: c.req.path,
|
|
120
|
+
method: c.req.method,
|
|
121
|
+
},
|
|
122
|
+
404,
|
|
123
|
+
)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Register asset fallback handler
|
|
129
|
+
*
|
|
130
|
+
* Serves static assets from Workers Assets binding.
|
|
131
|
+
* MUST be registered last as it's a catch-all for non-API routes.
|
|
132
|
+
*
|
|
133
|
+
* SPA Routing Support:
|
|
134
|
+
* - First tries to fetch the requested path (e.g., /assets/main.js)
|
|
135
|
+
* - If 404 and NOT an API route, falls back to /index.html
|
|
136
|
+
* - This allows client-side routing (React Router) to work on refresh
|
|
137
|
+
*/
|
|
138
|
+
export function registerAssetFallback(app: Hono<HonoEnv>): void {
|
|
139
|
+
app.get('*', async c => {
|
|
140
|
+
if (!c.env.ASSETS) {
|
|
141
|
+
return c.json(
|
|
142
|
+
{
|
|
143
|
+
error: 'Not Found',
|
|
144
|
+
message: 'Asset not found',
|
|
145
|
+
path: c.req.path,
|
|
146
|
+
},
|
|
147
|
+
404,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Try to fetch the requested asset
|
|
152
|
+
const response = await c.env.ASSETS.fetch(c.req.raw)
|
|
153
|
+
|
|
154
|
+
// If found, return it
|
|
155
|
+
if (response.status !== 404) {
|
|
156
|
+
return response
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// If not found and it's a file request (has extension), return 404
|
|
160
|
+
const path = new URL(c.req.url).pathname
|
|
161
|
+
if (path.includes('.')) {
|
|
162
|
+
return response
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Otherwise, fall back to index.html for predictable routing
|
|
166
|
+
const indexUrl = new URL(c.req.url)
|
|
167
|
+
indexUrl.pathname = '/index.html'
|
|
168
|
+
|
|
169
|
+
return await c.env.ASSETS.fetch(new Request(indexUrl.toString()))
|
|
170
|
+
})
|
|
171
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Middleware Factory
|
|
3
|
+
*
|
|
4
|
+
* Provides a factory function for creating Better Auth session middleware.
|
|
5
|
+
* Games using Better Auth can use this to populate c.get('user') automatically.
|
|
6
|
+
*
|
|
7
|
+
* This is a factory (not a direct middleware) because edge-play can't import
|
|
8
|
+
* game-specific code. Games pass their getAuth function to create the middleware.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Context } from 'hono'
|
|
12
|
+
import type { HonoEnv } from '../types'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create session middleware that populates c.get('user') from Better Auth sessions
|
|
16
|
+
*
|
|
17
|
+
* @param getAuth - Function that returns the game's Better Auth instance
|
|
18
|
+
* @returns Middleware function
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // In your bundled entry point (auto-injected by CLI)
|
|
23
|
+
* import { createSessionMiddleware } from '@playcademy/edge-play'
|
|
24
|
+
* import { getAuth } from './server/lib/auth'
|
|
25
|
+
*
|
|
26
|
+
* app.use('*', createSessionMiddleware(getAuth))
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function createSessionMiddleware(
|
|
30
|
+
getAuth: (c: Context<HonoEnv>) => {
|
|
31
|
+
api: { getSession: (options: { headers: Headers }) => Promise<{ user: unknown } | null> }
|
|
32
|
+
},
|
|
33
|
+
) {
|
|
34
|
+
return async (c: Context<HonoEnv>, next: () => Promise<void>) => {
|
|
35
|
+
const auth = getAuth(c)
|
|
36
|
+
const session = await auth.api.getSession({ headers: c.req.raw.headers })
|
|
37
|
+
|
|
38
|
+
if (session?.user) {
|
|
39
|
+
c.set('user', session.user)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await next()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -6,17 +6,29 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Bundled with esbuild and deployed to Cloudflare Workers (or AWS Lambda).
|
|
8
8
|
* Config is injected at build time via esbuild's `define` option.
|
|
9
|
+
*
|
|
10
|
+
* DO NOT REMOVE any code wrapped by ⚠️ BUILD_MARKER: <marker> ⚠️
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
import { Hono } from 'hono'
|
|
12
14
|
|
|
13
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
registerApiNotFoundHandler,
|
|
17
|
+
registerAssetFallback,
|
|
18
|
+
registerCors,
|
|
19
|
+
registerEnvSetup,
|
|
20
|
+
registerPlaycademyUser,
|
|
21
|
+
registerSdkInit,
|
|
22
|
+
} from './entry/middleware'
|
|
14
23
|
import { setupProcessGlobal } from './entry/setup'
|
|
15
24
|
import { registerBuiltinRoutes } from './register-routes'
|
|
16
25
|
|
|
17
26
|
import type { RuntimeConfig } from './entry/types'
|
|
18
27
|
import type { HonoEnv } from './types'
|
|
19
28
|
|
|
29
|
+
// DO NOT REMOVE THE BELOW COMMENT
|
|
30
|
+
// ⚠️ BUILD_MARKER: CUSTOM_ROUTE_IMPORTS ⚠️
|
|
31
|
+
|
|
20
32
|
/**
|
|
21
33
|
* Config injected at build time by esbuild
|
|
22
34
|
*
|
|
@@ -43,6 +55,10 @@ const app = new Hono<HonoEnv>()
|
|
|
43
55
|
registerCors(app)
|
|
44
56
|
registerEnvSetup(app, PLAYCADEMY_CONFIG)
|
|
45
57
|
registerSdkInit(app, PLAYCADEMY_CONFIG)
|
|
58
|
+
registerPlaycademyUser(app)
|
|
59
|
+
|
|
60
|
+
// DO NOT REMOVE THE BELOW COMMENT
|
|
61
|
+
// ⚠️ BUILD_MARKER: SESSION_MIDDLEWARE ⚠️
|
|
46
62
|
|
|
47
63
|
// Register built-in integration routes based on enabled integrations
|
|
48
64
|
// This function conditionally imports and registers routes like:
|
|
@@ -54,4 +70,20 @@ registerSdkInit(app, PLAYCADEMY_CONFIG)
|
|
|
54
70
|
// its route code is completely removed from the bundle.
|
|
55
71
|
await registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)
|
|
56
72
|
|
|
73
|
+
// DO NOT REMOVE THE BELOW COMMENT
|
|
74
|
+
// ⚠️ BUILD_MARKER: CUSTOM_ROUTES ⚠️
|
|
75
|
+
|
|
76
|
+
// Register API 404 handler
|
|
77
|
+
// Returns JSON error for unmatched /api/* routes
|
|
78
|
+
// Must be registered after all API routes
|
|
79
|
+
registerApiNotFoundHandler(app)
|
|
80
|
+
|
|
81
|
+
// Register static asset fallback handler
|
|
82
|
+
// Serves frontend assets from Workers Assets binding
|
|
83
|
+
// MUST be registered last as it uses a wildcard GET route (app.get('*', ...))
|
|
84
|
+
//
|
|
85
|
+
// In production: Serves frontend assets from Workers Assets binding
|
|
86
|
+
// In local dev: Returns 404 (Vite serves the frontend separately)
|
|
87
|
+
registerAssetFallback(app)
|
|
88
|
+
|
|
57
89
|
export default app
|
|
@@ -17,10 +17,6 @@ import type { HonoEnv, Integrations } from './types'
|
|
|
17
17
|
* @param integrations - Enabled integrations from config
|
|
18
18
|
*/
|
|
19
19
|
export async function registerBuiltinRoutes(app: Hono<HonoEnv>, integrations?: Integrations) {
|
|
20
|
-
// Root page (always included)
|
|
21
|
-
const root = await import('./routes/root')
|
|
22
|
-
app.get('/', root.GET)
|
|
23
|
-
|
|
24
20
|
// Route discovery (always included)
|
|
25
21
|
const routesIndex = await import('./routes/index')
|
|
26
22
|
app.get(ROUTES.INDEX, routesIndex.GET)
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for game backend runtime
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Hono context with typed env
|
|
7
|
-
* - Route handler signatures
|
|
4
|
+
* Base types that can be augmented by game-specific generated types.
|
|
5
|
+
* Games extend these via playcademy-env.d.ts module augmentation.
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
8
|
/// <reference types="@cloudflare/workers-types" />
|
|
@@ -17,7 +15,7 @@ import type { RouteMetadata } from './entry/types'
|
|
|
17
15
|
*/
|
|
18
16
|
export interface Integrations {
|
|
19
17
|
timeback?: unknown
|
|
20
|
-
auth?:
|
|
18
|
+
auth?: boolean
|
|
21
19
|
storage?: { enabled: boolean }
|
|
22
20
|
realtime?: { enabled: boolean }
|
|
23
21
|
}
|
|
@@ -39,6 +37,9 @@ export interface ServerEnv {
|
|
|
39
37
|
/** Game-specific secrets (optional) */
|
|
40
38
|
secrets?: Record<string, string>
|
|
41
39
|
|
|
40
|
+
/** Workers Assets binding for static files (Cloudflare-specific) */
|
|
41
|
+
ASSETS?: { fetch(request: Request): Promise<Response> }
|
|
42
|
+
|
|
42
43
|
/** KV namespace binding (optional, Cloudflare-specific) */
|
|
43
44
|
KV?: KVNamespace
|
|
44
45
|
|
|
@@ -59,6 +60,7 @@ export interface HonoVariables {
|
|
|
59
60
|
sdk: PlaycademyClient
|
|
60
61
|
config: PlaycademyConfig
|
|
61
62
|
routeMetadata: Array<RouteMetadata>
|
|
63
|
+
[key: string]: unknown
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/**
|