create-monolayer-app 0.5.1 → 0.6.3

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.
Files changed (87) hide show
  1. package/bin/index.js +91 -59
  2. package/package.json +19 -16
  3. package/template/.agents/skills/{monolayer-application-permissions → application-permissions}/SKILL.md +2 -2
  4. package/template/.agents/skills/application-permissions/agents/openai.yaml +4 -0
  5. package/template/.agents/skills/kysely/SKILL.md +48 -0
  6. package/template/.agents/skills/kysely/agents/openai.yaml +4 -0
  7. package/template/.agents/skills/kysely/references/execution-plugins-and-runtime.md +67 -0
  8. package/template/.agents/skills/kysely/references/expressions-and-helpers.md +102 -0
  9. package/template/.agents/skills/kysely/references/migrations.md +43 -0
  10. package/template/.agents/skills/kysely/references/official-docs-map.md +97 -0
  11. package/template/.agents/skills/kysely/references/query-building.md +119 -0
  12. package/template/.agents/skills/kysely/references/transactions.md +47 -0
  13. package/template/.agents/skills/kysely/references/troubleshooting-and-boundaries.md +67 -0
  14. package/template/.agents/skills/kysely/references/types.md +58 -0
  15. package/template/.agents/skills/persistent-app-data/SKILL.md +74 -0
  16. package/template/.agents/skills/persistent-app-data/agents/openai.yaml +4 -0
  17. package/template/.agents/skills/persistent-app-data/references/generated-types.md +23 -0
  18. package/template/.agents/skills/persistent-app-data/references/migrations.md +68 -0
  19. package/template/.agents/skills/vitest/GENERATION.md +25 -0
  20. package/template/.agents/skills/vitest/SKILL.md +59 -0
  21. package/template/.agents/skills/vitest/references/advanced-environments.md +26 -0
  22. package/template/.agents/skills/vitest/references/advanced-projects.md +31 -0
  23. package/template/.agents/skills/vitest/references/advanced-type-testing.md +30 -0
  24. package/template/.agents/skills/vitest/references/advanced-vi.md +23 -0
  25. package/template/.agents/skills/vitest/references/core-cli.md +23 -0
  26. package/template/.agents/skills/vitest/references/core-config.md +24 -0
  27. package/template/.agents/skills/vitest/references/core-describe.md +24 -0
  28. package/template/.agents/skills/vitest/references/core-expect.md +35 -0
  29. package/template/.agents/skills/vitest/references/core-hooks.md +26 -0
  30. package/template/.agents/skills/vitest/references/core-test-api.md +29 -0
  31. package/template/.agents/skills/vitest/references/features-benchmarking.md +25 -0
  32. package/template/.agents/skills/vitest/references/features-concurrency.md +20 -0
  33. package/template/.agents/skills/vitest/references/features-context.md +27 -0
  34. package/template/.agents/skills/vitest/references/features-coverage.md +17 -0
  35. package/template/.agents/skills/vitest/references/features-filtering.md +24 -0
  36. package/template/.agents/skills/vitest/references/features-mocking.md +32 -0
  37. package/template/.agents/skills/vitest/references/features-reporters.md +22 -0
  38. package/template/.agents/skills/vitest/references/features-snapshots.md +23 -0
  39. package/template/.agents/skills/vitest/references/features-test-tags.md +30 -0
  40. package/template/.agents/skills/vitest/references/version-compatibility.md +19 -0
  41. package/template/.claude/CLAUDE.md +2 -2
  42. package/template/.react-router/types/+routes.ts +41 -0
  43. package/template/.react-router/types/+server-build.d.ts +18 -0
  44. package/template/.react-router/types/app/+types/root.ts +68 -0
  45. package/template/.react-router/types/app/routes/+types/files.ts +71 -0
  46. package/template/.react-router/types/app/routes/+types/home.ts +71 -0
  47. package/template/AGENTS.md +6 -2
  48. package/template/README.md +10 -0
  49. package/template/app/routes/files.ts +3 -3
  50. package/template/build/client/assets/entry.client-Bc58m2-m.js +9 -0
  51. package/template/build/client/assets/errorBoundaries-CQxPIuVU.js +16 -0
  52. package/template/build/client/assets/files-BvRk9kiK.js +0 -0
  53. package/template/build/client/assets/home-CH2INc5e.js +1 -0
  54. package/template/build/client/assets/jsx-runtime-eLCqVKHJ.js +1 -0
  55. package/template/build/client/assets/logo-dark-pX2395Y0.svg +23 -0
  56. package/template/build/client/assets/logo-light-CVbx2LBR.svg +23 -0
  57. package/template/build/client/assets/manifest-2d65dd0f.js +1 -0
  58. package/template/build/client/assets/root-BeNS6njP.css +2 -0
  59. package/template/build/client/assets/root-C1tmjS9o.js +1 -0
  60. package/template/build/client/favicon.ico +0 -0
  61. package/template/build/server/index.js +1096 -0
  62. package/template/lib/db/client.ts +1 -27
  63. package/template/package-lock.json +8221 -0
  64. package/template/package.json +7 -3
  65. package/template/vendor/file-bucket/{client.ts → index.client.ts} +11 -9
  66. package/template/vendor/file-bucket/{server.ts → index.server.ts} +13 -8
  67. package/template/vendor/file-bucket/{index.ts → lib/file-bucket.ts} +2 -1
  68. package/template/vendor/file-bucket/{react-router.ts → react-router.server.ts} +15 -13
  69. package/template/vendor/kysely-dsql/dsql/dsql-kysely.ts +31 -3
  70. package/template/vendor/kysely-dsql/generator/migration-file.ts +3 -3
  71. package/template/vendor/kysely-dsql/plugins/index.ts +0 -2
  72. package/template/vendor/package-app/package-app.mjs +10 -3
  73. package/template/vendor/package-app/package-app.test.mjs +167 -0
  74. package/template/vite.config.ts +3 -1
  75. package/template/vitest.config.ts +7 -2
  76. package/template/.agents/skills/monolayer-application-permissions/agents/openai.yaml +0 -4
  77. package/template/vendor/kysely-dsql/plugins/soft-delete.plugin.ts +0 -217
  78. /package/template/.agents/skills/{monolayer-application-permissions → application-permissions}/references/development-and-verification.md +0 -0
  79. /package/template/.agents/skills/{monolayer-application-permissions → application-permissions}/references/interface-examples.md +0 -0
  80. /package/template/.agents/skills/{monolayer-application-permissions → application-permissions}/references/server-examples.md +0 -0
  81. /package/template/vendor/file-bucket/{errors.ts → lib/errors.server.ts} +0 -0
  82. /package/template/vendor/file-bucket/{multipart.ts → lib/multipart.ts} +0 -0
  83. /package/template/vendor/file-bucket/{protocol.ts → lib/protocol.ts} +0 -0
  84. /package/template/vendor/file-bucket/{transport.ts → lib/transport.client.ts} +0 -0
  85. /package/template/vendor/file-bucket/{types.ts → lib/types.ts} +0 -0
  86. /package/template/vendor/file-bucket/{transfers.ts → lib/upload-signed-url.ts} +0 -0
  87. /package/template/vendor/file-bucket/{xml.ts → lib/xml.ts} +0 -0
