@rebasepro/cli 0.23.0 → 0.24.1-canary.g19596ef

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 (186) hide show
  1. package/README.md +19 -3
  2. package/bin/rebase.js +4 -1
  3. package/dist/agent-setup-B31F00DC.js +324 -0
  4. package/dist/agent-setup-B31F00DC.js.map +1 -0
  5. package/dist/api-keys-Bl1I9q7M.js +525 -0
  6. package/dist/api-keys-Bl1I9q7M.js.map +1 -0
  7. package/dist/apps-CNgQJjg5.js +340 -0
  8. package/dist/apps-CNgQJjg5.js.map +1 -0
  9. package/dist/args-BnmsEXvu.js +109 -0
  10. package/dist/args-BnmsEXvu.js.map +1 -0
  11. package/dist/atlas-dev-database-D_LP_Woq.js +62 -0
  12. package/dist/atlas-dev-database-D_LP_Woq.js.map +1 -0
  13. package/dist/auth-aySHEF45.js +414 -0
  14. package/dist/auth-aySHEF45.js.map +1 -0
  15. package/dist/branch-pointer-DvSHBjjz.js +2 -0
  16. package/dist/build-DIHWIqO4.js +336 -0
  17. package/dist/build-DIHWIqO4.js.map +1 -0
  18. package/dist/bundle-CqURVN-K.js +1621 -0
  19. package/dist/bundle-CqURVN-K.js.map +1 -0
  20. package/dist/bundle.d.ts +12 -3
  21. package/dist/cli.d.ts +23 -2
  22. package/dist/cloud-DjByQlkW.js +9667 -0
  23. package/dist/cloud-DjByQlkW.js.map +1 -0
  24. package/dist/collection-drift-BKj_BoMq.js +106 -0
  25. package/dist/collection-drift-BKj_BoMq.js.map +1 -0
  26. package/dist/commands/__test_support__/parser-specs.d.ts +13 -0
  27. package/dist/commands/agent-setup.d.ts +116 -0
  28. package/dist/commands/api-keys.d.ts +86 -23
  29. package/dist/commands/apps.d.ts +17 -0
  30. package/dist/commands/auth.d.ts +7 -0
  31. package/dist/commands/build.d.ts +5 -1
  32. package/dist/commands/cloud/auth.d.ts +4 -3
  33. package/dist/commands/cloud/bundle-deploy.d.ts +45 -0
  34. package/dist/commands/cloud/context.d.ts +44 -5
  35. package/dist/commands/cloud/cron.d.ts +53 -0
  36. package/dist/commands/cloud/debug.d.ts +134 -0
  37. package/dist/commands/cloud/index.d.ts +89 -12
  38. package/dist/commands/cloud/token-capabilities.d.ts +76 -0
  39. package/dist/commands/cloud/tokens.d.ts +23 -0
  40. package/dist/commands/console.d.ts +12 -0
  41. package/dist/commands/db.d.ts +79 -25
  42. package/dist/commands/dev.d.ts +181 -13
  43. package/dist/commands/doctor.d.ts +5 -0
  44. package/dist/commands/generate_sdk.d.ts +29 -2
  45. package/dist/commands/init.d.ts +115 -14
  46. package/dist/commands/schema.d.ts +6 -0
  47. package/dist/commands/skills.d.ts +126 -7
  48. package/dist/commands/start.d.ts +44 -0
  49. package/dist/commands/storage.d.ts +46 -0
  50. package/dist/commands/upgrade.d.ts +1 -0
  51. package/dist/consent-ClRHmcxf.js +76 -0
  52. package/dist/consent-ClRHmcxf.js.map +1 -0
  53. package/dist/console-D6SJf8k_.js +102 -0
  54. package/dist/console-D6SJf8k_.js.map +1 -0
  55. package/dist/context-BqhRydBg.js +1234 -0
  56. package/dist/context-BqhRydBg.js.map +1 -0
  57. package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
  58. package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
  59. package/dist/daemon-pzsQwDHa.js +2 -0
  60. package/dist/db-B7RuNuaQ.js +1312 -0
  61. package/dist/db-B7RuNuaQ.js.map +1 -0
  62. package/dist/derive-Dl0z2OWi.js +484 -0
  63. package/dist/derive-Dl0z2OWi.js.map +1 -0
  64. package/dist/dev-CYr_WXan.js +1599 -0
  65. package/dist/dev-CYr_WXan.js.map +1 -0
  66. package/dist/dev-db/atlas-dev-database.d.ts +32 -0
  67. package/dist/dev-db/daemon-entry.d.ts +13 -0
  68. package/dist/dev-db/daemon.d.ts +10 -0
  69. package/dist/dev-db/last-dev-database.d.ts +41 -0
  70. package/dist/dev-db/prepare.d.ts +29 -3
  71. package/dist/dev-db/pull.d.ts +90 -5
  72. package/dist/dev-db/resolve.d.ts +26 -2
  73. package/dist/dev-preflight-B-LYyBf1.js +2 -0
  74. package/dist/dev-server-DG8Tt-uf.js +96 -0
  75. package/dist/dev-server-DG8Tt-uf.js.map +1 -0
  76. package/dist/doctor-BP8cZ-7F.js +771 -0
  77. package/dist/doctor-BP8cZ-7F.js.map +1 -0
  78. package/dist/eject-DizdoecV.js +335 -0
  79. package/dist/eject-DizdoecV.js.map +1 -0
  80. package/dist/fold-static-BC9CYicw.js +201 -0
  81. package/dist/fold-static-BC9CYicw.js.map +1 -0
  82. package/dist/fold-static.d.ts +26 -3
  83. package/dist/function-portability-BIvlvR9q.js +265 -0
  84. package/dist/function-portability-BIvlvR9q.js.map +1 -0
  85. package/dist/generate_sdk-BPqdJc3Q.js +489 -0
  86. package/dist/generate_sdk-BPqdJc3Q.js.map +1 -0
  87. package/dist/index.es.js +199 -22095
  88. package/dist/index.es.js.map +1 -1
  89. package/dist/init-Utstvh2Q.js +1149 -0
  90. package/dist/init-Utstvh2Q.js.map +1 -0
  91. package/dist/last-dev-database-B58g89uA.js +2 -0
  92. package/dist/manifest-DRp0UVTj.js +880 -0
  93. package/dist/manifest-DRp0UVTj.js.map +1 -0
  94. package/dist/manifest.d.ts +56 -4
  95. package/dist/normalize-imports-X0Tgze9y.js +107 -0
  96. package/dist/normalize-imports-X0Tgze9y.js.map +1 -0
  97. package/dist/package-manager-DSEOwTqM.js +193 -0
  98. package/dist/package-manager-DSEOwTqM.js.map +1 -0
  99. package/dist/path-flags-BFgG9V6y.js +91 -0
  100. package/dist/path-flags-BFgG9V6y.js.map +1 -0
  101. package/dist/precompress.d.ts +17 -0
  102. package/dist/prepare-CDN6lSpz.js +1236 -0
  103. package/dist/prepare-CDN6lSpz.js.map +1 -0
  104. package/dist/{project-3WCbrrfW.js → project-Wp2zoyeN.js} +34 -184
  105. package/dist/project-Wp2zoyeN.js.map +1 -0
  106. package/dist/pull-CrGYMEsy.js +458 -0
  107. package/dist/pull-CrGYMEsy.js.map +1 -0
  108. package/dist/resolve-B2O-wKKk.js +2 -0
  109. package/dist/resources-FD7AfZx8.js +183 -0
  110. package/dist/resources-FD7AfZx8.js.map +1 -0
  111. package/dist/schema-DMdr431S.js +125 -0
  112. package/dist/schema-DMdr431S.js.map +1 -0
  113. package/dist/skills-DTFibT1m.js +642 -0
  114. package/dist/skills-DTFibT1m.js.map +1 -0
  115. package/dist/start-DEa-hOBh.js +267 -0
  116. package/dist/start-DEa-hOBh.js.map +1 -0
  117. package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
  118. package/dist/state-Do7ZJYkF.js.map +1 -0
  119. package/dist/status-DxnJTekv.js +352 -0
  120. package/dist/status-DxnJTekv.js.map +1 -0
  121. package/dist/storage-DyVq-f_F.js +303 -0
  122. package/dist/storage-DyVq-f_F.js.map +1 -0
  123. package/dist/telemetry/identity.d.ts +10 -1
  124. package/dist/telemetry-C8Uq8phb.js +129 -0
  125. package/dist/telemetry-C8Uq8phb.js.map +1 -0
  126. package/dist/telemetry-EMFRVRQi.js +351 -0
  127. package/dist/telemetry-EMFRVRQi.js.map +1 -0
  128. package/dist/unknown-command-DNwDcX3w.js +98 -0
  129. package/dist/unknown-command-DNwDcX3w.js.map +1 -0
  130. package/dist/upgrade-CtZtRkTE.js +1332 -0
  131. package/dist/upgrade-CtZtRkTE.js.map +1 -0
  132. package/dist/upgrade.d.ts +38 -5
  133. package/dist/utils/dev-preflight.d.ts +47 -5
  134. package/dist/utils/dev-server.d.ts +30 -0
  135. package/dist/utils/path-flags.d.ts +31 -0
  136. package/dist/utils/project.d.ts +16 -0
  137. package/dist/utils/temp-archive.d.ts +9 -0
  138. package/dist/version-Buyyo1ut.js +58 -0
  139. package/dist/version-Buyyo1ut.js.map +1 -0
  140. package/package.json +7 -7
  141. package/templates/eject/Dockerfile +4 -2
  142. package/templates/eject/backend/src/env.ts +1 -1
  143. package/templates/eject/backend/src/index.ts +42 -4
  144. package/templates/eject/docker-compose.custom.yml +8 -4
  145. package/templates/generated-schemas/blank.ts +40 -0
  146. package/templates/generated-schemas/blog.ts +127 -0
  147. package/templates/generated-schemas/ecommerce.ts +93 -0
  148. package/templates/overlays/baas/ai-instructions.md +1 -1
  149. package/templates/overlays/baas/config/package.json +1 -0
  150. package/templates/overlays/baas/config/storage.ts +5 -3
  151. package/templates/overlays/baas/package.json +1 -0
  152. package/templates/template/.cursorrules +1 -1
  153. package/templates/template/.env.example +20 -10
  154. package/templates/template/.github/copilot-instructions.md +1 -1
  155. package/templates/template/.mcp.json +2 -2
  156. package/templates/template/.windsurfrules +1 -1
  157. package/templates/template/AGENTS.md +1 -1
  158. package/templates/template/CLAUDE.md +1 -1
  159. package/templates/template/README.md +21 -15
  160. package/templates/template/ai-instructions.md +5 -4
  161. package/templates/template/backend/functions/hello.ts +8 -0
  162. package/templates/template/backend/package.json +1 -1
  163. package/templates/template/config/resources.ts +7 -6
  164. package/templates/template/config/storage.ts +5 -3
  165. package/templates/template/docker-compose.yml +15 -9
  166. package/templates/template/frontend/vite.config.ts +13 -3
  167. package/templates/template/gitignore +6 -1
  168. package/templates/template/package.json +5 -3
  169. package/templates/template/scripts/example.ts +6 -4
  170. package/templates/template/tsconfig.json +25 -0
  171. package/dist/branch-pointer-dBiClJ0p.js +0 -111
  172. package/dist/branch-pointer-dBiClJ0p.js.map +0 -1
  173. package/dist/constraints-DKGbNfUW.js +0 -48
  174. package/dist/constraints-DKGbNfUW.js.map +0 -1
  175. package/dist/daemon-DR9Tk0La.js +0 -355
  176. package/dist/daemon-DR9Tk0La.js.map +0 -1
  177. package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
  178. package/dist/dev-preflight-CNLl4rdo.js +0 -367
  179. package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
  180. package/dist/project-3WCbrrfW.js.map +0 -1
  181. package/dist/pull-Dywm5FWP.js +0 -232
  182. package/dist/pull-Dywm5FWP.js.map +0 -1
  183. package/dist/resolve-BAeRUBhg.js +0 -74
  184. package/dist/resolve-BAeRUBhg.js.map +0 -1
  185. package/dist/state-C59Elrnt.js.map +0 -1
  186. package/templates/template/backend/src/schema.generated.ts +0 -13
