flingit 0.0.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.
Files changed (161) hide show
  1. package/README.md +84 -0
  2. package/dist/cli/commands/db.d.ts +9 -0
  3. package/dist/cli/commands/db.d.ts.map +1 -0
  4. package/dist/cli/commands/db.js +215 -0
  5. package/dist/cli/commands/db.js.map +1 -0
  6. package/dist/cli/commands/dev.d.ts +6 -0
  7. package/dist/cli/commands/dev.d.ts.map +1 -0
  8. package/dist/cli/commands/dev.js +145 -0
  9. package/dist/cli/commands/dev.js.map +1 -0
  10. package/dist/cli/commands/feedback.d.ts +6 -0
  11. package/dist/cli/commands/feedback.d.ts.map +1 -0
  12. package/dist/cli/commands/feedback.js +116 -0
  13. package/dist/cli/commands/feedback.js.map +1 -0
  14. package/dist/cli/commands/init.d.ts +6 -0
  15. package/dist/cli/commands/init.d.ts.map +1 -0
  16. package/dist/cli/commands/init.js +161 -0
  17. package/dist/cli/commands/init.js.map +1 -0
  18. package/dist/cli/commands/launch.d.ts +12 -0
  19. package/dist/cli/commands/launch.d.ts.map +1 -0
  20. package/dist/cli/commands/launch.js +176 -0
  21. package/dist/cli/commands/launch.js.map +1 -0
  22. package/dist/cli/commands/login.d.ts +6 -0
  23. package/dist/cli/commands/login.d.ts.map +1 -0
  24. package/dist/cli/commands/login.js +90 -0
  25. package/dist/cli/commands/login.js.map +1 -0
  26. package/dist/cli/commands/logout.d.ts +6 -0
  27. package/dist/cli/commands/logout.d.ts.map +1 -0
  28. package/dist/cli/commands/logout.js +32 -0
  29. package/dist/cli/commands/logout.js.map +1 -0
  30. package/dist/cli/commands/logs.d.ts +12 -0
  31. package/dist/cli/commands/logs.d.ts.map +1 -0
  32. package/dist/cli/commands/logs.js +261 -0
  33. package/dist/cli/commands/logs.js.map +1 -0
  34. package/dist/cli/commands/onboard.d.ts +22 -0
  35. package/dist/cli/commands/onboard.d.ts.map +1 -0
  36. package/dist/cli/commands/onboard.js +244 -0
  37. package/dist/cli/commands/onboard.js.map +1 -0
  38. package/dist/cli/commands/project.d.ts +6 -0
  39. package/dist/cli/commands/project.d.ts.map +1 -0
  40. package/dist/cli/commands/project.js +142 -0
  41. package/dist/cli/commands/project.js.map +1 -0
  42. package/dist/cli/commands/push.d.ts +6 -0
  43. package/dist/cli/commands/push.d.ts.map +1 -0
  44. package/dist/cli/commands/push.js +351 -0
  45. package/dist/cli/commands/push.js.map +1 -0
  46. package/dist/cli/commands/register.d.ts +6 -0
  47. package/dist/cli/commands/register.d.ts.map +1 -0
  48. package/dist/cli/commands/register.js +115 -0
  49. package/dist/cli/commands/register.js.map +1 -0
  50. package/dist/cli/commands/secret.d.ts +11 -0
  51. package/dist/cli/commands/secret.d.ts.map +1 -0
  52. package/dist/cli/commands/secret.js +124 -0
  53. package/dist/cli/commands/secret.js.map +1 -0
  54. package/dist/cli/commands/whoami.d.ts +6 -0
  55. package/dist/cli/commands/whoami.d.ts.map +1 -0
  56. package/dist/cli/commands/whoami.js +54 -0
  57. package/dist/cli/commands/whoami.js.map +1 -0
  58. package/dist/cli/deploy/assets.d.ts +73 -0
  59. package/dist/cli/deploy/assets.d.ts.map +1 -0
  60. package/dist/cli/deploy/assets.js +167 -0
  61. package/dist/cli/deploy/assets.js.map +1 -0
  62. package/dist/cli/deploy/base64-stream.d.ts +16 -0
  63. package/dist/cli/deploy/base64-stream.d.ts.map +1 -0
  64. package/dist/cli/deploy/base64-stream.js +44 -0
  65. package/dist/cli/deploy/base64-stream.js.map +1 -0
  66. package/dist/cli/deploy/bucket-stream.d.ts +18 -0
  67. package/dist/cli/deploy/bucket-stream.d.ts.map +1 -0
  68. package/dist/cli/deploy/bucket-stream.js +63 -0
  69. package/dist/cli/deploy/bucket-stream.js.map +1 -0
  70. package/dist/cli/deploy/bundler.d.ts +22 -0
  71. package/dist/cli/deploy/bundler.d.ts.map +1 -0
  72. package/dist/cli/deploy/bundler.js +131 -0
  73. package/dist/cli/deploy/bundler.js.map +1 -0
  74. package/dist/cli/deploy/worker-entry.d.ts +14 -0
  75. package/dist/cli/deploy/worker-entry.d.ts.map +1 -0
  76. package/dist/cli/deploy/worker-entry.js +60 -0
  77. package/dist/cli/deploy/worker-entry.js.map +1 -0
  78. package/dist/cli/deploy/wrangler-config.d.ts +20 -0
  79. package/dist/cli/deploy/wrangler-config.d.ts.map +1 -0
  80. package/dist/cli/deploy/wrangler-config.js +54 -0
  81. package/dist/cli/deploy/wrangler-config.js.map +1 -0
  82. package/dist/cli/index.d.ts +10 -0
  83. package/dist/cli/index.d.ts.map +1 -0
  84. package/dist/cli/index.js +72 -0
  85. package/dist/cli/index.js.map +1 -0
  86. package/dist/cli/utils/config.d.ts +39 -0
  87. package/dist/cli/utils/config.d.ts.map +1 -0
  88. package/dist/cli/utils/config.js +105 -0
  89. package/dist/cli/utils/config.js.map +1 -0
  90. package/dist/cli/utils/duration.d.ts +21 -0
  91. package/dist/cli/utils/duration.d.ts.map +1 -0
  92. package/dist/cli/utils/duration.js +44 -0
  93. package/dist/cli/utils/duration.js.map +1 -0
  94. package/dist/cli/utils/environment.d.ts +17 -0
  95. package/dist/cli/utils/environment.d.ts.map +1 -0
  96. package/dist/cli/utils/environment.js +27 -0
  97. package/dist/cli/utils/environment.js.map +1 -0
  98. package/dist/cli/utils/project.d.ts +24 -0
  99. package/dist/cli/utils/project.d.ts.map +1 -0
  100. package/dist/cli/utils/project.js +47 -0
  101. package/dist/cli/utils/project.js.map +1 -0
  102. package/dist/cli/utils/registry.d.ts +34 -0
  103. package/dist/cli/utils/registry.d.ts.map +1 -0
  104. package/dist/cli/utils/registry.js +94 -0
  105. package/dist/cli/utils/registry.js.map +1 -0
  106. package/dist/cli/utils/token.d.ts +12 -0
  107. package/dist/cli/utils/token.d.ts.map +1 -0
  108. package/dist/cli/utils/token.js +27 -0
  109. package/dist/cli/utils/token.js.map +1 -0
  110. package/dist/index.d.ts +12 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +12 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/runtime/cron.d.ts +50 -0
  115. package/dist/runtime/cron.d.ts.map +1 -0
  116. package/dist/runtime/cron.js +80 -0
  117. package/dist/runtime/cron.js.map +1 -0
  118. package/dist/runtime/db.d.ts +93 -0
  119. package/dist/runtime/db.d.ts.map +1 -0
  120. package/dist/runtime/db.js +137 -0
  121. package/dist/runtime/db.js.map +1 -0
  122. package/dist/runtime/http.d.ts +33 -0
  123. package/dist/runtime/http.d.ts.map +1 -0
  124. package/dist/runtime/http.js +36 -0
  125. package/dist/runtime/http.js.map +1 -0
  126. package/dist/runtime/log.d.ts +90 -0
  127. package/dist/runtime/log.d.ts.map +1 -0
  128. package/dist/runtime/log.js +211 -0
  129. package/dist/runtime/log.js.map +1 -0
  130. package/dist/runtime/migrate.d.ts +54 -0
  131. package/dist/runtime/migrate.d.ts.map +1 -0
  132. package/dist/runtime/migrate.js +130 -0
  133. package/dist/runtime/migrate.js.map +1 -0
  134. package/dist/runtime/secrets.d.ts +36 -0
  135. package/dist/runtime/secrets.d.ts.map +1 -0
  136. package/dist/runtime/secrets.js +75 -0
  137. package/dist/runtime/secrets.js.map +1 -0
  138. package/dist/worker-runtime/index.d.ts +79 -0
  139. package/dist/worker-runtime/index.d.ts.map +1 -0
  140. package/dist/worker-runtime/index.js +203 -0
  141. package/dist/worker-runtime/index.js.map +1 -0
  142. package/package.json +81 -0
  143. package/templates/default/.nvmrc +1 -0
  144. package/templates/default/CLAUDE.md +197 -0
  145. package/templates/default/index.html +13 -0
  146. package/templates/default/package.json +25 -0
  147. package/templates/default/public/vite.svg +1 -0
  148. package/templates/default/skills/fling/API.md +335 -0
  149. package/templates/default/skills/fling/EXAMPLES.md +204 -0
  150. package/templates/default/skills/fling/FEEDBACK.md +73 -0
  151. package/templates/default/skills/fling/SKILL.md +159 -0
  152. package/templates/default/src/react-app/App.css +34 -0
  153. package/templates/default/src/react-app/App.tsx +27 -0
  154. package/templates/default/src/react-app/index.css +15 -0
  155. package/templates/default/src/react-app/main.tsx +10 -0
  156. package/templates/default/src/react-app/vite-env.d.ts +1 -0
  157. package/templates/default/src/worker/index.ts +27 -0
  158. package/templates/default/tsconfig.app.json +22 -0
  159. package/templates/default/tsconfig.json +7 -0
  160. package/templates/default/tsconfig.worker.json +11 -0
  161. package/templates/default/vite.config.ts +21 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/cli/commands/register.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * fling secret - Secrets management commands
