@lunora/cli 0.0.0 → 1.0.0-alpha.2

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 (72) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +109 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/bin.mjs +11 -0
  5. package/dist/index.d.mts +852 -0
  6. package/dist/index.d.ts +852 -0
  7. package/dist/index.mjs +19 -0
  8. package/dist/packem_chunks/handler.mjs +76 -0
  9. package/dist/packem_chunks/handler10.mjs +22 -0
  10. package/dist/packem_chunks/handler11.mjs +192 -0
  11. package/dist/packem_chunks/handler12.mjs +131 -0
  12. package/dist/packem_chunks/handler13.mjs +65 -0
  13. package/dist/packem_chunks/handler14.mjs +58 -0
  14. package/dist/packem_chunks/handler15.mjs +79 -0
  15. package/dist/packem_chunks/handler16.mjs +41 -0
  16. package/dist/packem_chunks/handler17.mjs +105 -0
  17. package/dist/packem_chunks/handler18.mjs +172 -0
  18. package/dist/packem_chunks/handler19.mjs +89 -0
  19. package/dist/packem_chunks/handler2.mjs +114 -0
  20. package/dist/packem_chunks/handler20.mjs +94 -0
  21. package/dist/packem_chunks/handler21.mjs +311 -0
  22. package/dist/packem_chunks/handler3.mjs +204 -0
  23. package/dist/packem_chunks/handler4.mjs +33 -0
  24. package/dist/packem_chunks/handler5.mjs +49 -0
  25. package/dist/packem_chunks/handler6.mjs +91 -0
  26. package/dist/packem_chunks/handler7.mjs +42 -0
  27. package/dist/packem_chunks/handler8.mjs +174 -0
  28. package/dist/packem_chunks/handler9.mjs +16 -0
  29. package/dist/packem_chunks/planDevCommand.mjs +543 -0
  30. package/dist/packem_chunks/runCodegenCommand.mjs +52 -0
  31. package/dist/packem_chunks/runDeployCommand.mjs +504 -0
  32. package/dist/packem_chunks/runInitCommand.mjs +652 -0
  33. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +397 -0
  34. package/dist/packem_chunks/runResetCommand.mjs +41 -0
  35. package/dist/packem_chunks/runRpcCommand.mjs +68 -0
  36. package/dist/packem_shared/COMMANDS-1V_KEx35.mjs +905 -0
  37. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +244 -0
  38. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +19 -0
  39. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +13 -0
  40. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +38 -0
  41. package/dist/packem_shared/command-BDXcJCCJ.mjs +14 -0
  42. package/dist/packem_shared/createLogger-CHPNjFw2.mjs +73 -0
  43. package/dist/packem_shared/defaultSpawner-DxI3mebw.mjs +43 -0
  44. package/dist/packem_shared/diffSnapshots-RR2ZE8Ya.mjs +161 -0
  45. package/dist/packem_shared/docker-hMQ97KSQ.mjs +21 -0
  46. package/dist/packem_shared/features-ocSSpZtS.mjs +24 -0
  47. package/dist/packem_shared/insertSchemaExtension-BuzF6-t2.mjs +59 -0
  48. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +41 -0
  49. package/dist/packem_shared/output-format-7gyGR3h8.mjs +17 -0
  50. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +56 -0
  51. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +94 -0
  52. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +16 -0
  53. package/dist/packem_shared/runAddCommand-BZGkRnBs.mjs +693 -0
  54. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +79 -0
  55. package/dist/packem_shared/schemaIrToSnapshot-aBTo7TM5.mjs +43 -0
  56. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +12 -0
  57. package/package.json +61 -18
  58. package/skills/README.md +29 -0
  59. package/skills/lunora/SKILL.md +83 -0
  60. package/skills/lunora-create-package/SKILL.md +129 -0
  61. package/skills/lunora-deploy/SKILL.md +150 -0
  62. package/skills/lunora-functions/SKILL.md +182 -0
  63. package/skills/lunora-migration-helper/SKILL.md +194 -0
  64. package/skills/lunora-performance-audit/SKILL.md +143 -0
  65. package/skills/lunora-quickstart/SKILL.md +240 -0
  66. package/skills/lunora-realtime/SKILL.md +177 -0
  67. package/skills/lunora-setup-auth/SKILL.md +170 -0
  68. package/skills/lunora-setup-hyperdrive/SKILL.md +154 -0
  69. package/skills/lunora-setup-hyperdrive-global/SKILL.md +171 -0
  70. package/skills/lunora-setup-mail/SKILL.md +151 -0
  71. package/skills/lunora-setup-scheduler/SKILL.md +157 -0
  72. package/skills/lunora-setup-storage/SKILL.md +154 -0
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: lunora-setup-storage
3
+ description: Adds R2-backed file storage to a Lunora app. Use for uploads/downloads via `lunora registry add storage`, signed PUT/GET URLs, the `UPLOADS` R2 bucket binding, `STORAGE_SIGNING_SECRET`, per-tenant key scoping, and verifying downloads in the Worker.
4
+ ---
5
+
6
+ # Lunora Setup Storage
7
+
8
+ Wire R2-backed file storage into a Lunora app using the `storage` registry item,
9
+ which is built on `@lunora/storage` (an R2 adapter plus HMAC signed-URL helpers)
10
+ and exposes idiomatic Lunora functions for direct browser uploads, gated
11
+ downloads, delete, and list — so the bytes never proxy through your Worker.
12
+
13
+ ## When to Use
14
+
15
+ - Uploading user files (avatars, attachments) straight to R2.
16
+ - Serving private/gated downloads via short-lived signed URLs.
17
+ - Listing or deleting a caller's stored objects.
18
+
19
+ ## When Not to Use
20
+
21
+ - The project has no Lunora backend yet — use `lunora-quickstart` first.
22
+ - Storage is already installed and you just want to upload — call
23
+ `client.action("storage/generateUploadUrl", …)` and `PUT` to the returned URL.
24
+
25
+ ## Workflow
26
+
27
+ 1. Add the `storage` item.
28
+ 2. Configure the `UPLOADS` R2 bucket binding and the signing secret.
29
+ 3. Regenerate types with `lunora codegen`.
30
+ 4. Verify signed downloads in the Worker's `GET /storage/:key` route.
31
+ 5. Upload/download from the client.
32
+
33
+ ## Step 1: Add the item
34
+
35
+ ```bash
36
+ lunora registry add storage
37
+ ```
38
+
39
+ This:
40
+
41
+ 1. Adds `@lunora/storage` and `@lunora/server` to `package.json` (run
42
+ `pnpm install` afterwards).
43
+ 2. Adds an R2 bucket binding to `wrangler.jsonc` (`r2_buckets`, binding
44
+ **`UPLOADS`**, `bucket_name: "REPLACE_ME-uploads"` — rename it to a real
45
+ bucket). It **merges** into any existing `r2_buckets`.
46
+ 3. Scaffolds `STORAGE_SIGNING_SECRET` (a secret) and `STORAGE_PUBLIC_BASE_URL`
47
+ into `.dev.vars`.
48
+ 4. Copies `lunora/storage/index.ts` (the `generateUploadUrl` /
49
+ `getDownloadUrl` / `deleteObject` / `listObjects` functions) into your
50
+ project — it is **yours** to edit.
51
+
52
+ ## Step 2: Configure the binding + secrets
53
+
54
+ | Name | Where | Notes |
55
+ | ------------------------- | ------------------------------------ | ------------------------------------------------------------------------ |
56
+ | `UPLOADS` | `wrangler.jsonc` → `r2_buckets[]` | The R2 bucket binding. Point `bucket_name` at a real bucket. |
57
+ | `STORAGE_SIGNING_SECRET` | secret (`.dev.vars` / `secret put`) | HMAC secret for signed URLs. Min 32 chars; never share across buckets. |
58
+ | `STORAGE_PUBLIC_BASE_URL` | var (`.dev.vars` / `wrangler.jsonc`) | Public host/route that fronts the bucket and serves `GET /storage/:key`. |
59
+
60
+ Generate a real signing secret with `openssl rand -base64 32` and write it with
61
+ `wrangler secret put STORAGE_SIGNING_SECRET` for production.
62
+
63
+ ## Step 3: Regenerate types
64
+
65
+ ```bash
66
+ lunora codegen
67
+ ```
68
+
69
+ The functions surface in the generated `api` as `api.storage.generateUploadUrl`,
70
+ `api.storage.getDownloadUrl`, `api.storage.deleteObject`, and
71
+ `api.storage.listObjects`.
72
+
73
+ ## Step 4: Verify downloads in the Worker
74
+
75
+ Signed URLs are only as safe as the route that checks them. Gate
76
+ `GET /storage/:key` with `verifySignedUrl` before streaming the R2 body
77
+ (`@lunora/server` also ships `serveStorageObject` to do this):
78
+
79
+ ```ts
80
+ import { verifySignedUrl } from "@lunora/storage";
81
+
82
+ export default {
83
+ async fetch(request: Request, env: Env): Promise<Response> {
84
+ const url = new URL(request.url);
85
+
86
+ if (url.pathname.startsWith("/storage/")) {
87
+ const result = await verifySignedUrl(url, env.STORAGE_SIGNING_SECRET);
88
+
89
+ if (!result.valid || result.key === undefined) {
90
+ // Expose only `valid` — a precise reason is a signing oracle.
91
+ return new Response("forbidden", { status: 403 });
92
+ }
93
+
94
+ const object = await env.UPLOADS.get(result.key);
95
+
96
+ if (!object) {
97
+ return new Response("not found", { status: 404 });
98
+ }
99
+
100
+ return new Response(object.body, {
101
+ headers: { "content-type": object.httpMetadata?.contentType ?? "application/octet-stream" },
102
+ });
103
+ }
104
+
105
+ // ... your Lunora handler
106
+ return new Response("not found", { status: 404 });
107
+ },
108
+ };
109
+ ```
110
+
111
+ `verifySignedUrl` checks expiry, then the HMAC. On a host-rewrite / CDN topology
112
+ pass `{ expectedHost }` (the `STORAGE_PUBLIC_BASE_URL` host) so the signature
113
+ canonicalizes against the host it was minted for.
114
+
115
+ ## Step 5: Upload / download from the client
116
+
117
+ ```ts
118
+ // 1. ask the server for a signed PUT URL
119
+ const { key, url } = await client.action("storage/generateUploadUrl", {
120
+ key: "avatar.png",
121
+ contentType: file.type,
122
+ });
123
+
124
+ // 2. upload straight to R2 (no Worker proxy)
125
+ await fetch(url, { method: "PUT", headers: { "content-type": file.type }, body: file });
126
+
127
+ // 3. later, get a signed GET URL to display it
128
+ const { url: downloadUrl } = await client.action("storage/getDownloadUrl", { key: "avatar.png" });
129
+ ```
130
+
131
+ Every key is scoped per-tenant with `scopeKey(tenantPrefix(ctx.auth.userId),
132
+ key)`, so a client-supplied key can never address another user's data. The
133
+ functions return the **scoped** key (`<userId>/avatar.png`) alongside the URL;
134
+ persist that, and pass the bare key back in — the component re-scopes it.
135
+
136
+ ## Common Pitfalls
137
+
138
+ 1. **Skipping `verifySignedUrl` on the download route.** Without it, anyone can
139
+ read any key. Always verify before streaming.
140
+ 2. **Placeholder bucket name.** `bucket_name: "REPLACE_ME-uploads"` ships as a
141
+ placeholder — rename it to a real R2 bucket.
142
+ 3. **Short / shared signing secret.** Use ≥32 chars and a distinct secret per
143
+ bucket; reusing it lets one bucket's URLs sign for another.
144
+ 4. **Proxying bytes through the Worker.** The design uploads/downloads directly
145
+ to R2 via signed URLs — don't re-route the file body through a function.
146
+
147
+ ## Checklist
148
+
149
+ - [ ] `lunora registry add storage` run, `pnpm install` done.
150
+ - [ ] `UPLOADS` bucket bound to a real bucket; `STORAGE_SIGNING_SECRET` (≥32
151
+ chars) and `STORAGE_PUBLIC_BASE_URL` set.
152
+ - [ ] `lunora codegen` run so `api.storage.*` is generated.
153
+ - [ ] `GET /storage/:key` route verifies signed URLs before streaming.
154
+ - [ ] Verified a client upload → signed download round-trip.