@@ -36,6 +36,22 @@ import usersCollection from "../../config/collections/users";
36
36
  const __filename = fileURLToPath(import.meta.url);
37
37
  const __dirname = path.dirname(__filename);
38
38
 
39
+ /**
40
+ * The project root: the nearest directory holding `rebase.json`.
41
+ *
42
+ * Walked up to rather than counted, because this file runs from two depths —
43
+ * `backend/src` under `rebase dev`, `backend/dist/backend/src` compiled — and
44
+ * a fixed `../..` from the compiled file is `backend/dist`, where local uploads
45
+ * then landed and the next build wiped them.
46
+ */
47
+ function findProjectRoot(from: string): string {
48
+ for (let dir = from; ; dir = path.dirname(dir)) {
49
+ if (fs.existsSync(path.join(dir, "rebase.json"))) return dir;
50
+ if (path.dirname(dir) === dir) return path.resolve(from, "../..");
51
+ }
52
+ }
53
+ const projectRoot = findProjectRoot(__dirname);
54
+
39
55
  // Which databases and buckets this project has, read from the declarations in
40
56
  // `config/resources.ts`. One declaration site, so this process, the platform
41
57
  // and the frontend all read the same list. A project that declares none gets
@@ -81,7 +97,24 @@ app.use("/*", cors({
81
97
  if (!origin) return "*";
82
98
  return isLocalhostOrigin(origin) ? origin : null;
83
99
  },
84
- credentials: true
100
+ credentials: true,
101
+ // A frontend on another origin reads only the response headers named here.
102
+ // Without `ETag` the SDK's `etagOf(row)` is always undefined there, and an
103
+ // `ifMatch` write silently goes out unconditional; without `Retry-After`
104
+ // and `X-Request-ID` a 429 or an error loses what it reports.
105
+ exposeHeaders: [
106
+ "ETag",
107
+ "Retry-After",
108
+ "X-Request-ID",
109
+ "X-RateLimit-Limit",
110
+ "X-RateLimit-Remaining",
111
+ "X-RateLimit-Reset",
112
+ "Preference-Applied",
113
+ // A resumable upload: where it lives, how far it got, what was stored.
114
+ "Location",
115
+ "Upload-Offset",
116
+ "Rebase-Upload-Result"
117
+ ]
85
118
  }));
