playcademy 0.13.15 → 0.13.17

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.js CHANGED
@@ -5735,10 +5735,10 @@ import { writeFileSync as writeFileSync4 } from "fs";
5735
5735
  import { dirname as dirname4, join as join10 } from "path";
5736
5736
  import { fileURLToPath as fileURLToPath2 } from "url";
5737
5737
  var playcademyEnvTemplate = loadTemplateString("playcademy-env.d.ts");
5738
- async function ensurePlaycademyTypes(configPath) {
5738
+ async function ensurePlaycademyTypes() {
5739
5739
  try {
5740
5740
  const workspace = getWorkspace();
5741
- const config = await loadConfig(configPath);
5741
+ const config = await loadConfig();
5742
5742
  const hasDB = hasDatabaseSetup();
5743
5743
  const hasKV = hasKVSetup(config);
5744
5744
  const hasBucket = hasBucketSetup(config);
@@ -8206,10 +8206,9 @@ var initCommand = new Command2("init").description("Initialize a playcademy.conf
8206
8206
  bucket: bucket ?? void 0,
8207
8207
  timeback: timebackConfig ?? void 0
8208
8208
  });
8209
- const configPath = resolve10(getWorkspace(), configFileName);
8210
- writeFileSync8(configPath, configContent, "utf-8");
8209
+ writeFileSync8(resolve10(getWorkspace(), configFileName), configContent, "utf-8");
8211
8210
  if (database || kv || bucket) {
8212
- await ensurePlaycademyTypes(configPath);
8211
+ await ensurePlaycademyTypes();
8213
8212
  }
8214
8213
  displaySuccessMessage({
8215
8214
  configFileName,
@@ -8841,16 +8840,18 @@ function setupCleanupHandlers(workspace, getServer) {
8841
8840
  async function setupServerHotReload(serverRef, port, workspace, config, loggerEnabled) {
8842
8841
  return startHotReload(
8843
8842
  async () => {
8844
- if (serverRef.current) {
8845
- await serverRef.current.dispose();
8846
- }
8847
8843
  const newConfig = await loadConfig();
8848
- const { server } = await startDevServer({
8844
+ await bundleBackend(newConfig, { sourcemap: false, minify: false });
8845
+ const oldServer = serverRef.current;
8846
+ if (oldServer) {
8847
+ await oldServer.dispose();
8848
+ }
8849
+ const { server: newServer } = await startDevServer({
8849
8850
  port,
8850
8851
  config: newConfig,
8851
8852
  logger: loggerEnabled
8852
8853
  });
8853
- serverRef.current = server;
8854
+ serverRef.current = newServer;
8854
8855
  await discoverRoutes(getCustomRoutesDirectory(workspace, newConfig));
8855
8856
  },
8856
8857
  { config }
@@ -4,42 +4,35 @@
4
4
  * This route will be available at: https://<your-game-slug>.playcademy.gg/api/sample/bucket
5
5
  */
6
6
 
7
- /**
8
- * File metadata for bucket storage
9
- */
7
+ import type { Context } from 'hono'
8
+
10
9
  interface FileInfo {
11
- /** File key in bucket */
12
10
  key: string
13
- /** File size in bytes */
11
+ name: string
14
12
  size: number
15
- /** Upload timestamp */
16
13
  uploaded: string
14
+ extension: string
17
15
  }
18
16
 
19
17
  /**
20
18
  * GET /api/sample/bucket
21
19
  *
22
- * List or retrieve files from bucket storage
20
+ * List all files or retrieve a specific file by key
21
+ * Query params:
22
+ * - key: File key to download (optional)
23
+ * - prefix: Filter files by prefix (optional)
23
24
  */
24
25
  export async function GET(c: Context): Promise<Response> {
25
26
  try {
26
27
  const fileKey = c.req.query('key')
27
28
 
28
- // If key provided, get specific file
29
29
  if (fileKey) {
30
30
  const object = await c.env.BUCKET.get(fileKey)
31
31
 
32
32
  if (!object) {
33
- return c.json(
34
- {
35
- success: false,
36
- error: 'File not found',
37
- },
38
- 404,
39
- )
33
+ return c.json({ success: false, error: 'File not found' }, 404)
40
34
  }
41
35
 
42
- // Return file with appropriate headers
43
36
  return new Response(object.body, {
44
37
  headers: {
45
38
  'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
@@ -49,28 +42,28 @@ export async function GET(c: Context): Promise<Response> {
49
42
  })
50
43
  }
51
44
 
52
- // Otherwise, list files
53
45
  const prefix = c.req.query('prefix') || ''
54
46
  const listed = await c.env.BUCKET.list({ prefix })
55
47
 
56
- const files: FileInfo[] = listed.objects.map(obj => ({
57
- key: obj.key,
58
- size: obj.size,
59
- uploaded: obj.uploaded.toISOString(),
60
- }))
48
+ const files: FileInfo[] = (listed.objects as Array<{ key: string; size: number; uploaded: Date }>).map(obj => {
49
+ const fileName = obj.key.split('/').pop() || obj.key
50
+ const extensionMatch = fileName.match(/\.([^.]+)$/)
51
+ return {
52
+ key: obj.key,
53
+ name: fileName,
54
+ size: obj.size,
55
+ uploaded: obj.uploaded.toISOString(),
56
+ extension: extensionMatch?.[1]?.toLowerCase() || 'unknown',
57
+ }
58
+ })
61
59
 
62
60
  return c.json({
63
61
  success: true,
64
- data: files,
65
- truncated: listed.truncated,
62
+ data: { files, total: files.length, truncated: listed.truncated },
66
63
  })
67
64
  } catch (error) {
68
65
  return c.json(
69
- {
70
- success: false,
71
- error: 'Failed to access bucket storage',
72
- details: error instanceof Error ? error.message : String(error),
73
- },
66
+ { success: false, error: 'Failed to list files', details: error instanceof Error ? error.message : 'Unknown error' },
74
67
  500,
75
68
  )
76
69
  }
@@ -80,60 +73,60 @@ export async function GET(c: Context): Promise<Response> {
80
73
  * PUT /api/sample/bucket
81
74
  *
82
75
  * Upload a file to bucket storage
76
+ * Query params:
77
+ * - name: File name (required)
83
78
  */
84
79
  export async function PUT(c: Context): Promise<Response> {
85
80
  try {
86
- const fileKey = c.req.query('key')
87
-
88
- if (!fileKey) {
89
- return c.json(
90
- {
91
- success: false,
92
- error: 'File key is required',
93
- },
94
- 400,
95
- )
81
+ const fileName = c.req.query('name')
82
+ if (!fileName) {
83
+ return c.json({ success: false, error: 'name query param is required' }, 400)
96
84
  }
97
85
 
98
- // Get file from request body
99
86
  const body = await c.req.arrayBuffer()
100
-
101
87
  if (!body || body.byteLength === 0) {
102
- return c.json(
103
- {
104
- success: false,
105
- error: 'File body is required',
106
- },
107
- 400,
108
- )
88
+ return c.json({ success: false, error: 'File body is required' }, 400)
109
89
  }
110
90
 
111
- // Get content type from header or default to binary
112
- const contentType = c.req.header('Content-Type') || 'application/octet-stream'
91
+ // Validate file size (10MB limit)
92
+ const maxSize = 10 * 1024 * 1024
93
+ if (body.byteLength > maxSize) {
94
+ return c.json({ success: false, error: `File exceeds ${maxSize / 1024 / 1024}MB limit` }, 400)
95
+ }
113
96
 
114
- // Upload to bucket
115
- await c.env.BUCKET.put(fileKey, body, {
116
- httpMetadata: {
117
- contentType,
118
- },
119
- })
97
+ // Sanitize name and create key with timestamp
98
+ const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, '_')
99
+ const fileKey = `uploads/${Date.now()}-${safeName}`
100
+
101
+ // Map extension to content-type
102
+ const extension = safeName.split('.').pop()?.toLowerCase() || ''
103
+ const contentTypeMap: Record<string, string> = {
104
+ jpg: 'image/jpeg',
105
+ jpeg: 'image/jpeg',
106
+ png: 'image/png',
107
+ gif: 'image/gif',
108
+ webp: 'image/webp',
109
+ pdf: 'application/pdf',
110
+ txt: 'text/plain',
111
+ json: 'application/json',
112
+ }
113
+ const contentType = contentTypeMap[extension] || c.req.header('Content-Type') || 'application/octet-stream'
114
+
115
+ await c.env.BUCKET.put(fileKey, body, { httpMetadata: { contentType } })
120
116
 
121
117
  return c.json({
122
118
  success: true,
123
119
  data: {
124
120
  key: fileKey,
121
+ name: safeName,
125
122
  size: body.byteLength,
126
123
  uploaded: new Date().toISOString(),
124
+ extension: extension || 'unknown',
127
125
  },
128
- message: 'File uploaded successfully',
129
126
  })
130
127
  } catch (error) {
131
128
  return c.json(
132
- {
133
- success: false,
134
- error: 'Failed to upload file',
135
- details: error instanceof Error ? error.message : String(error),
136
- },
129
+ { success: false, error: 'Failed to upload file', details: error instanceof Error ? error.message : 'Unknown error' },
137
130
  500,
138
131
  )
139
132
  }
@@ -143,35 +136,22 @@ export async function PUT(c: Context): Promise<Response> {
143
136
  * DELETE /api/sample/bucket
144
137
  *
145
138
  * Delete a file from bucket storage
139
+ * Query params:
140
+ * - key: File key to delete (required)
146
141
  */
147
142
  export async function DELETE(c: Context): Promise<Response> {
148
143
  try {
149
144
  const fileKey = c.req.query('key')
150
145
 
151
146
  if (!fileKey) {
152
- return c.json(
153
- {
154
- success: false,
155
- error: 'File key is required',
156
- },
157
- 400,
158
- )
147
+ return c.json({ success: false, error: 'key query param is required' }, 400)
159
148
  }
160
149
 
161
- // Delete from bucket
162
150
  await c.env.BUCKET.delete(fileKey)
163
-
164
- return c.json({
165
- success: true,
166
- message: 'File deleted successfully',
167
- })
151
+ return c.json({ success: true, message: 'File deleted successfully' })
168
152
  } catch (error) {
169
153
  return c.json(
170
- {
171
- success: false,
172
- error: 'Failed to delete file',
173
- details: error instanceof Error ? error.message : String(error),
174
- },
154
+ { success: false, error: 'Failed to delete file', details: error instanceof Error ? error.message : 'Unknown error' },
175
155
  500,
176
156
  )
177
157
  }
package/dist/utils.js CHANGED
@@ -1549,10 +1549,10 @@ import { writeFileSync as writeFileSync3 } from "fs";
1549
1549
  import { dirname as dirname4, join as join8 } from "path";
1550
1550
  import { fileURLToPath as fileURLToPath2 } from "url";
1551
1551
  var playcademyEnvTemplate = loadTemplateString("playcademy-env.d.ts");
1552
- async function ensurePlaycademyTypes(configPath) {
1552
+ async function ensurePlaycademyTypes() {
1553
1553
  try {
1554
1554
  const workspace = getWorkspace();
1555
- const config = await loadConfig(configPath);
1555
+ const config = await loadConfig();
1556
1556
  const hasDB = hasDatabaseSetup();
1557
1557
  const hasKV = hasKVSetup(config);
1558
1558
  const hasBucket = hasBucketSetup(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playcademy",
3
- "version": "0.13.15",
3
+ "version": "0.13.17",
4
4
  "type": "module",
5
5
  "module": "./dist/index.js",
6
6
  "main": "./dist/index.js",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@inquirer/prompts": "^7.8.6",
43
- "@playcademy/sdk": "0.1.6",
43
+ "@playcademy/sdk": "0.1.7",
44
44
  "better-sqlite3": "^12.4.1",
45
45
  "chokidar": "^4.0.3",
46
46
  "colorette": "^2.0.20",