3
+ *
4
+ * Behavior:
5
+ * - Secrets are always stored locally in .fling/secrets
6
+ * - Use `fling push` to deploy secrets to production
7
+ */
8
+ import { Command } from "commander";
9
+ export declare function loadLocalSecrets(): Map<string, string>;
10
+ export declare const secretCommand: Command;
11
+ //# sourceMappingURL=secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/secret.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAoBtD;AAoBD,eAAO,MAAM,aAAa,SAcxB,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * fling secret - Secrets management commands
3
+ *
4
+ * Behavior:
5
+ * - Secrets are always stored locally in .fling/secrets
6
+ * - Use `fling push` to deploy secrets to production
7
+ */
8
+ import { Command } from "commander";
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
10
+ import { join, dirname } from "node:path";
11
+ const SECRETS_PATH = join(process.cwd(), ".fling", "secrets");
12
+ // ============================================================================
13
+ // Local secrets management
14
+ // ============================================================================
15
+ export function loadLocalSecrets() {
16
+ const secrets = new Map();
17
+ if (existsSync(SECRETS_PATH)) {
18
+ const content = readFileSync(SECRETS_PATH, "utf-8");
19
+ for (const line of content.split("\n")) {
20
+ const trimmed = line.trim();
21
+ if (!trimmed || trimmed.startsWith("#")) {
22
+ continue;
23
+ }
24
+ const eqIndex = trimmed.indexOf("=");
25
+ if (eqIndex > 0) {
26
+ const key = trimmed.slice(0, eqIndex);
27
+ const value = trimmed.slice(eqIndex + 1);
28
+ secrets.set(key, value);
29
+ }
30
+ }
31
+ }
32
+ return secrets;
33
+ }
34
+ function saveLocalSecrets(secrets) {
35
+ const dir = dirname(SECRETS_PATH);
36
+ if (!existsSync(dir)) {
37
+ mkdirSync(dir, { recursive: true });
38
+ }
39
+ const lines = ["# Fling secrets", "# Format: KEY=value", ""];
40
+ for (const [key, value] of secrets) {
41
+ lines.push(`${key}=${value}`);
42
+ }
43
+ writeFileSync(SECRETS_PATH, lines.join("\n") + "\n", { mode: 0o600 });
44
+ }
45
+ // ============================================================================
46
+ // Commands
47
+ // ============================================================================
48
+ export const secretCommand = new Command("secret")
49
+ .description("Manage secrets for your project")
50
+ .addHelpText("after", `
51
+ Environment:
52
+ Secrets are stored locally in .fling/secrets (gitignored).
53
+ Use 'fling push' to deploy secrets to production.
54
+
55
+ Secret naming:
56
+ Names must be UPPER_SNAKE_CASE (e.g., GITHUB_TOKEN, API_KEY, DB_PASSWORD).
57
+
58
+ Examples:
59
+ fling secret list List secret names
60
+ fling secret set API_KEY=xxx Set a secret
61
+ fling secret remove API_KEY Remove a secret
62
+ `);
63
+ secretCommand
64
+ .command("set <keyvalue>")
65
+ .description("Set a secret (must be UPPER_SNAKE_CASE=value format)")
66
+ .addHelpText("after", `
67
+ Examples:
68
+ fling secret set GITHUB_TOKEN=ghp_xxx
69
+ fling secret set API_KEY=secret123
70
+ `)
71
+ .action((keyvalue) => {
72
+ const eqIndex = keyvalue.indexOf("=");
73
+ if (eqIndex <= 0) {
74
+ console.error("Error: Invalid format. Use: fling secret set KEY=value");
75
+ process.exit(1);
76
+ }
77
+ const key = keyvalue.slice(0, eqIndex);
78
+ const value = keyvalue.slice(eqIndex + 1);
79
+ if (!key.match(/^[A-Z_][A-Z0-9_]*$/)) {
80
+ console.error("Error: Secret name must be uppercase with underscores (e.g., GITHUB_TOKEN)");
81
+ process.exit(1);
82
+ }
83
+ const secrets = loadLocalSecrets();
84
+ const exists = secrets.has(key);
85
+ secrets.set(key, value);
86
+ saveLocalSecrets(secrets);
87
+ console.log(exists ? `Updated secret: ${key}` : `Set secret: ${key}`);
88
+ });
89
+ secretCommand
90
+ .command("list")
91
+ .description("List all secret names (values are never shown)")
92
+ .addHelpText("after", `
93
+ Examples:
94
+ fling secret list List secrets
95
+ `)
96
+ .action(() => {
97
+ const secrets = loadLocalSecrets();
98
+ if (secrets.size === 0) {
99
+ console.log("No secrets configured.");
100
+ return;
101
+ }
102
+ console.log("Configured secrets:");
103
+ for (const key of secrets.keys()) {
104
+ console.log(` ${key}`);
105
+ }
106
+ });
107
+ secretCommand
108
+ .command("remove <key>")
109
+ .description("Delete a secret by name")
110
+ .addHelpText("after", `
111
+ Examples:
112
+ fling secret remove API_KEY Remove a secret
113
+ `)
114
+ .action((key) => {
115
+ const secrets = loadLocalSecrets();
116
+ if (!secrets.has(key)) {
117
+ console.error(`Error: Secret "${key}" not found.`);
118
+ process.exit(1);
119
+ }
120
+ secrets.delete(key);
121
+ saveLocalSecrets(secrets);
122
+ console.log(`Removed secret: ${key}`);
123
+ });
124
+ //# sourceMappingURL=secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.js","sourceRoot":"","sources":["../../../src/cli/commands/secret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * fling whoami - Show current user and project info
3
+ */
4
+ import { Command } from "commander";
5
+ export declare const whoamiCommand: Command;
6
+ //# sourceMappingURL=whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,eAAO,MAAM,aAAa,SAoDtB,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * fling whoami - Show current user and project info
3
+ */
4
+ import { Command } from "commander";
5
+ import { isLoggedIn, platformFetch } from "../utils/config.js";
6
+ export const whoamiCommand = new Command("whoami")
7
+ .description("Display current logged-in user and project information")
8
+ .addHelpText("after", `
9
+ Displays:
10
+ - User name and email
11
+ - Project slug and public URL
12
+ - Current deployment version
13
+ - Last deployment timestamp
14
+
15
+ Requires login (run 'fling login' first).
16
+ `)
17
+ .action(async () => {
18
+ if (!isLoggedIn()) {
19
+ console.log("Not logged in. Run 'fling login' first.");
20
+ process.exit(1);
21
+ }
22
+ try {
23
+ const response = await platformFetch("/me");
24
+ if (!response.ok) {
25
+ const data = await response.json();
26
+ console.error(`Error: ${data.error ?? "Unknown error"}`);
27
+ process.exit(1);
28
+ }
29
+ const data = await response.json();
30
+ console.log(`User: ${data.user.name} (${data.user.email})`);
31
+ // Display first project (currently users have one project with id 'default')
32
+ const project = data.projects[0];
33
+ if (!project) {
34
+ console.log("No projects found.");
35
+ }
36
+ else {
37
+ console.log(`Project: ${project.slug}`);
38
+ console.log(`URL: ${project.url}`);
39
+ console.log(`Version: ${project.current_version}`);
40
+ if (project.last_deployed_at) {
41
+ const deployedAt = new Date(project.last_deployed_at);
42
+ console.log(`Last deployed: ${deployedAt.toLocaleString()}`);
43
+ }
44
+ else {
45
+ console.log("Last deployed: never");
46
+ }
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error(`Error: ${error instanceof Error ? error.message : "Network error"}`);
51
+ process.exit(1);
52
+ }
53
+ });
54
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAoB/D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,wDAAwD,CAAC;KACrE,WAAW,CAAC,OAAO,EAAE;;;;;;;;CAQvB,CAAC;KACC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QAE5C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAwB,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC,CAAC;YACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAgB,CAAC;QAEjD,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QAE5D,6EAA6E;QAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;YAEnD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBAC7B,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YAC/D,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACrE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Asset collection and hashing for static file uploads.
3
+ * Scans dist/client/ directory and prepares assets for Cloudflare upload.
4
+ */
5
+ /**
6
+ * Get MIME type for a file path based on extension.
7
+ */
8
+ export declare function getMimeType(filePath: string): string;
9
+ /**
10
+ * Represents a single static asset file.
11
+ */
12
+ export interface AssetFile {
13
+ /** URL path (e.g., "/index.html", "/images/logo.png") */
14
+ path: string;
15
+ /** Full path on disk */
16
+ localPath: string;
17
+ /** 32-character hex hash (project-scoped) */
18
+ hash: string;
19
+ /** File size in bytes */
20
+ size: number;
21
+ /** MIME type for Content-Type header */
22
+ contentType: string;
23
+ }
24
+ /**
25
+ * Manifest format for Cloudflare's upload session API.
26
+ */
27
+ export interface AssetManifest {
28
+ [path: string]: {
29
+ hash: string;
30
+ size: number;
31
+ };
32
+ }
33
+ /** Maximum size per file: 25MB */
34
+ export declare const MAX_FILE_SIZE: number;
35
+ /** Maximum total size for all assets: 100MB */
36
+ export declare const MAX_TOTAL_SIZE: number;
37
+ /**
38
+ * Scan dist/client/ directory recursively and collect asset files.
39
+ * Hidden files and directories (starting with .) are ignored.
40
+ *
41
+ * @param projectDir - The project root directory
42
+ * @param userId - User ID for hash scoping
43
+ * @param projectId - Project ID for hash scoping
44
+ * @returns Array of asset files found
45
+ */
46
+ export declare function collectAssets(projectDir: string, userId: string, projectId: string): Promise<AssetFile[]>;
47
+ /**
48
+ * Generate a globally unique hash for an asset file.
49
+ * Hash includes userId AND projectId to ensure asset isolation between users
50
+ * and projects (projectId alone is not globally unique).
51
+ *
52
+ * @param userId - User ID (UUID)
53
+ * @param projectId - Project ID
54
+ * @param content - File content as Uint8Array
55
+ * @returns 32-character hex hash
56
+ */
57
+ export declare function hashAssetFile(userId: string, projectId: string, content: Uint8Array): Promise<string>;
58
+ /**
59
+ * Build manifest for Cloudflare's asset upload session API.
60
+ *
61
+ * @param assets - Array of asset files
62
+ * @returns Manifest object for Cloudflare API
63
+ */
64
+ export declare function buildManifest(assets: AssetFile[]): AssetManifest;
65
+ /**
66
+ * Validate asset sizes against limits.
67
+ * Throws descriptive error if limits are exceeded.
68
+ *
69
+ * @param assets - Array of asset files to validate
70
+ * @throws Error if any file exceeds MAX_FILE_SIZE or total exceeds MAX_TOTAL_SIZE
71
+ */
72
+ export declare function validateAssetSizes(assets: AssetFile[]): void;
73
+ //# sourceMappingURL=assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../../src/cli/deploy/assets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+CH;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,kCAAkC;AAClC,eAAO,MAAM,aAAa,QAAmB,CAAC;AAE9C,+CAA+C;AAC/C,eAAO,MAAM,cAAc,QAAoB,CAAC;AAEhD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,EAAE,CAAC,CAUtB;AAqDD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,aAAa,CAMhE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAiB5D"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Asset collection and hashing for static file uploads.
3
+ * Scans dist/client/ directory and prepares assets for Cloudflare upload.
4
+ */
5
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
6
+ import { join, relative, posix, extname } from "node:path";
7
+ import { createHash } from "node:crypto";
8
+ /**
9
+ * MIME type mapping for common file extensions.
10
+ * Uses text/javascript per HTML Living Standard recommendation.
11
+ * Includes charset=utf-8 for text-based types.
12
+ */
13
+ const MIME_TYPES = {
14
+ // Text/code with charset
15
+ ".html": "text/html; charset=utf-8",
16
+ ".css": "text/css; charset=utf-8",
17
+ ".js": "text/javascript; charset=utf-8",
18
+ ".mjs": "text/javascript; charset=utf-8",
19
+ ".json": "application/json; charset=utf-8",
20
+ ".txt": "text/plain; charset=utf-8",
21
+ ".xml": "application/xml; charset=utf-8",
22
+ ".svg": "image/svg+xml; charset=utf-8",
23
+ ".map": "application/json; charset=utf-8",
24
+ // Images
25
+ ".png": "image/png",
26
+ ".jpg": "image/jpeg",
27
+ ".jpeg": "image/jpeg",
28
+ ".gif": "image/gif",
29
+ ".webp": "image/webp",
30
+ ".avif": "image/avif",
31
+ ".ico": "image/x-icon",
32
+ // Fonts
33
+ ".woff": "font/woff",
34
+ ".woff2": "font/woff2",
35
+ ".ttf": "font/ttf",
36
+ ".otf": "font/otf",
37
+ ".eot": "application/vnd.ms-fontobject",
38
+ // Media
39
+ ".mp4": "video/mp4",
40
+ ".webm": "video/webm",
41
+ ".mp3": "audio/mpeg",
42
+ ".ogg": "audio/ogg",
43
+ // Other
44
+ ".wasm": "application/wasm",
45
+ ".webmanifest": "application/manifest+json",
46
+ ".pdf": "application/pdf",
47
+ };
48
+ /**
49
+ * Get MIME type for a file path based on extension.
50
+ */
51
+ export function getMimeType(filePath) {
52
+ const ext = extname(filePath).toLowerCase();
53
+ return MIME_TYPES[ext] ?? "application/octet-stream";
54
+ }
55
+ /** Maximum size per file: 25MB */
56
+ export const MAX_FILE_SIZE = 25 * 1024 * 1024;
57
+ /** Maximum total size for all assets: 100MB */
58
+ export const MAX_TOTAL_SIZE = 100 * 1024 * 1024;
59
+ /**
60
+ * Scan dist/client/ directory recursively and collect asset files.
61
+ * Hidden files and directories (starting with .) are ignored.
62
+ *
63
+ * @param projectDir - The project root directory
64
+ * @param userId - User ID for hash scoping
65
+ * @param projectId - Project ID for hash scoping
66
+ * @returns Array of asset files found
67
+ */
68
+ export async function collectAssets(projectDir, userId, projectId) {
69
+ const clientDir = join(projectDir, "dist", "client");
70
+ if (!existsSync(clientDir)) {
71
+ return [];
72
+ }
73
+ const assets = [];
74
+ await scanDirectory(clientDir, clientDir, userId, projectId, assets);
75
+ return assets;
76
+ }
77
+ /**
78
+ * Recursively scan a directory for files.
79
+ */
80
+ async function scanDirectory(baseDir, currentDir, userId, projectId, assets) {
81
+ const entries = readdirSync(currentDir, { withFileTypes: true });
82
+ for (const entry of entries) {
83
+ // Skip hidden files and directories
84
+ if (entry.name.startsWith(".")) {
85
+ continue;
86
+ }
87
+ const fullPath = join(currentDir, entry.name);
88
+ if (entry.isDirectory()) {
89
+ await scanDirectory(baseDir, fullPath, userId, projectId, assets);
90
+ }
91
+ else if (entry.isFile()) {
92
+ // Check file size before reading to avoid OOM on oversized files
93
+ const stats = statSync(fullPath);
94
+ if (stats.size > MAX_FILE_SIZE) {
95
+ const relativePath = relative(baseDir, fullPath);
96
+ const urlPath = "/" + relativePath.split(/[/\\]/).join(posix.sep);
97
+ throw new Error(`File ${urlPath} exceeds 25MB limit (${(stats.size / 1024 / 1024).toFixed(1)}MB)`);
98
+ }
99
+ const content = readFileSync(fullPath);
100
+ const hash = await hashAssetFile(userId, projectId, content);
101
+ const relativePath = relative(baseDir, fullPath);
102
+ // Use POSIX path separators for URL paths
103
+ const urlPath = "/" + relativePath.split(/[/\\]/).join(posix.sep);
104
+ const contentType = getMimeType(urlPath);
105
+ assets.push({
106
+ path: urlPath,
107
+ localPath: fullPath,
108
+ hash,
109
+ size: content.length,
110
+ contentType,
111
+ });
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Generate a globally unique hash for an asset file.
117
+ * Hash includes userId AND projectId to ensure asset isolation between users
118
+ * and projects (projectId alone is not globally unique).
119
+ *
120
+ * @param userId - User ID (UUID)
121
+ * @param projectId - Project ID
122
+ * @param content - File content as Uint8Array
123
+ * @returns 32-character hex hash
124
+ */
125
+ export async function hashAssetFile(userId, projectId, content) {
126
+ const hash = createHash("sha256");
127
+ // Include userId and projectId in the hash for isolation
128
+ hash.update(userId);
129
+ hash.update(projectId);
130
+ hash.update(content);
131
+ // Truncate to 32 hex characters (16 bytes) as per Cloudflare spec
132
+ return hash.digest("hex").slice(0, 32);
133
+ }
134
+ /**
135
+ * Build manifest for Cloudflare's asset upload session API.
136
+ *
137
+ * @param assets - Array of asset files
138
+ * @returns Manifest object for Cloudflare API
139
+ */
140
+ export function buildManifest(assets) {
141
+ const manifest = {};
142
+ for (const asset of assets) {
143
+ manifest[asset.path] = { hash: asset.hash, size: asset.size };
144
+ }
145
+ return manifest;
146
+ }
147
+ /**
148
+ * Validate asset sizes against limits.
149
+ * Throws descriptive error if limits are exceeded.
150
+ *
151
+ * @param assets - Array of asset files to validate
152
+ * @throws Error if any file exceeds MAX_FILE_SIZE or total exceeds MAX_TOTAL_SIZE
153
+ */
154
+ export function validateAssetSizes(assets) {
155
+ // Check individual file sizes first
156
+ for (const asset of assets) {
157
+ if (asset.size > MAX_FILE_SIZE) {
158
+ throw new Error(`File ${asset.path} exceeds 25MB limit (${(asset.size / 1024 / 1024).toFixed(1)}MB)`);
159
+ }
160
+ }
161
+ // Check total size
162
+ const totalSize = assets.reduce((sum, asset) => sum + asset.size, 0);
163
+ if (totalSize > MAX_TOTAL_SIZE) {
164
+ throw new Error(`Total assets exceed 100MB limit (${Math.round(totalSize / 1024 / 1024)}MB)`);
165
+ }
166
+ }
167
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/cli/deploy/assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Streaming base64 encoder for memory-efficient file uploads.
3
+ * Handles the 3-byte-to-4-char base64 encoding in a streaming fashion.
4
+ */
5
+ import { Transform } from "node:stream";
6
+ /**
7
+ * Create a Transform stream that base64-encodes input bytes.
8
+ * Handles buffering for the 3-byte input requirement.
9
+ */
10
+ export declare function createBase64EncodeStream(): Transform;
11
+ /**
12
+ * Calculate base64-encoded size for a given raw size.
13
+ * Base64 encodes 3 bytes into 4 characters.
14
+ */
15
+ export declare function base64EncodedSize(rawSize: number): number;
16
+ //# sourceMappingURL=base64-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-stream.d.ts","sourceRoot":"","sources":["../../../src/cli/deploy/base64-stream.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,SAAS,CA4BpD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGzD"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Streaming base64 encoder for memory-efficient file uploads.
3
+ * Handles the 3-byte-to-4-char base64 encoding in a streaming fashion.
4
+ */
5
+ import { Transform } from "node:stream";
6
+ /**
7
+ * Create a Transform stream that base64-encodes input bytes.
8
+ * Handles buffering for the 3-byte input requirement.
9
+ */
10
+ export function createBase64EncodeStream() {
11
+ let buffer = Buffer.alloc(0);
12
+ return new Transform({
13
+ transform(chunk, _encoding, callback) {
14
+ // Combine buffer with new chunk
15
+ buffer = Buffer.concat([buffer, chunk]);
16
+ // Process complete 3-byte groups
17
+ const completeGroups = Math.floor(buffer.length / 3);
18
+ const processBytes = completeGroups * 3;
19
+ if (completeGroups > 0) {
20
+ const toProcess = buffer.subarray(0, processBytes);
21
+ this.push(toProcess.toString("base64"));
22
+ buffer = buffer.subarray(processBytes);
23
+ }
24
+ callback();
25
+ },
26
+ flush(callback) {
27
+ // Handle remaining 1 or 2 bytes with padding
28
+ if (buffer.length > 0) {
29
+ this.push(buffer.toString("base64"));
30
+ }
31
+ callback();
32
+ },
33
+ });
34
+ }
35
+ /**
36
+ * Calculate base64-encoded size for a given raw size.
37
+ * Base64 encodes 3 bytes into 4 characters.
38
+ */
39
+ export function base64EncodedSize(rawSize) {
40
+ if (rawSize === 0)
41
+ return 0;
42
+ return Math.ceil(rawSize / 3) * 4;
43
+ }
44
+ //# sourceMappingURL=base64-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-stream.js","sourceRoot":"","sources":["../../../src/cli/deploy/base64-stream.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE7B,OAAO,IAAI,SAAS,CAAC;QACnB,SAAS,CAAC,KAAa,EAAE,SAAyB,EAAE,QAA2B;YAC7E,gCAAgC;YAChC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAExC,iCAAiC;YACjC,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACrD,MAAM,YAAY,GAAG,cAAc,GAAG,CAAC,CAAC;YAExC,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACxC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACzC,CAAC;YAED,QAAQ,EAAE,CAAC;QACb,CAAC;QACD,KAAK,CAAC,QAA2B;YAC/B,6CAA6C;YAC7C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YACvC,CAAC;YACD,QAAQ,EAAE,CAAC;QACb,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type Readable } from "node:stream";
2
+ import type { AssetFile } from "./assets.js";
3
+ /** Fixed boundary for asset uploads - matches what platform expects */
4
+ export declare const BOUNDARY = "----fling-asset";
5
+ /**
6
+ * Create a streaming multipart body for a bucket of assets.
7
+ * All files are base64-encoded as they stream through.
8
+ * Each part includes Content-Type header for proper MIME type serving.
9
+ *
10
+ * @param assets - Array of asset files to include in the multipart body
11
+ * @returns stream - The multipart body stream
12
+ * @returns contentLength - Total content length (for testing validation)
13
+ */
14
+ export declare function createBucketUploadStream(assets: AssetFile[]): {
15
+ stream: Readable;
16
+ contentLength: number;
17
+ };
18
+ //# sourceMappingURL=bucket-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-stream.d.ts","sourceRoot":"","sources":["../../../src/cli/deploy/bucket-stream.ts"],"names":[],"mappings":"AAKA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,uEAAuE;AACvE,eAAO,MAAM,QAAQ,oBAAoB,CAAC;AAE1C;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EAAE,GAClB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAkD7C"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Create a streaming multipart body for uploading a bucket of assets.
3
+ * Streams multiple files with base64 encoding in a single request.
4
+ */
5
+ import { createReadStream } from "node:fs";
6
+ import { PassThrough } from "node:stream";
7
+ import { pipeline } from "node:stream/promises";
8
+ import { createBase64EncodeStream, base64EncodedSize } from "./base64-stream.js";
9
+ /** Fixed boundary for asset uploads - matches what platform expects */
10
+ export const BOUNDARY = "----fling-asset";
11
+ /**
12
+ * Create a streaming multipart body for a bucket of assets.
13
+ * All files are base64-encoded as they stream through.
14
+ * Each part includes Content-Type header for proper MIME type serving.
15
+ *
16
+ * @param assets - Array of asset files to include in the multipart body
17
+ * @returns stream - The multipart body stream
18
+ * @returns contentLength - Total content length (for testing validation)
19
+ */
20
+ export function createBucketUploadStream(assets) {
21
+ // Calculate total content length for validation
22
+ // Header includes Content-Disposition AND Content-Type
23
+ // Use Buffer.byteLength for safety with non-ASCII chars in Content-Type (e.g., charset)
24
+ let contentLength = 0;
25
+ for (const asset of assets) {
26
+ const header = `--${BOUNDARY}\r\n` +
27
+ `Content-Disposition: form-data; name="${asset.hash}"\r\n` +
28
+ `Content-Type: ${asset.contentType}\r\n` +
29
+ `\r\n`;
30
+ const base64Size = base64EncodedSize(asset.size);
31
+ contentLength += Buffer.byteLength(header, "utf8") + base64Size + 2; // +2 for \r\n after content
32
+ }
33
+ contentLength += Buffer.byteLength(`--${BOUNDARY}--\r\n`, "utf8");
34
+ // Create streaming pipeline that yields parts one by one
35
+ const output = new PassThrough();
36
+ void (async () => {
37
+ try {
38
+ for (const asset of assets) {
39
+ // Write part header with Content-Type for proper MIME serving
40
+ const header = `--${BOUNDARY}\r\n` +
41
+ `Content-Disposition: form-data; name="${asset.hash}"\r\n` +
42
+ `Content-Type: ${asset.contentType}\r\n` +
43
+ `\r\n`;
44
+ output.write(header);
45
+ // Stream file through base64 encoder
46
+ const fileStream = createReadStream(asset.localPath);
47
+ const base64Stream = fileStream.pipe(createBase64EncodeStream());
48
+ // Pipe base64 stream to output without ending output
49
+ await pipeline(base64Stream, output, { end: false });
50
+ // Write part terminator
51
+ output.write("\r\n");
52
+ }
53
+ // Write final boundary
54
+ output.write(`--${BOUNDARY}--\r\n`);
55
+ output.end();
56
+ }
57
+ catch (err) {
58
+ output.destroy(err instanceof Error ? err : new Error(String(err)));
59
+ }
60
+ })();
61
+ return { stream: output, contentLength };
62
+ }
63
+ //# sourceMappingURL=bucket-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-stream.js","sourceRoot":"","sources":["../../../src/cli/deploy/bucket-stream.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGjF,uEAAuE;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,iBAAiB,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAmB;IAEnB,gDAAgD;IAChD,uDAAuD;IACvD,wFAAwF;IACxF,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GACV,KAAK,QAAQ,MAAM;YACnB,yCAAyC,KAAK,CAAC,IAAI,OAAO;YAC1D,iBAAiB,KAAK,CAAC,WAAW,MAAM;YACxC,MAAM,CAAC;QACT,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,aAAa,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,4BAA4B;IACnG,CAAC;IACD,aAAa,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,QAAQ,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElE,yDAAyD;IACzD,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IAEjC,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,CAAC;YACH,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,8DAA8D;gBAC9D,MAAM,MAAM,GACV,KAAK,QAAQ,MAAM;oBACnB,yCAAyC,KAAK,CAAC,IAAI,OAAO;oBAC1D,iBAAiB,KAAK,CAAC,WAAW,MAAM;oBACxC,MAAM,CAAC;gBACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAErB,qCAAqC;gBACrC,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACrD,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,CAAC;gBAEjE,qDAAqD;gBACrD,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;gBAErD,wBAAwB;gBACxB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAED,uBAAuB;YACvB,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,OAAO,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAC3C,CAAC"}