86
119
 
87
120
  app.use("/*", secureHeaders());
@@ -106,7 +139,11 @@ const resolvedDataSources = resolveDataSources(process.env, dataSources);
106
139
  async function startServer() {
107
140
  const jwtSecret = env.JWT_SECRET;
108
141
  const PORT = env.PORT;
109
- const server = createServer(getRequestListener(app.fetch));
142
+ // 64 KB of request line plus headers, as the Rebase runtime takes, not
143
+ // Node's 16 KB: a vector search's embedding travels in `?vector=`, and
144
+ // 768 dimensions of it are already 17 KB of URL. (The SDK sends a vector
145
+ // search in a POST body; this is for a URL built by hand.)
146
+ const server = createServer({ maxHeaderSize: 64 * 1024 }, getRequestListener(app.fetch));
110
147
 
111
148
  // `backend/crons` holds the scheduled jobs — nightly backups among them.
112
149
  // Passed only when the directory exists, because a configured `cronsDir`
@@ -204,7 +241,9 @@ pass: env.SMTP_PASS! }
204
241
  storage: resolveStorageSources(
205
242
  process.env,
206
243
  storageSources,
207
- path.resolve(__dirname, "../../uploads")
244
+ // `<project>/uploads`, from source and compiled alike — where
245
+ // `rebase dev` keeps them too. `STORAGE_PATH` overrides it.
246
+ path.join(projectRoot, "uploads")
208
247
  ),