@@ -0,0 +1,1096 @@
1
+ import { PassThrough } from "node:stream";
2
+ import { createReadableStreamFromReadable } from "@react-router/node";
3
+ import { Links, Meta, Outlet, Scripts, ScrollRestoration, ServerRouter, UNSAFE_withComponentProps, UNSAFE_withErrorBoundaryProps, createContext, isRouteErrorResponse } from "react-router";
4
+ import { isbot } from "isbot";
5
+ import { renderToPipeableStream } from "react-dom/server";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { z } from "zod";
8
+ import { createHash } from "node:crypto";
9
+ import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
10
+ import "@aws-sdk/lib-storage";
11
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
12
+ //#region \0rolldown/runtime.js
13
+ var __defProp = Object.defineProperty;
14
+ var __exportAll = (all, no_symbols) => {
15
+ let target = {};
16
+ for (var name in all) __defProp(target, name, {
17
+ get: all[name],
18
+ enumerable: true
19
+ });
20
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
21
+ return target;
22
+ };
23
+ //#endregion
24
+ //#region node_modules/@react-router/dev/dist/config/defaults/entry.server.node.tsx
25
+ var entry_server_node_exports = /* @__PURE__ */ __exportAll({
26
+ default: () => handleRequest,
27
+ streamTimeout: () => streamTimeout
28
+ });
29
+ var streamTimeout = 5e3;
30
+ function handleRequest(request, responseStatusCode, responseHeaders, routerContext, loadContext) {
31
+ if (request.method.toUpperCase() === "HEAD") return new Response(null, {
32
+ status: responseStatusCode,
33
+ headers: responseHeaders
34
+ });
35
+ return new Promise((resolve, reject) => {
36
+ let shellRendered = false;
37
+ let userAgent = request.headers.get("user-agent");
38
+ let readyOption = userAgent && isbot(userAgent) || routerContext.isSpaMode ? "onAllReady" : "onShellReady";
39
+ let timeoutId = setTimeout(() => abort(), 6e3);
40
+ const { pipe, abort } = renderToPipeableStream(/* @__PURE__ */ jsx(ServerRouter, {
41
+ context: routerContext,
42
+ url: request.url
43
+ }), {
44
+ [readyOption]() {
45
+ shellRendered = true;
46
+ const body = new PassThrough({ final(callback) {
47
+ clearTimeout(timeoutId);
48
+ timeoutId = void 0;
49
+ callback();
50
+ } });
51
+ const stream = createReadableStreamFromReadable(body);
52
+ responseHeaders.set("Content-Type", "text/html");
53
+ pipe(body);
54
+ resolve(new Response(stream, {
55
+ headers: responseHeaders,
56
+ status: responseStatusCode
57
+ }));
58
+ },
59
+ onShellError(error) {
60
+ reject(error);
61
+ },
62
+ onError(error) {
63
+ responseStatusCode = 500;
64
+ if (shellRendered) console.error(error);
65
+ }
66
+ });
67
+ });
68
+ }
69
+ //#endregion
70
+ //#region lib/user-context.server.ts
71
+ var userContextMiddleware = async ({ request, context }, next) => {
72
+ const result = userSchema.safeParse({
73
+ email: request.headers.get("x-user-email"),
74
+ role: request.headers.get("x-user-authorization")
75
+ });
76
+ if (!result.success) throw new Response("Unauthorized", { status: 401 });
77
+ context.set(userContext, result.data);
78
+ return next();
79
+ };
80
+ var userSchema = z.object({
81
+ email: z.email(),
82
+ role: z.enum([
83
+ "owner",
84
+ "editor",
85
+ "viewer"
86
+ ])
87
+ });
88
+ var userContext = createContext();
89
+ //#endregion
90
+ //#region app/root.tsx
91
+ var root_exports = /* @__PURE__ */ __exportAll({
92
+ ErrorBoundary: () => ErrorBoundary,
93
+ Layout: () => Layout,
94
+ default: () => root_default,
95
+ links: () => links,
96
+ middleware: () => middleware
97
+ });
98
+ var links = () => [
99
+ {
100
+ rel: "preconnect",
101
+ href: "https://fonts.googleapis.com"
102
+ },
103
+ {
104
+ rel: "preconnect",
105
+ href: "https://fonts.gstatic.com",
106
+ crossOrigin: "anonymous"
107
+ },
108
+ {
109
+ rel: "stylesheet",
110
+ href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
111
+ }
112
+ ];
113
+ function Layout({ children }) {
114
+ return /* @__PURE__ */ jsxs("html", {
115
+ lang: "en",
116
+ children: [/* @__PURE__ */ jsxs("head", { children: [
117
+ /* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
118
+ /* @__PURE__ */ jsx("meta", {
119
+ name: "viewport",
120
+ content: "width=device-width, initial-scale=1"
121
+ }),
122
+ /* @__PURE__ */ jsx(Meta, {}),
123
+ /* @__PURE__ */ jsx(Links, {})
124
+ ] }), /* @__PURE__ */ jsxs("body", { children: [
125
+ children,
126
+ /* @__PURE__ */ jsx(ScrollRestoration, {}),
127
+ /* @__PURE__ */ jsx(Scripts, {})
128
+ ] })]
129
+ });
130
+ }
131
+ var root_default = UNSAFE_withComponentProps(function App() {
132
+ return /* @__PURE__ */ jsx(Outlet, {});
133
+ });
134
+ var ErrorBoundary = UNSAFE_withErrorBoundaryProps(function ErrorBoundary({ error }) {
135
+ let message = "Oops!";
136
+ let details = "An unexpected error occurred.";
137
+ let stack;
138
+ if (isRouteErrorResponse(error)) {
139
+ message = error.status === 404 ? "404" : "Error";
140
+ details = error.status === 404 ? "The requested page could not be found." : error.statusText || details;
141
+ }
142
+ return /* @__PURE__ */ jsxs("main", {
143
+ className: "container mx-auto p-4 pt-16",
144
+ children: [
145
+ /* @__PURE__ */ jsx("h1", { children: message }),
146
+ /* @__PURE__ */ jsx("p", { children: details }),
147
+ stack
148
+ ]
149
+ });
150
+ });
151
+ var middleware = [userContextMiddleware];
152
+ //#endregion
153
+ //#region app/welcome/logo-dark.svg
154
+ var logo_dark_default = "/assets/logo-dark-pX2395Y0.svg";
155
+ //#endregion
156
+ //#region app/welcome/logo-light.svg
157
+ var logo_light_default = "/assets/logo-light-CVbx2LBR.svg";
158
+ //#endregion
159
+ //#region app/welcome/welcome.tsx
160
+ function Welcome() {
161
+ return /* @__PURE__ */ jsx("main", {
162
+ className: "flex items-center justify-center pt-16 pb-4",
163
+ children: /* @__PURE__ */ jsxs("div", {
164
+ className: "flex min-h-0 flex-1 flex-col items-center gap-16",
165
+ children: [/* @__PURE__ */ jsx("header", {
166
+ className: "flex flex-col items-center gap-9",
167
+ children: /* @__PURE__ */ jsxs("div", {
168
+ className: "w-[500px] max-w-[100vw] p-4",
169
+ children: [/* @__PURE__ */ jsx("img", {
170
+ src: logo_light_default,
171
+ alt: "React Router",
172
+ className: "block w-full dark:hidden"
173
+ }), /* @__PURE__ */ jsx("img", {
174
+ src: logo_dark_default,
175
+ alt: "React Router",
176
+ className: "hidden w-full dark:block"
177
+ })]
178
+ })
179
+ }), /* @__PURE__ */ jsx("div", {
180
+ className: "w-full max-w-[300px] space-y-6 px-4",
181
+ children: /* @__PURE__ */ jsxs("nav", {
182
+ className: "space-y-4 rounded-3xl border border-gray-200 p-6 dark:border-gray-700",
183
+ children: [/* @__PURE__ */ jsx("p", {
184
+ className: "text-center leading-6 text-gray-700 dark:text-gray-200",
185
+ children: "What's next?"
186
+ }), /* @__PURE__ */ jsx("ul", { children: resources.map(({ href, text, icon }) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
187
+ className: "group flex items-center gap-3 self-stretch p-3 leading-normal text-blue-700 hover:underline dark:text-blue-500",
188
+ href,
189
+ target: "_blank",
190
+ rel: "noreferrer",
191
+ children: [icon, text]
192
+ }) }, href)) })]
193
+ })
194
+ })]
195
+ })
196
+ });
197
+ }
198
+ var resources = [{
199
+ href: "https://reactrouter.com/docs",
200
+ text: "React Router Docs",
201
+ icon: /* @__PURE__ */ jsx("svg", {
202
+ xmlns: "http://www.w3.org/2000/svg",
203
+ width: "24",
204
+ height: "20",
205
+ viewBox: "0 0 20 20",
206
+ fill: "none",
207
+ className: "stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300",
208
+ children: /* @__PURE__ */ jsx("path", {
209
+ d: "M9.99981 10.0751V9.99992M17.4688 17.4688C15.889 19.0485 11.2645 16.9853 7.13958 12.8604C3.01467 8.73546 0.951405 4.11091 2.53116 2.53116C4.11091 0.951405 8.73546 3.01467 12.8604 7.13958C16.9853 11.2645 19.0485 15.889 17.4688 17.4688ZM2.53132 17.4688C0.951566 15.8891 3.01483 11.2645 7.13974 7.13963C11.2647 3.01471 15.8892 0.951453 17.469 2.53121C19.0487 4.11096 16.9854 8.73551 12.8605 12.8604C8.73562 16.9853 4.11107 19.0486 2.53132 17.4688Z",
210
+ strokeWidth: "1.5",
211
+ strokeLinecap: "round"
212
+ })
213
+ })
214
+ }, {
215
+ href: "https://rmx.as/discord",
216
+ text: "Join Discord",
217
+ icon: /* @__PURE__ */ jsx("svg", {
218
+ xmlns: "http://www.w3.org/2000/svg",
219
+ width: "24",
220
+ height: "20",
221
+ viewBox: "0 0 24 20",
222
+ fill: "none",
223
+ className: "stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300",
224
+ children: /* @__PURE__ */ jsx("path", {
225
+ d: "M15.0686 1.25995L14.5477 1.17423L14.2913 1.63578C14.1754 1.84439 14.0545 2.08275 13.9422 2.31963C12.6461 2.16488 11.3406 2.16505 10.0445 2.32014C9.92822 2.08178 9.80478 1.84975 9.67412 1.62413L9.41449 1.17584L8.90333 1.25995C7.33547 1.51794 5.80717 1.99419 4.37748 2.66939L4.19 2.75793L4.07461 2.93019C1.23864 7.16437 0.46302 11.3053 0.838165 15.3924L0.868838 15.7266L1.13844 15.9264C2.81818 17.1714 4.68053 18.1233 6.68582 18.719L7.18892 18.8684L7.50166 18.4469C7.96179 17.8268 8.36504 17.1824 8.709 16.4944L8.71099 16.4904C10.8645 17.0471 13.128 17.0485 15.2821 16.4947C15.6261 17.1826 16.0293 17.8269 16.4892 18.4469L16.805 18.8725L17.3116 18.717C19.3056 18.105 21.1876 17.1751 22.8559 15.9238L23.1224 15.724L23.1528 15.3923C23.5873 10.6524 22.3579 6.53306 19.8947 2.90714L19.7759 2.73227L19.5833 2.64518C18.1437 1.99439 16.6386 1.51826 15.0686 1.25995ZM16.6074 10.7755L16.6074 10.7756C16.5934 11.6409 16.0212 12.1444 15.4783 12.1444C14.9297 12.1444 14.3493 11.6173 14.3493 10.7877C14.3493 9.94885 14.9378 9.41192 15.4783 9.41192C16.0471 9.41192 16.6209 9.93851 16.6074 10.7755ZM8.49373 12.1444C7.94513 12.1444 7.36471 11.6173 7.36471 10.7877C7.36471 9.94885 7.95323 9.41192 8.49373 9.41192C9.06038 9.41192 9.63892 9.93712 9.6417 10.7815C9.62517 11.6239 9.05462 12.1444 8.49373 12.1444Z",
226
+ strokeWidth: "1.5"
227
+ })
228
+ })
229
+ }];
230
+ //#endregion
231
+ //#region app/routes/home.tsx
232
+ var home_exports = /* @__PURE__ */ __exportAll({
233
+ default: () => home_default,
234
+ meta: () => meta
235
+ });
236
+ function meta({}) {
237
+ return [{ title: "New React Router App" }, {
238
+ name: "description",
239
+ content: "Welcome to React Router!"
240
+ }];
241
+ }
242
+ var home_default = UNSAFE_withComponentProps(function Home() {
243
+ return /* @__PURE__ */ jsx(Welcome, {});
244
+ });
245
+ //#endregion
246
+ //#region vendor/file-bucket/lib/xml.ts
247
+ var XML_DECL = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
248
+ var ParseError = class extends Error {
249
+ constructor(message) {
250
+ super(message);
251
+ this.name = "ParseError";
252
+ }
253
+ };
254
+ function escapeXml(str) {
255
+ if (str === null || str === void 0) return "";
256
+ return String(str).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
257
+ }
258
+ function unescapeXml(str) {
259
+ return str.replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&").replaceAll(/&#(\d+);/gu, (_, dec) => String.fromCodePoint(Math.trunc(Number(dec)))).replaceAll(/&#x([0-9a-fA-F]+);/gu, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)));
260
+ }
261
+ /**
262
+ * Strips namespace prefix or Clark notation from a tag name.
263
+ * e.g. "{http://...}Rule" -> "Rule", "s3:Rule" -> "Rule", "Rule" -> "Rule"
264
+ */
265
+ function localTagName(tag) {
266
+ if (tag.startsWith("{")) {
267
+ const idx = tag.indexOf("}");
268
+ if (idx !== -1) return tag.slice(idx + 1);
269
+ }
270
+ const colonIdx = tag.indexOf(":");
271
+ if (colonIdx !== -1) return tag.slice(colonIdx + 1);
272
+ return tag;
273
+ }
274
+ function isString(value) {
275
+ return typeof value === "string";
276
+ }
277
+ var XmlElement = class XmlElement {
278
+ tag;
279
+ attrs;
280
+ text;
281
+ children;
282
+ constructor(tag, attrs = {}, text) {
283
+ this.tag = tag;
284
+ this.attrs = { ...attrs };
285
+ this.text = text;
286
+ this.children = [];
287
+ }
288
+ localName() {
289
+ return localTagName(this.tag);
290
+ }
291
+ subElement(tag, textOrAttrs, text) {
292
+ const el = isString(textOrAttrs) ? new XmlElement(tag, {}, textOrAttrs) : new XmlElement(tag, textOrAttrs || {}, text);
293
+ this.children.push(el);
294
+ return el;
295
+ }
296
+ find(tag) {
297
+ const targetLocal = localTagName(tag);
298
+ return this.children.find((c) => c.tag === tag || c.localName() === targetLocal);
299
+ }
300
+ findAll(tag) {
301
+ const targetLocal = localTagName(tag);
302
+ return this.children.filter((c) => c.tag === tag || c.localName() === targetLocal);
303
+ }
304
+ findText(tag) {
305
+ return this.find(tag)?.text;
306
+ }
307
+ findDescendant(tag) {
308
+ const targetLocal = localTagName(tag);
309
+ for (const c of this.children) {
310
+ if (c.tag === tag || c.localName() === targetLocal) return c;
311
+ const deep = c.findDescendant(tag);
312
+ if (deep) return deep;
313
+ }
314
+ }
315
+ findAllDescendants(tag) {
316
+ const targetLocal = localTagName(tag);
317
+ const result = [];
318
+ for (const c of this.children) {
319
+ if (c.tag === tag || c.localName() === targetLocal) result.push(c);
320
+ result.push(...c.findAllDescendants(tag));
321
+ }
322
+ return result;
323
+ }
324
+ iter() {
325
+ const result = [this];
326
+ for (const child of this.children) result.push(...child.iter());
327
+ return result;
328
+ }
329
+ toXml(includeDeclaration = false) {
330
+ let s = includeDeclaration ? `${XML_DECL}\n` : "";
331
+ s += `<${this.tag}`;
332
+ for (const [k, v] of Object.entries(this.attrs)) s += ` ${k}="${escapeXml(v)}"`;
333
+ if (this.children.length === 0 && this.text === void 0) {
334
+ s += "/>";
335
+ return s;
336
+ }
337
+ s += ">";
338
+ if (this.text !== void 0) s += escapeXml(this.text);
339
+ for (const child of this.children) s += child.toXml(false);
340
+ s += `</${this.tag}>`;
341
+ return s;
342
+ }
343
+ };
344
+ function parseXml(xml) {
345
+ let str = (typeof xml === "string" ? xml : xml.toString("utf-8")).trim();
346
+ if (!str) throw new ParseError("Empty XML document");
347
+ if (str.startsWith("<?xml")) {
348
+ const end = str.indexOf("?>");
349
+ if (end !== -1) str = str.slice(end + 2).trim();
350
+ }
351
+ let pos = 0;
352
+ const len = str.length;
353
+ let root = null;
354
+ const stack = [];
355
+ while (pos < len) {
356
+ const lt = str.indexOf("<", pos);
357
+ if (lt === -1) {
358
+ if (stack.length > 0) {
359
+ const text = unescapeXml(str.slice(pos).trim());
360
+ const top = stack.at(-1);
361
+ if (text && top) top.text = (top.text || "") + text;
362
+ }
363
+ break;
364
+ }
365
+ if (lt > pos && stack.length > 0) {
366
+ const text = unescapeXml(str.slice(pos, lt).trim());
367
+ const top = stack.at(-1);
368
+ if (text && top) top.text = (top.text || "") + text;
369
+ }
370
+ pos = lt + 1;
371
+ if (str.startsWith("!--", pos)) {
372
+ const end = str.indexOf("-->", pos + 3);
373
+ pos = end === -1 ? len : end + 3;
374
+ continue;
375
+ }
376
+ if (str.startsWith("![CDATA[", pos)) {
377
+ const end = str.indexOf("]]>", pos + 8);
378
+ const cdata = end === -1 ? str.slice(pos + 8) : str.slice(pos + 8, end);
379
+ const top = stack.at(-1);
380
+ if (top) top.text = (top.text || "") + cdata;
381
+ pos = end === -1 ? len : end + 3;
382
+ continue;
383
+ }
384
+ if (str.startsWith("?", pos) || str.startsWith("!", pos)) {
385
+ const gt = str.indexOf(">", pos);
386
+ pos = gt === -1 ? len : gt + 1;
387
+ continue;
388
+ }
389
+ if (str[pos] === "/") {
390
+ const gt = str.indexOf(">", pos);
391
+ if (gt === -1) throw new ParseError("Unclosed closing tag");
392
+ const closeTag = str.slice(pos + 1, gt).trim();
393
+ const open = stack.pop();
394
+ if (!open) throw new ParseError(`Unexpected closing tag: </${closeTag}>`);
395
+ if (localTagName(open.tag) !== localTagName(closeTag) && open.tag !== closeTag) throw new ParseError(`Mismatched closing tag: expected </${open.tag}>, got </${closeTag}>`);
396
+ pos = gt + 1;
397
+ continue;
398
+ }
399
+ const gt = str.indexOf(">", pos);
400
+ if (gt === -1) throw new ParseError("Unclosed opening tag");
401
+ let tagBody = str.slice(pos, gt).trim();
402
+ const selfClosing = tagBody.endsWith("/");
403
+ if (selfClosing) tagBody = tagBody.slice(0, -1).trim();
404
+ const spaceIdx = tagBody.search(/\s/u);
405
+ const tagName = spaceIdx === -1 ? tagBody : tagBody.slice(0, spaceIdx);
406
+ const attrs = {};
407
+ if (spaceIdx !== -1) {
408
+ const attrStr = tagBody.slice(spaceIdx).trim();
409
+ const attrRegex = /([a-zA-Z0-9_:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gu;
410
+ let m;
411
+ while ((m = attrRegex.exec(attrStr)) !== null) attrs[m[1]] = unescapeXml(m[2] ?? m[3] ?? m[4] ?? "");
412
+ }
413
+ const el = new XmlElement(tagName, attrs);
414
+ const top = stack.at(-1);
415
+ if (top) top.children.push(el);
416
+ else {
417
+ if (root) throw new ParseError("Multiple root elements found in XML document");
418
+ root = el;
419
+ }
420
+ if (!selfClosing) stack.push(el);
421
+ pos = gt + 1;
422
+ }
423
+ const lastUnclosed = stack.at(-1);
424
+ if (lastUnclosed) throw new ParseError(`Unclosed element: <${lastUnclosed.tag}>`);
425
+ if (!root) throw new ParseError("Empty XML document");
426
+ return root;
427
+ }
428
+ //#endregion
429
+ //#region vendor/file-bucket/lib/multipart.ts
430
+ function uploadError(name, message, cause) {
431
+ return Object.assign(new Error(message, { cause }), { name });
432
+ }
433
+ function delay(ms, signal) {
434
+ signal.throwIfAborted();
435
+ return new Promise((resolve, reject) => {
436
+ const abort = () => {
437
+ clearTimeout(timer);
438
+ reject(signal.reason);
439
+ };
440
+ const timer = setTimeout(() => {
441
+ signal.removeEventListener("abort", abort);
442
+ resolve();
443
+ }, ms);
444
+ signal.addEventListener("abort", abort, { once: true });
445
+ });
446
+ }
447
+ async function responseError(response) {
448
+ let code = "UploadFailed";
449
+ try {
450
+ const error = parseXml(await response.text());
451
+ code = error.findText("Code") || code;
452
+ if (code === "AccessDenied" && /expired/iu.test(error.findText("Message") || "")) code = "UploadExpired";
453
+ } catch {}
454
+ if (code === "RequestExpired" || code === "ExpiredToken") code = "UploadExpired";
455
+ return uploadError(code, `S3 upload request failed (${response.status}, ${code}).`);
456
+ }
457
+ /** Hydrate the wire descriptor without Node.js or AWS SDK runtime dependencies. */
458
+ function createMultipartUploadClient(descriptor) {
459
+ const controller = new AbortController();
460
+ let state = "ready";
461
+ let active;
462
+ let cleaned = false;
463
+ let cleaning;
464
+ function checkExpiry() {
465
+ if (Date.now() >= descriptor.expiresAt) throw uploadError("UploadExpired", "The multipart upload URLs have expired. Create a new handle.");
466
+ }
467
+ async function sendPart(url, body, signal) {
468
+ for (let attempt = 0; attempt < 3; attempt += 1) {
469
+ signal.throwIfAborted();
470
+ checkExpiry();
471
+ let response;
472
+ try {
473
+ response = await fetch(url, {
474
+ method: "PUT",
475
+ body,
476
+ signal: AbortSignal.any([signal, AbortSignal.timeout(12e4)])
477
+ });
478
+ } catch (error) {
479
+ signal.throwIfAborted();
480
+ if (attempt === 2) throw error;
481
+ await delay(200 * 2 ** attempt, signal);
482
+ continue;
483
+ }
484
+ if (response.ok) {
485
+ const etag = response.headers.get("etag");
486
+ await response.arrayBuffer();
487
+ if (!etag) throw uploadError("MissingETag", "S3 did not expose the part ETag. Check the bucket CORS configuration.");
488
+ return etag;
489
+ }
490
+ const error = await responseError(response);
491
+ if (attempt === 2 || response.status !== 429 && response.status < 500) throw error;
492
+ await delay(200 * 2 ** attempt, signal);
493
+ }
494
+ throw new Error("Part upload retry limit reached.");
495
+ }
496
+ async function cleanUp() {
497
+ if (cleaned) return;
498
+ if (cleaning) return cleaning;
499
+ cleaning = (async () => {
500
+ const response = await fetch(descriptor.abortUrl, {
501
+ method: "DELETE",
502
+ signal: AbortSignal.timeout(15e3)
503
+ });
504
+ if (!response.ok && response.status !== 404) throw await responseError(response);
505
+ await response.arrayBuffer();
506
+ cleaned = true;
507
+ })();
508
+ try {
509
+ await cleaning;
510
+ } finally {
511
+ cleaning = void 0;
512
+ }
513
+ }
514
+ async function execute(body, options) {
515
+ const signal = options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
516
+ try {
517
+ signal.throwIfAborted();
518
+ checkExpiry();
519
+ const file = body instanceof Blob ? body : new Blob([body instanceof Uint8Array ? new Uint8Array(body).buffer : body]);
520
+ if (file.size !== descriptor.size) throw new RangeError("File size does not match the multipart upload handle.");
521
+ let nextPart = 0;
522
+ let uploadedBytes = 0;
523
+ const etags = Array.from({ length: descriptor.partUrls.length }, () => "");
524
+ const worker = async () => {
525
+ try {
526
+ while (nextPart < descriptor.partUrls.length) {
527
+ signal.throwIfAborted();
528
+ const index = nextPart;
529
+ nextPart += 1;
530
+ const start = index * descriptor.partSize;
531
+ const part = file.slice(start, Math.min(file.size, start + descriptor.partSize));
532
+ etags[index] = await sendPart(descriptor.partUrls[index], part, signal);
533
+ uploadedBytes += part.size;
534
+ options.onProgress?.({
535
+ uploadedBytes,
536
+ totalBytes: descriptor.size
537
+ });
538
+ }
539
+ } catch (error) {
540
+ controller.abort(error);
541
+ throw error;
542
+ }
543
+ };
544
+ const failure = (await Promise.allSettled(Array.from({ length: Math.min(4, etags.length) }, worker))).find((result) => result.status === "rejected");
545
+ if (failure?.status === "rejected") throw failure.reason;
546
+ signal.throwIfAborted();
547
+ checkExpiry();
548
+ const xml = `<CompleteMultipartUpload>${etags.map((etag, index) => `<Part><PartNumber>${index + 1}</PartNumber><ETag>${escapeXml(etag)}</ETag></Part>`).join("")}</CompleteMultipartUpload>`;
549
+ let response;
550
+ let result;
551
+ try {
552
+ response = await fetch(descriptor.completeUrl, {
553
+ method: "POST",
554
+ body: xml,
555
+ headers: { "Content-Type": "application/xml" },
556
+ signal: AbortSignal.any([signal, AbortSignal.timeout(3e5)])
557
+ });
558
+ result = parseXml(await response.text());
559
+ } catch (error) {
560
+ state = "unknown";
561
+ throw uploadError("UploadCompletionUnknown", "The completion result is unknown. Check the object before retrying.", error);
562
+ }
563
+ if (result.localName() === "Error") throw uploadError(result.findText("Code") || "UploadFailed", result.findText("Message") || "S3 rejected multipart completion.");
564
+ if (!response.ok || result.localName() !== "CompleteMultipartUploadResult" || !result.findText("ETag")) {
565
+ state = "unknown";
566
+ throw uploadError("UploadCompletionUnknown", "S3 did not return a valid completion result. Check the object before retrying.");
567
+ }
568
+ state = "completed";
569
+ } catch (error) {
570
+ controller.abort(error);
571
+ if (state !== "unknown") {
572
+ state = signal.aborted ? "aborted" : "failed";
573
+ try {
574
+ await cleanUp();
575
+ } catch (cleanupError) {
576
+ throw uploadError(error instanceof Error ? error.name : "UploadFailed", "Upload failed and its session could not be cleaned up.", new AggregateError([error, cleanupError], "Upload and cleanup failed."));
577
+ }
578
+ }
579
+ throw error;
580
+ }
581
+ }
582
+ const isCompleted = () => state === "completed";
583
+ return {
584
+ async upload(body, options = {}) {
585
+ if (state !== "ready") throw uploadError("InvalidUploadState", "This multipart upload handle has already been used.");
586
+ state = "uploading";
587
+ active = execute(body, options);
588
+ await active;
589
+ },
590
+ async abort() {
591
+ if (isCompleted()) return;
592
+ controller.abort(new DOMException("Upload aborted.", "AbortError"));
593
+ if (active) try {
594
+ await active;
595
+ } catch {}
596
+ if (isCompleted()) return;
597
+ await cleanUp();
598
+ state = "aborted";
599
+ },
600
+ toJSON: () => descriptor
601
+ };
602
+ }
603
+ var DEFAULT_PART_SIZE = 8388608;
604
+ var MAX_PARTS = 1e4;
605
+ var MAX_PART_SIZE = 5368709120;
606
+ function multipartPartSize(size) {
607
+ if (size === void 0) return DEFAULT_PART_SIZE;
608
+ if (!Number.isSafeInteger(size) || size < 0) throw new RangeError("Upload size must be a nonnegative safe integer.");
609
+ const partSize = Math.max(DEFAULT_PART_SIZE, Math.ceil(size / MAX_PARTS));
610
+ if (partSize > MAX_PART_SIZE) throw new RangeError("Upload exceeds the multipart size limit.");
611
+ return partSize;
612
+ }
613
+ //#endregion
614
+ //#region vendor/file-bucket/lib/file-bucket.ts
615
+ /** Shared file bucket definition for server and browser code. */
616
+ var FileBucket = class {
617
+ name;
618
+ constructor(name) {
619
+ this.name = name;
620
+ }
621
+ };
622
+ //#endregion
623
+ //#region vendor/file-bucket/index.server.ts
624
+ var clients = /* @__PURE__ */ new WeakMap();
625
+ function getClient(bucket) {
626
+ const client = clients.get(bucket) ?? new S3Client(process.env.NODE_ENV === "production" ? { requestChecksumCalculation: "WHEN_REQUIRED" } : {
627
+ requestChecksumCalculation: "WHEN_REQUIRED",
628
+ endpoint: process.env.S3_EMULATOR_ENDPOINT || "http://127.0.0.1:4566",
629
+ region: "us-east-1",
630
+ forcePathStyle: true,
631
+ credentials: {
632
+ accessKeyId: "test",
633
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "test"
634
+ }
635
+ });
636
+ clients.set(bucket, client);
637
+ return client;
638
+ }
639
+ /**
640
+ * Delete an object through S3. Deleting a missing key succeeds.
641
+ *
642
+ * @param bucket - Shared definition whose ID is resolved to the configured physical bucket.
643
+ * @param path - Object key to delete.
644
+ * @returns A promise that resolves after S3 confirms deletion.
645
+ */
646
+ async function deleteFile(bucket, path) {
647
+ const bucketName = resolveBucketName(bucket);
648
+ await getClient(bucket).send(new DeleteObjectCommand({
649
+ Bucket: bucketName,
650
+ Key: path
651
+ }));
652
+ }
653
+ /**
654
+ * Delete objects in sequential batches of up to 1,000 keys.
655
+ *
656
+ * An empty list sends no S3 request. Deletion is not atomic: a rejected batch may
657
+ * already have deleted some objects, and later batches are not attempted.
658
+ *
659
+ * @param bucket - Shared definition whose ID is resolved to the configured physical bucket.
660
+ * @param paths - Object keys to delete.
661
+ * @returns A promise that resolves after all batches complete.
662
+ * @throws {AggregateError} If S3 reports per-object deletion errors.
663
+ */
664
+ async function deleteBatch(bucket, paths) {
665
+ if (paths.length === 0) return;
666
+ const bucketName = resolveBucketName(bucket);
667
+ const client = getClient(bucket);
668
+ const CHUNK_SIZE = 1e3;
669
+ for (let i = 0; i < paths.length; i += CHUNK_SIZE) {
670
+ const chunk = paths.slice(i, i + CHUNK_SIZE);
671
+ const result = await client.send(new DeleteObjectsCommand({
672
+ Bucket: bucketName,
673
+ Delete: {
674
+ Objects: chunk.map((Key) => ({ Key })),
675
+ Quiet: true
676
+ }
677
+ }));
678
+ if (result.Errors?.length) throw new AggregateError(result.Errors.map((error) => /* @__PURE__ */ new Error(`Failed to delete ${error.Key}: ${error.Code} ${error.Message ?? ""}`)), "Some files could not be deleted.");
679
+ }
680
+ }
681
+ /**
682
+ * Sign a GET URL on the server without reading the object or checking that it exists.
683
+ *
684
+ * @param bucket - Shared definition whose ID is resolved to the configured physical bucket.
685
+ * @param path - Object key to access.
686
+ * @param options - URL lifetime in seconds (default 3600) and optional response content disposition.
687
+ * @returns A signed URL; requesting it performs the download.
688
+ */
689
+ function getUrl(bucket, path, options) {
690
+ const bucketName = resolveBucketName(bucket);
691
+ return getSignedUrl(getClient(bucket), new GetObjectCommand({
692
+ Bucket: bucketName,
693
+ Key: path,
694
+ ResponseContentDisposition: options?.contentDisposition
695
+ }), { expiresIn: options?.expiresIn ?? 3600 });
696
+ }
697
+ /**
698
+ * Sign a URL for one PUT without uploading bytes or creating a multipart session.
699
+ *
700
+ * Send the raw body with the content-type and cache-control headers used when
701
+ * signing. Metadata is carried in the signed query. Uploading replaces an existing key.
702
+ *
703
+ * @param bucket - Shared definition whose ID is resolved to the configured physical bucket.
704
+ * @param path - Destination object key.
705
+ * @param options - Object metadata, content headers, and URL lifetime in seconds (default 3600).
706
+ * @returns A signed URL for a single PUT.
707
+ */
708
+ function putUrl(bucket, path, options) {
709
+ const bucketName = resolveBucketName(bucket);
710
+ return getSignedUrl(getClient(bucket), new PutObjectCommand({
711
+ Bucket: bucketName,
712
+ Key: path,
713
+ ContentType: options?.contentType,
714
+ Metadata: options?.metadata,
715
+ CacheControl: options?.cacheControl
716
+ }), { expiresIn: options?.expiresIn ?? 3600 });
717
+ }
718
+ /**
719
+ * Create an S3 multipart session and sign its part, completion, and abort URLs.
720
+ *
721
+ * The returned handle uploads once, supports cancellation and progress, and can
722
+ * abort an unused session. Serialize with `toJSON()` and hydrate in the browser
723
+ * with `createMultipartUploadClient`. Creation sends no file bytes.
724
+ *
725
+ * @param bucket - Shared definition whose ID is resolved to the configured physical bucket.
726
+ * @param path - Destination object key.
727
+ * @param options - Expected byte size, object metadata, content headers, and URL lifetime (default 3600 seconds).
728
+ * @returns A multipart handle tied to the newly created session.
729
+ * @throws {RangeError} For an unsupported size or URL lifetime.
730
+ */
731
+ async function createMultipartUploadHandle(bucket, path, options) {
732
+ const bucketName = resolveBucketName(bucket);
733
+ const partSize = multipartPartSize(options.size);
734
+ const expiresIn = options.expiresIn ?? 3600;
735
+ if (!Number.isInteger(expiresIn) || expiresIn < 1 || expiresIn > 604800) throw new RangeError("URL lifetime must be between 1 and 604800 seconds.");
736
+ const client = getClient(bucket);
737
+ const target = {
738
+ Bucket: bucketName,
739
+ Key: path
740
+ };
741
+ const { UploadId } = await client.send(new CreateMultipartUploadCommand({
742
+ ...target,
743
+ ContentType: options.contentType,
744
+ Metadata: options.metadata,
745
+ CacheControl: options.cacheControl
746
+ }));
747
+ if (!UploadId) throw new Error("S3 did not return an upload ID.");
748
+ const session = {
749
+ ...target,
750
+ UploadId
751
+ };
752
+ const expiresAt = Math.floor(Date.now() / 1e3) * 1e3 + expiresIn * 1e3;
753
+ try {
754
+ const partUrls = [];
755
+ const count = Math.max(1, Math.ceil(options.size / partSize));
756
+ for (let partNumber = 1; partNumber <= count; partNumber += 1) partUrls.push(await getSignedUrl(client, new UploadPartCommand({
757
+ ...session,
758
+ PartNumber: partNumber
759
+ }), { expiresIn }));
760
+ const completeUrl = await getSignedUrl(client, new CompleteMultipartUploadCommand({
761
+ ...session,
762
+ MultipartUpload: { Parts: [] }
763
+ }), {
764
+ expiresIn,
765
+ unsignableHeaders: /* @__PURE__ */ new Set(["content-length"])
766
+ });
767
+ const abortUrl = await getSignedUrl(client, new AbortMultipartUploadCommand(session), { expiresIn });
768
+ return createMultipartUploadClient({
769
+ __blocks: "file-bucket/multipart-upload",
770
+ uploadId: UploadId,
771
+ size: options.size,
772
+ partSize,
773
+ expiresAt,
774
+ partUrls,
775
+ completeUrl,
776
+ abortUrl
777
+ });
778
+ } catch (error) {
779
+ try {
780
+ await client.send(new AbortMultipartUploadCommand(session));
781
+ } catch (cleanupError) {
782
+ throw new AggregateError([error, cleanupError], "Failed to create and clean up multipart upload.", { cause: cleanupError });
783
+ }
784
+ throw error;
785
+ }
786
+ }
787
+ function resolveBucketName(bucket) {
788
+ const hash = createHash("sha256").update(bucket.name).digest("hex").slice(0, 8);
789
+ return process.env[`FILE_BUCKET_${hash}`] || bucket.name;
790
+ }
791
+ //#endregion
792
+ //#region vendor/file-bucket/lib/protocol.ts
793
+ var MAX_KEY_BYTES = 1024;
794
+ var MAX_METADATA_ENTRIES = 50;
795
+ var MAX_METADATA_KEY_LENGTH = 128;
796
+ var MAX_METADATA_VALUE_LENGTH = 2048;
797
+ function isRecord(value) {
798
+ return typeof value === "object" && value !== null && !Array.isArray(value);
799
+ }
800
+ function textByteLength(value) {
801
+ return new TextEncoder().encode(value).length;
802
+ }
803
+ function isKey(value) {
804
+ return typeof value === "string" && value.length > 0 && textByteLength(value) <= MAX_KEY_BYTES;
805
+ }
806
+ function isOptionalText(value, maxLen = 1024) {
807
+ return value === void 0 || typeof value === "string" && textByteLength(value) <= maxLen;
808
+ }
809
+ function isStringRecord(value) {
810
+ if (!isRecord(value)) return false;
811
+ const entries = Object.entries(value);
812
+ if (entries.length > MAX_METADATA_ENTRIES) return false;
813
+ return entries.every(([k, v]) => typeof v === "string" && k.length <= MAX_METADATA_KEY_LENGTH && v.length <= MAX_METADATA_VALUE_LENGTH);
814
+ }
815
+ function isSize(value) {
816
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
817
+ }
818
+ function isMetadata(input) {
819
+ return isOptionalText(input.contentType, 512) && isOptionalText(input.cacheControl, 512) && (input.metadata === void 0 || isStringRecord(input.metadata));
820
+ }
821
+ function isSigningOptions(input) {
822
+ return isMetadata(input) && isOptionalText(input.contentDisposition, 512) && (input.expiresIn === void 0 || isSize(input.expiresIn) && input.expiresIn >= 1 && input.expiresIn <= 604800);
823
+ }
824
+ function deepFreeze(object) {
825
+ if (object === null || typeof object !== "object") return object;
826
+ Object.freeze(object);
827
+ for (const key of Object.keys(object)) {
828
+ const val = object[key];
829
+ if (val !== null && typeof val === "object" && !Object.isFrozen(val)) deepFreeze(val);
830
+ }
831
+ return object;
832
+ }
833
+ function isRequest(input, maxBatchKeys = 1e3) {
834
+ if (!isKey(input.bucketId)) return false;
835
+ if (input.operation === "deleteBatch") return Array.isArray(input.paths) && input.paths.length > 0 && input.paths.length <= maxBatchKeys && input.paths.every(isKey);
836
+ if (!isKey(input.path)) return false;
837
+ switch (input.operation) {
838
+ case "get":
839
+ case "delete": return true;
840
+ case "getUrl":
841
+ case "putUrl": return isSigningOptions(input);
842
+ case "put": return isSize(input.size) && isMetadata(input);
843
+ default: return false;
844
+ }
845
+ }
846
+ function parseFileBucketRequest(value, options) {
847
+ if (!isRecord(value)) throw new Error("Invalid file request");
848
+ const input = {
849
+ ...value,
850
+ operation: value.operation === void 0 ? "put" : value.operation
851
+ };
852
+ if (!isRequest(input, options?.maxBatchKeys)) throw new Error("Invalid file request");
853
+ return deepFreeze(input);
854
+ }
855
+ //#endregion
856
+ //#region vendor/file-bucket/react-router.server.ts
857
+ var MULTIPART_THRESHOLD = 8388608;
858
+ var MAX_REQUEST_BYTES = 65536;
859
+ var MAX_BATCH_KEYS = 1e3;
860
+ async function readLimitedJson(request, maxBytes) {
861
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new RangeError("maxBytes must be a positive safe integer.");
862
+ const reader = request.body?.getReader();
863
+ if (!reader) throw new Response("Invalid JSON", { status: 400 });
864
+ const chunks = [];
865
+ let length = 0;
866
+ try {
867
+ while (true) {
868
+ const { done, value } = await reader.read();
869
+ if (done) break;
870
+ length += value.byteLength;
871
+ if (length > maxBytes) {
872
+ try {
873
+ await reader.cancel();
874
+ } catch {}
875
+ throw new Response("Request too large", { status: 413 });
876
+ }
877
+ chunks.push(value);
878
+ }
879
+ } finally {
880
+ reader.releaseLock();
881
+ }
882
+ const bytes = new Uint8Array(length);
883
+ let offset = 0;
884
+ for (const chunk of chunks) {
885
+ bytes.set(chunk, offset);
886
+ offset += chunk.byteLength;
887
+ }
888
+ try {
889
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
890
+ } catch {
891
+ throw new Response("Invalid JSON", { status: 400 });
892
+ }
893
+ }
894
+ async function uploadGrant(bucket, input) {
895
+ const options = {
896
+ contentType: input.contentType,
897
+ cacheControl: input.cacheControl,
898
+ metadata: input.metadata
899
+ };
900
+ if (input.size >= MULTIPART_THRESHOLD) return {
901
+ type: "multipart",
902
+ descriptor: (await createMultipartUploadHandle(bucket, input.path, {
903
+ ...options,
904
+ size: input.size
905
+ })).toJSON()
906
+ };
907
+ const url = await putUrl(bucket, input.path, options);
908
+ const headers = {};
909
+ if (options.contentType) headers["Content-Type"] = options.contentType;
910
+ if (options.cacheControl) headers["Cache-Control"] = options.cacheControl;
911
+ return {
912
+ type: "single",
913
+ url,
914
+ headers
915
+ };
916
+ }
917
+ async function dispatch(bucket, input) {
918
+ switch (input.operation) {
919
+ case "delete":
920
+ await deleteFile(bucket, input.path);
921
+ return;
922
+ case "deleteBatch":
923
+ await deleteBatch(bucket, input.paths);
924
+ return;
925
+ case "get": return { url: await getUrl(bucket, input.path, { contentDisposition: "attachment" }) };
926
+ case "getUrl": return { url: await getUrl(bucket, input.path, {
927
+ expiresIn: input.expiresIn,
928
+ contentDisposition: input.contentDisposition
929
+ }) };
930
+ case "putUrl": return { url: await putUrl(bucket, input.path, {
931
+ expiresIn: input.expiresIn,
932
+ contentType: input.contentType,
933
+ metadata: input.metadata,
934
+ cacheControl: input.cacheControl
935
+ }) };
936
+ case "put": return uploadGrant(bucket, input);
937
+ default: throw new Error("Unsupported file operation");
938
+ }
939
+ }
940
+ //#endregion
941
+ //#region app/routes/files.ts
942
+ var files_exports = /* @__PURE__ */ __exportAll({ action: () => action });
943
+ var action = async ({ request }) => {
944
+ if (request.method !== "POST") return new Response("Method not allowed", {
945
+ status: 405,
946
+ headers: { Allow: "POST" }
947
+ });
948
+ if (request.headers.get("content-type")?.toLowerCase().split(";")[0]?.trim() !== "application/json") return new Response("Unsupported Media Type", { status: 415 });
949
+ const contentLengthHeader = request.headers.get("content-length");
950
+ if (contentLengthHeader !== null) {
951
+ const declaredLength = Number(contentLengthHeader);
952
+ if (!Number.isNaN(declaredLength) && declaredLength > 65536) return new Response("Request too large", { status: 413 });
953
+ }
954
+ let value;
955
+ try {
956
+ value = await readLimitedJson(request, MAX_REQUEST_BYTES);
957
+ } catch (error) {
958
+ if (error instanceof Response) return error;
959
+ return new Response("Invalid JSON", { status: 400 });
960
+ }
961
+ let input;
962
+ try {
963
+ input = parseFileBucketRequest(value, { maxBatchKeys: MAX_BATCH_KEYS });
964
+ } catch {
965
+ return new Response("Invalid file request", { status: 400 });
966
+ }
967
+ const result = await dispatch(new FileBucket(input.bucketId), input);
968
+ if (result === void 0) return new Response(null, {
969
+ status: 204,
970
+ headers: { "Cache-Control": "no-store" }
971
+ });
972
+ return Response.json(result, { headers: { "Cache-Control": "private, no-store" } });
973
+ };
974
+ //#endregion
975
+ //#region \0virtual:react-router/server-manifest
976
+ var server_manifest_default = {
977
+ "entry": {
978
+ "module": "/assets/entry.client-Bc58m2-m.js",
979
+ "imports": ["/assets/jsx-runtime-eLCqVKHJ.js", "/assets/errorBoundaries-CQxPIuVU.js"],
980
+ "css": []
981
+ },
982
+ "routes": {
983
+ "root": {
984
+ "id": "root",
985
+ "parentId": void 0,
986
+ "path": "",
987
+ "index": void 0,
988
+ "caseSensitive": void 0,
989
+ "hasAction": false,
990
+ "hasLoader": false,
991
+ "hasClientAction": false,
992
+ "hasClientLoader": false,
993
+ "hasClientMiddleware": false,
994
+ "hasDefaultExport": true,
995
+ "hasErrorBoundary": true,
996
+ "module": "/assets/root-C1tmjS9o.js",
997
+ "imports": ["/assets/jsx-runtime-eLCqVKHJ.js", "/assets/errorBoundaries-CQxPIuVU.js"],
998
+ "css": ["/assets/root-BeNS6njP.css"],
999
+ "clientActionModule": void 0,
1000
+ "clientLoaderModule": void 0,
1001
+ "clientMiddlewareModule": void 0,
1002
+ "hydrateFallbackModule": void 0
1003
+ },
1004
+ "routes/home": {
1005
+ "id": "routes/home",
1006
+ "parentId": "root",
1007
+ "path": void 0,
1008
+ "index": true,
1009
+ "caseSensitive": void 0,
1010
+ "hasAction": false,
1011
+ "hasLoader": false,
1012
+ "hasClientAction": false,
1013
+ "hasClientLoader": false,
1014
+ "hasClientMiddleware": false,
1015
+ "hasDefaultExport": true,
1016
+ "hasErrorBoundary": false,
1017
+ "module": "/assets/home-CH2INc5e.js",
1018
+ "imports": ["/assets/jsx-runtime-eLCqVKHJ.js"],
1019
+ "css": [],
1020
+ "clientActionModule": void 0,
1021
+ "clientLoaderModule": void 0,
1022
+ "clientMiddlewareModule": void 0,
1023
+ "hydrateFallbackModule": void 0
1024
+ },
1025
+ "routes/files": {
1026
+ "id": "routes/files",
1027
+ "parentId": "root",
1028
+ "path": "resources/files",
1029
+ "index": void 0,
1030
+ "caseSensitive": void 0,
1031
+ "hasAction": true,
1032
+ "hasLoader": false,
1033
+ "hasClientAction": false,
1034
+ "hasClientLoader": false,
1035
+ "hasClientMiddleware": false,
1036
+ "hasDefaultExport": false,
1037
+ "hasErrorBoundary": false,
1038
+ "module": "/assets/files-BvRk9kiK.js",
1039
+ "imports": [],
1040
+ "css": [],
1041
+ "clientActionModule": void 0,
1042
+ "clientLoaderModule": void 0,
1043
+ "clientMiddlewareModule": void 0,
1044
+ "hydrateFallbackModule": void 0
1045
+ }
1046
+ },
1047
+ "url": "/assets/manifest-2d65dd0f.js",
1048
+ "version": "2d65dd0f",
1049
+ "sri": void 0
1050
+ };
1051
+ //#endregion
1052
+ //#region \0virtual:react-router/server-build
1053
+ var assetsBuildDirectory = "build/client";
1054
+ var basename = "/";
1055
+ var future = {
1056
+ "unstable_enableNodeReadableStream": false,
1057
+ "unstable_optimizeDeps": false
1058
+ };
1059
+ var ssr = true;
1060
+ var isSpaMode = false;
1061
+ var prerender = [];
1062
+ var routeDiscovery = {
1063
+ "mode": "lazy",
1064
+ "manifestPath": "/__manifest"
1065
+ };
1066
+ var publicPath = "/";
1067
+ var entry = { module: entry_server_node_exports };
1068
+ var routes = {
1069
+ "root": {
1070
+ id: "root",
1071
+ parentId: void 0,
1072
+ path: "",
1073
+ index: void 0,
1074
+ caseSensitive: void 0,
1075
+ module: root_exports
1076
+ },
1077
+ "routes/home": {
1078
+ id: "routes/home",
1079
+ parentId: "root",
1080
+ path: void 0,
1081
+ index: true,
1082
+ caseSensitive: void 0,
1083
+ module: home_exports
1084
+ },
1085
+ "routes/files": {
1086
+ id: "routes/files",
1087
+ parentId: "root",
1088
+ path: "resources/files",
1089
+ index: void 0,
1090
+ caseSensitive: void 0,
1091
+ module: files_exports
1092
+ }
1093
+ };
1094
+ var allowedActionOrigins = false;
1095
+ //#endregion
1096
+ export { allowedActionOrigins, server_manifest_default as assets, assetsBuildDirectory, basename, entry, future, isSpaMode, prerender, publicPath, routeDiscovery, routes, ssr };