209
248
  // Storage is not under row-level security, so this hook IS its access
210
249
  // model — the server refuses to boot in production without one, because
@@ -278,7 +317,6 @@ pass: env.SMTP_PASS! }
278
317
 
279
318
  if (!isProduction) {
280
319
  // Dev mode: retry the next port if the current one is in use
281
- const projectRoot = path.resolve(__dirname, "../..");
282
320
  const actualPort = await listenWithPortRetry(server, PORT, { portFileDir: projectRoot, serviceKey: env.REBASE_SERVICE_KEY });
283
321
 
284
322
  // Clean up port file on exit
@@ -63,17 +63,21 @@ services:
63
63
  # served from.
64
64
  # {{/frontend}}
65
65
  CORS_ORIGINS: ${CORS_ORIGINS:?set CORS_ORIGINS to the origin you browse to}
66
- # A durable named volume, which is the case this flag acknowledges.
67
- STORAGE_PATH: /uploads
66
+ # A durable named volume, which is the case this flag acknowledges. It is
67
+ # mounted at /storage, one level above STORAGE_PATH, because every bucket
68
+ # declared in config/resources.ts gets a directory beside this one —
69
+ # bucket("media") is /storage/uploads__media — and all of them belong on
70
+ # the volume. The Dockerfile creates /storage for the `rebase` user.
71
+ STORAGE_PATH: /storage/uploads
68
72
  FORCE_LOCAL_STORAGE: "true"
69
73
  depends_on:
70
74
  db:
71
75
  condition: service_healthy
72
76
  volumes:
73
- - uploads:/uploads
77
+ - storage:/storage
74
78
 
75
79
  volumes:
76
80
  postgres_data:
77
81
  driver: local
78
- uploads:
82
+ storage:
79
83
  driver: local
@@ -0,0 +1,40 @@
1
+ // This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
2
+
3
+ import { boolean, jsonb, pgPolicy, pgSchema, text, timestamp, uuid } from 'drizzle-orm/pg-core';
4
+ import { relations as drizzleRelations, sql } from 'drizzle-orm';
5
+
6
+ export const rebaseSchema = pgSchema("rebase");
7
+
8
+
9
+ export const users = rebaseSchema.table("users", {
10
+ id: uuid("id").primaryKey().defaultRandom(),
11
+ email: text("email").unique().notNull(),
12
+ displayName: text("display_name"),
13
+ photoURL: text("photo_url"),
14
+ roles: text("roles").array(),
15
+ passwordHash: text("password_hash"),
16
+ emailVerified: boolean("email_verified").default(sql`FALSE`),
17
+ emailVerificationToken: text("email_verification_token"),
18
+ emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
19
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
20
+ createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
21
+ updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
22
+ }, (table) => ([
23
+ pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
24
+ pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
25
+ pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
26
+ pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
27
+ pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
28
+ pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
29
+ pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
30
+ pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
31
+ pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
32
+ pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
33
+ pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
34
+ pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
35
+ ])).enableRLS();
36
+
37
+ export const tables = { users };
38
+ export const enums = { };
39
+ export const relations = { };
40
+
@@ -0,0 +1,127 @@
1
+ // This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
2
+
3
+ import { type AnyPgColumn, boolean, index, integer, jsonb, pgEnum, pgPolicy, pgSchema, pgTable, primaryKey, text, timestamp, uuid } from 'drizzle-orm/pg-core';
4
+ import { relations as drizzleRelations, sql } from 'drizzle-orm';
5
+
6
+ export const rebaseSchema = pgSchema("rebase");
7
+
8
+ export const postsStatus = pgEnum("posts_status", ["draft", "review", "published"]);
9
+
10
+ export const authors = pgTable("authors", {
11
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
12
+ name: text("name").notNull(),
13
+ email: text("email").notNull(),
14
+ picture: text("picture")
15
+ }, (table) => ([
16
+ pgPolicy("authors_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
17
+ pgPolicy("authors_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
18
+ pgPolicy("authors_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
19
+ pgPolicy("authors_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
20
+ pgPolicy("authors_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
21
+ pgPolicy("authors_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
22
+ pgPolicy("authors_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
23
+ pgPolicy("authors_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
24
+ ])).enableRLS();
25
+
26
+ export const posts = pgTable("posts", {
27
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
28
+ title: text("title").notNull(),
29
+ content: text("content"),
30
+ status: postsStatus("status"),
31
+ authorId: integer("author_id").references((): AnyPgColumn => authors.id, { onDelete: "set null" })
32
+ }, (table) => ([
33
+ index("posts_author_id_ix_00ad191").on(table.authorId.asc().nullsLast()),
34
+ pgPolicy("posts_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
35
+ pgPolicy("posts_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
36
+ pgPolicy("posts_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
37
+ pgPolicy("posts_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
38
+ pgPolicy("posts_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
39
+ pgPolicy("posts_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
40
+ pgPolicy("posts_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
41
+ pgPolicy("posts_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
42
+ ])).enableRLS();
43
+
44
+ export const postsTags = pgTable("posts_tags", {
45
+ post_id: integer("post_id").notNull().references((): AnyPgColumn => posts.id, { onDelete: "cascade" }),
46
+ tag_id: integer("tag_id").notNull().references((): AnyPgColumn => tags.id, { onDelete: "cascade" }),
47
+ }, (table) => ([
48
+ primaryKey({ columns: [table.post_id, table.tag_id] }),
49
+ index("posts_tags_tag_id_ix_d4456f1").on(table.tag_id.asc().nullsLast()),
50
+ pgPolicy("posts_tags_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
51
+ pgPolicy("posts_tags_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
52
+ pgPolicy("posts_tags_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
53
+ pgPolicy("posts_tags_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
54
+ pgPolicy("posts_tags_default_edge_read", { as: "permissive", for: "select", to: ["public"], using: sql`(EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE "_ex0".id = "public"."posts_tags".post_id)) AND (EXISTS (SELECT 1 FROM "public"."tags" "_ex1" WHERE "_ex1".id = "public"."posts_tags".tag_id))` }),
55
+ pgPolicy("posts_tags_default_edge_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
56
+ pgPolicy("posts_tags_default_edge_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))`, withCheck: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
57
+ pgPolicy("posts_tags_default_edge_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
58
+ ])).enableRLS();
59
+
60
+ export const tags = pgTable("tags", {
61
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
62
+ name: text("name").notNull()
63
+ }, (table) => ([
64
+ pgPolicy("tags_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
65
+ pgPolicy("tags_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
66
+ pgPolicy("tags_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
67
+ pgPolicy("tags_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
68
+ pgPolicy("tags_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
69
+ pgPolicy("tags_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
70
+ pgPolicy("tags_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
71
+ pgPolicy("tags_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
72
+ ])).enableRLS();
73
+
74
+ export const users = rebaseSchema.table("users", {
75
+ id: uuid("id").primaryKey().defaultRandom(),
76
+ email: text("email").unique().notNull(),
77
+ displayName: text("display_name"),
78
+ photoURL: text("photo_url"),
79
+ roles: text("roles").array(),
80
+ passwordHash: text("password_hash"),
81
+ emailVerified: boolean("email_verified").default(sql`FALSE`),
82
+ emailVerificationToken: text("email_verification_token"),
83
+ emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
84
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
85
+ createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
86
+ updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
87
+ }, (table) => ([
88
+ pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
89
+ pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
90
+ pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
91
+ pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
92
+ pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
93
+ pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
94
+ pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
95
+ pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
96
+ pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
97
+ pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
98
+ pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
99
+ pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
100
+ ])).enableRLS();
101
+
102
+ export const postsRelations = drizzleRelations(posts, ({ one, many }) => ({
103
+ "author": one(authors, {
104
+ fields: [posts.authorId],
105
+ references: [authors.id],
106
+ relationName: "posts_authorId"
107
+ }),
108
+ "tags": many(postsTags, { relationName: "tags" })
109
+ }));
110
+
111
+ export const postsTagsRelations = drizzleRelations(postsTags, ({ one, many }) => ({
112
+ "post_id": one(posts, {
113
+ fields: [postsTags.post_id],
114
+ references: [posts.id],
115
+ relationName: "tags"
116
+ }),
117
+ "tag_id": one(tags, {
118
+ fields: [postsTags.tag_id],
119
+ references: [tags.id],
120
+ relationName: "posts_tags_tag_id"
121
+ })
122
+ }));
123
+
124
+ export const tables = { authors, posts, postsTags, tags, users };
125
+ export const enums = { postsStatus };
126
+ export const relations = { postsRelations, postsTagsRelations };
127
+
@@ -0,0 +1,93 @@
1
+ // This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
2
+
3
+ import { type AnyPgColumn, boolean, index, integer, jsonb, numeric, pgEnum, pgPolicy, pgSchema, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
4
+ import { relations as drizzleRelations, sql } from 'drizzle-orm';
5
+
6
+ export const rebaseSchema = pgSchema("rebase");
7
+
8
+ export const ordersStatus = pgEnum("orders_status", ["pending", "processing", "shipped", "delivered", "cancelled"]);
9
+ export const productsStatus = pgEnum("products_status", ["draft", "active", "archived"]);
10
+
11
+ export const categories = pgTable("categories", {
12
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
13
+ name: text("name").notNull(),
14
+ slug: text("slug").notNull(),
15
+ description: text("description"),
16
+ icon: text("icon")
17
+ }, (table) => ([
18
+ pgPolicy("categories_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
19
+ pgPolicy("categories_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
20
+ pgPolicy("categories_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
21
+ pgPolicy("categories_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
22
+ ])).enableRLS();
23
+
24
+ export const orders = pgTable("orders", {
25
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
26
+ customerEmail: text("customer_email").notNull(),
27
+ status: ordersStatus("status"),
28
+ total: numeric("total").notNull(),
29
+ notes: text("notes"),
30
+ createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
31
+ }, (table) => ([
32
+ pgPolicy("orders_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
33
+ pgPolicy("orders_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
34
+ pgPolicy("orders_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
35
+ pgPolicy("orders_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
36
+ ])).enableRLS();
37
+
38
+ export const products = pgTable("products", {
39
+ id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
40
+ name: text("name").notNull(),
41
+ description: text("description"),
42
+ price: numeric("price").notNull(),
43
+ image: text("image"),
44
+ status: productsStatus("status"),
45
+ categoryId: integer("category_id").references((): AnyPgColumn => categories.id, { onDelete: "set null" })
46
+ }, (table) => ([
47
+ index("products_category_id_ix_066548f").on(table.categoryId.asc().nullsLast()),
48
+ pgPolicy("products_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
49
+ pgPolicy("products_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
50
+ pgPolicy("products_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
51
+ pgPolicy("products_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
52
+ ])).enableRLS();
53
+
54
+ export const users = rebaseSchema.table("users", {
55
+ id: uuid("id").primaryKey().defaultRandom(),
56
+ email: text("email").unique().notNull(),
57
+ displayName: text("display_name"),
58
+ photoURL: text("photo_url"),
59
+ roles: text("roles").array(),
60
+ passwordHash: text("password_hash"),
61
+ emailVerified: boolean("email_verified").default(sql`FALSE`),
62
+ emailVerificationToken: text("email_verification_token"),
63
+ emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
64
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
65
+ createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
66
+ updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
67
+ }, (table) => ([
68
+ pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
69
+ pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
70
+ pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
71
+ pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
72
+ pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
73
+ pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
74
+ pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
75
+ pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
76
+ pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
77
+ pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
78
+ pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
79
+ pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
80
+ ])).enableRLS();
81
+
82
+ export const productsRelations = drizzleRelations(products, ({ one, many }) => ({
83
+ "category": one(categories, {
84
+ fields: [products.categoryId],
85
+ references: [categories.id],
86
+ relationName: "products_categoryId"
87
+ })
88
+ }));
89
+
90
+ export const tables = { categories, orders, products, users };
91
+ export const enums = { ordersStatus, productsStatus };
92
+ export const relations = { productsRelations };
93
+
@@ -12,7 +12,7 @@ This is a **headless** Rebase project: a REST API, auth, storage and realtime ov
12
12
  | `rebase db url` | Print the connection string in use — it pipes straight into `psql` |
13
13
  | `pnpm db:migrate` | Apply the migration files in `backend/drizzle/migrations`. Needs your own PostgreSQL, not the managed development database |
14
14
  | `pnpm schema:introspect` | Existing tables → collection definitions in `config/collections/`. That directory ends headless mode; see rule 1 |
15
- | `pnpm generate:sdk` | Regenerate the typed client from `config/collections/` — so in a headless project it has nothing to read until you introspect |
15
+ | `pnpm generate:sdk` | Regenerate the typed client from `config/collections/` — so in a headless project it has nothing to read until you introspect. Once there are collections, `pnpm dev` does this on start and on every save; the script is for CI and for when `pnpm dev` is not running |
16
16
  | `pnpm build` then `pnpm start` | Build the deployable bundle, then run it |
17
17
  | `pnpm skills:install` | Install the Rebase skills for your assistant |
18
18
  | `pnpm example` | Run `scripts/example.ts` against the running backend — the SDK, end to end |
@@ -16,6 +16,7 @@
16
16
  "@rebasepro/types": "workspace:*"
17
17
  },
18
18
  "devDependencies": {
19
+ "@types/node": "^22.19.4",
19
20
  "typescript": "^6.0.3"
20
21
  },
21
22
  "exports": {
@@ -1,4 +1,4 @@
1
- import { isPublicStoragePath, type StorageAuthorize } from "@rebasepro/types";
1
+ import type { StorageAuthorize } from "@rebasepro/types";
2
2
 
3
3
  /**
4
4
  * Who may do what to files in storage.
@@ -39,7 +39,7 @@ import { isPublicStoragePath, type StorageAuthorize } from "@rebasepro/types";
39
39
  *
40
40
  * ```ts
41
41
  * export const storageAuthorize: StorageAuthorize = async ({ key, user, operation, data }) => {
42
- * if (operation === "read" && isPublicStoragePath(key)) return true;
42
+ * if (operation === "read" && key.startsWith("public/")) return true;
43
43
  * if (!user) return false;
44
44
  * const row = (await data?.collection("attachments").find({
45
45
  * where: { storage_key: ["==", key] }, limit: 1
@@ -55,7 +55,9 @@ import { isPublicStoragePath, type StorageAuthorize } from "@rebasepro/types";
55
55
  */
56
56
  export const storageAuthorize: StorageAuthorize = ({ key, user, operation }) => {
57
57
  // The `public/` prefix is the framework's convention for world-readable objects.
58
- if (operation === "read" && isPublicStoragePath(key)) {
58
+ // `key` is the canonical key the server serves, so a prefix test is exact:
59
+ // `default/public/x` is a private object in a folder named `default`.
60
+ if (operation === "read" && key.startsWith("public/")) {
59
61
  return true;
60
62
  }
61
63
 
@@ -22,6 +22,7 @@
22
22
  "devDependencies": {
23
23
  "@rebasepro/cli": "workspace:*",
24
24
  "@rebasepro/types": "workspace:*",
25
+ "@types/node": "^22.19.4",
25
26
  "tsx": "^4.20.6",
26
27
  "typescript": "^6.0.3"
27
28
  },
@@ -1,3 +1,3 @@
1
1
  # Rebase AI Rules
2
2
  Please refer to and follow the instructions defined in [ai-instructions.md](./ai-instructions.md).
3
- Install the Rebase skills for this assistant: `rebase skills install --agent cursor`.
3
+ The Rebase skills for this assistant live in `.cursor/rules/`. If they are not there yet, install them, with the Rebase MCP server: `rebase skills install --agent cursor --mcp`.
@@ -51,10 +51,12 @@ DATABASE_URL=postgresql://rebase_app:changeme@localhost:5432/rebase?options=-c%2
51
51
  # DB_POOL_CONNECT_TIMEOUT=10000
52
52
 
53
53
  # ── Server ────────────────────────────────────────────────────────────────────
54
- # Used by `rebase start` (production). `rebase dev` IGNORES this and binds a
54
+ # Used by `rebase start`. `rebase dev` IGNORES this and binds a
55
55
  # port derived from the project path, so several projects can run at once —
56
56
  # trust the URL `rebase dev` prints, or pin one with `rebase dev --port 3001`.
57
57
  PORT=3001
58
+ # `rebase start` runs in this mode too, and warns that it is not a production
59
+ # server: set NODE_ENV=production (here or in the shell) for one.
58
60
  NODE_ENV=development
59
61
  # Allow connecting to localhost / 127.0.0.1 in production mode (for local testing of prod builds)
60
62
  # ALLOW_LOCALHOST_IN_PRODUCTION=false
@@ -159,14 +161,20 @@ VITE_API_URL=
159
161
  # In production, "local" means NO file storage: uploads answer
160
162
  # 501 STORAGE_NOT_CONFIGURED instead of landing on a container filesystem that
161
163
  # is erased on the next redeploy. Configure a bucket below to switch it on, or
162
- # set FORCE_LOCAL_STORAGE=true if a durable volume really is mounted at
163
- # STORAGE_PATH (the generated docker-compose.yml does exactly that).
164
+ # set FORCE_LOCAL_STORAGE=true if a durable volume really holds STORAGE_PATH
165
+ # (the generated docker-compose.yml does exactly that). Mount it on the parent
166
+ # of STORAGE_PATH: a named local bucket keeps its files beside it, in
167
+ # <STORAGE_PATH>__<key>, and those belong on the volume too.
164
168
  #
165
169
  # STORAGE_TYPE=local
166
170
 
167
171
  # --- Local filesystem (default — great for dev and single-server deployments) --
168
- # STORAGE_PATH=./uploads
169
- # FORCE_LOCAL_STORAGE=true # Only with a durable volume mounted at STORAGE_PATH
172
+ # Unset, files land in <project>/uploads, under `rebase dev` and in the server
173
+ # `rebase eject` writes alike. A relative path is read from the directory the
174
+ # server runs in: the project root under `rebase dev`, backend/ after
175
+ # `rebase eject`. So ./uploads names two places, and an absolute path one.
176
+ # STORAGE_PATH=/absolute/path/to/uploads
177
+ # FORCE_LOCAL_STORAGE=true # Only with a durable volume holding STORAGE_PATH
170
178
 
171
179
  # --- S3-compatible (AWS S3, Cloudflare R2, MinIO, Hetzner Object Storage, Backblaze B2) ---
172
180
  # STORAGE_TYPE=s3
@@ -222,7 +230,7 @@ VITE_API_URL=
222
230
  # BACKUP_DESTINATION=./backups # local path, or s3://bucket/prefix / gs://bucket/prefix
223
231
  # BACKUP_RETENTION_DAYS=14 # delete backups older than N days (unset/0 ⇒ keep all)
224
232
  # BACKUP_KEEP_MINIMUM=3 # always retain at least N most-recent backups
225
- # PG_DUMP_PATH= # override pg_dump binary (must match server major version)
233
+ # PG_DUMP_PATH= # override pg_dump binary (server's major version or newer)
226
234
  # PG_RESTORE_PATH= # override pg_restore binary
227
235
 
228
236
  # ── Admin Service Key (optional) ──────────────────────────────────────────────
@@ -250,9 +258,11 @@ VITE_API_URL=
250
258
  # unset. To upgrade, change this and restart — your bundle is untouched.
251
259
  REBASE_VERSION=latest
252
260
 
253
- # What boot does about versioned migrations in `backend/migrations`.
254
- # ensure create missing tables and columns from your collections (the default)
255
- # migrate run the migration files, then ensure
256
- # off neither
261
+ # Whether boot provisions the schema your collections describe.
262
+ # ensure create missing tables, columns and enum types, additively (the default)
263
+ # none leave the schema alone
264
+ # Any other value refuses to boot. Boot never runs migration files: the ones
265
+ # `rebase db generate` writes to `backend/drizzle/migrations` are applied with
266
+ # `rebase db migrate`.
257
267
  # REBASE_MIGRATE_ON_BOOT=ensure
258
268
 
@@ -1,3 +1,3 @@
1
1
  # Rebase AI Rules
2
2
  Please refer to and follow the instructions defined in [ai-instructions.md](../ai-instructions.md).
3
- Install the Rebase skills for this assistant: `rebase skills install --agent copilot`.
3
+ The Rebase skills for this assistant live in `.github/instructions/`. If they are not there yet, install them, with the Rebase MCP server: `rebase skills install --agent copilot --mcp`.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "mcpServers": {
3
3
  "rebase": {
4
- "command": "npx",
5
- "args": ["-y", "@rebasepro/mcp"],
4
+ "command": "pnpm",
5
+ "args": ["exec", "rebase-mcp"],
6
6
  "env": {
7
7
  "REBASE_PROJECT_DIR": "."
8
8
  }
@@ -1,3 +1,3 @@
1
1
  # Rebase AI Rules
2
2
  Please refer to and follow the instructions defined in [ai-instructions.md](./ai-instructions.md).
3
- Install the Rebase skills for this assistant: `rebase skills install --agent windsurf`.
3
+ The Rebase skills for this assistant live in `.windsurf/rules/`. If they are not there yet, install them, with the Rebase MCP server: `rebase skills install --agent windsurf --mcp`.
@@ -1,3 +1,3 @@
1
1
  # Rebase AI Rules
2
2
  Please refer to and follow the instructions defined in [ai-instructions.md](ai-instructions.md).
3
- Install the Rebase skills for this assistant: `rebase skills install --agent codex`.
3
+ The Rebase skills for this assistant live in `.agents/skills/`. If they are not there yet, install them, with the Rebase MCP server: `rebase skills install --agent codex --mcp`.
@@ -1,3 +1,3 @@
1
1
  # Rebase AI Rules
2
2
  Please refer to and follow the instructions defined in [ai-instructions.md](./ai-instructions.md).
3
- Install the Rebase skills for this assistant: `rebase skills install --agent claude`.
3
+ The Rebase skills for this assistant live in `.claude/skills/`. If they are not there yet, install them, with the Rebase MCP server: `rebase skills install --agent claude --mcp`.