@rebasepro/server-postgres 0.11.1-canary.gfd39654 → 0.12.1-canary.g181d0fe

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 (50) hide show
  1. package/dist/PostgresBootstrapper.d.ts +8 -0
  2. package/dist/auth/services.d.ts +16 -0
  3. package/dist/backup-service-DH9kPg-E.js +8866 -0
  4. package/dist/backup-service-DH9kPg-E.js.map +1 -0
  5. package/dist/collections/buildRegistry.d.ts +1 -1
  6. package/dist/connection-B5Wndbr1.js +196 -0
  7. package/dist/connection-B5Wndbr1.js.map +1 -0
  8. package/dist/ensure-collection-policies-CT-zIUWA.js +57 -0
  9. package/dist/ensure-collection-policies-CT-zIUWA.js.map +1 -0
  10. package/dist/{ensure-collection-tables-DGMYK0fr.js → ensure-collection-tables-Vu-GRELM.js} +83 -6
  11. package/dist/ensure-collection-tables-Vu-GRELM.js.map +1 -0
  12. package/dist/history/HistoryService.d.ts +9 -29
  13. package/dist/index.es.js +773 -9607
  14. package/dist/index.es.js.map +1 -1
  15. package/dist/schema/auth-schema.d.ts +83 -144
  16. package/dist/schema/dynamic-tables.d.ts +1 -1
  17. package/dist/schema/ensure-collection-policies.d.ts +60 -0
  18. package/dist/schema/generate-postgres-ddl-logic.d.ts +43 -1
  19. package/dist/schema/introspect-runtime.d.ts +1 -1
  20. package/dist/services/FetchService.d.ts +36 -1
  21. package/dist/services/row-pipeline.d.ts +3 -1
  22. package/dist/{src-3VmUJ8Xn.js → src-DihrDFuP.js} +354 -165
  23. package/dist/src-DihrDFuP.js.map +1 -0
  24. package/dist/{src-D5xBTl32.js → src-DoU9yPqq.js} +79 -189
  25. package/dist/src-DoU9yPqq.js.map +1 -0
  26. package/dist/utils/drizzle-conditions.d.ts +157 -3
  27. package/dist/utils/pg-error-utils.d.ts +25 -3
  28. package/dist/websocket-BKcGvILX.js +528 -0
  29. package/dist/websocket-BKcGvILX.js.map +1 -0
  30. package/package.json +14 -14
  31. package/src/PostgresAdapter.ts +14 -0
  32. package/src/PostgresBootstrapper.ts +127 -18
  33. package/src/auth/ensure-tables.ts +164 -9
  34. package/src/auth/services.ts +21 -2
  35. package/src/collections/buildRegistry.ts +1 -1
  36. package/src/history/HistoryService.ts +13 -31
  37. package/src/schema/auth-schema.ts +30 -19
  38. package/src/schema/dynamic-tables.ts +1 -1
  39. package/src/schema/ensure-collection-policies.ts +105 -0
  40. package/src/schema/generate-drizzle-schema-logic.ts +17 -5
  41. package/src/schema/generate-postgres-ddl-logic.ts +100 -13
  42. package/src/schema/introspect-runtime.ts +1 -1
  43. package/src/services/FetchService.ts +79 -11
  44. package/src/services/row-pipeline.ts +3 -1
  45. package/src/utils/drizzle-conditions.ts +509 -45
  46. package/src/utils/pg-error-utils.ts +98 -3
  47. package/dist/chunk-DSJWtz9O.js +0 -40
  48. package/dist/ensure-collection-tables-DGMYK0fr.js.map +0 -1
  49. package/dist/src-3VmUJ8Xn.js.map +0 -1
  50. package/dist/src-D5xBTl32.js.map +0 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Applying a bundle's RLS policies to a database at boot, idempotently.
3
+ *
4
+ * ## Why this exists
5
+ *
6
+ * {@link ensureCollectionTables} creates the collection *tables* a managed
7
+ * runtime boots against, but a table with row-level security disabled and no
8
+ * policies is not servable: authenticated requests run as the restricted
9
+ * `rebase_user` role, so a read with no `SELECT` policy returns nothing (a
10
+ * public collection answered 401) and a write with no `INSERT`/`UPDATE` policy
11
+ * is denied. The policies live in the collections' `securityRules`; nothing at
12
+ * boot applied them. `rebase db push` does — but it drives Atlas against a
13
+ * local `DATABASE_URL`, and a managed tenant's database is reachable only from
14
+ * inside the cluster, by the runtime that is already connected to it. So the
15
+ * runtime is the only thing that *can* apply them, and this is where it does.
16
+ *
17
+ * ## Why this is safe to run on every boot
18
+ *
19
+ * Every statement is idempotent: `ENABLE ROW LEVEL SECURITY` is a no-op once
20
+ * enabled, and each policy is a `DROP POLICY IF EXISTS` immediately followed by
21
+ * a `CREATE POLICY`, so re-applying asserts exactly the declared state. It adds
22
+ * and replaces; it never drops data. (It does not *reconcile* — a policy a
23
+ * previous push left behind under an old name is not removed here; that stays a
24
+ * `db push` / `db migrate` concern, alongside destructive schema changes.)
25
+ *
26
+ * Unlike table creation, a failure here is not fatal: RLS stays enabled, so a
27
+ * table whose policies could not be applied fails **closed** (denies) rather
28
+ * than leaking rows. One collection's policy failing (e.g. a rule that
29
+ * references a table a real migration has not created yet) must not crash-loop
30
+ * the whole deployment and take the other collections' working routes down with
31
+ * it. Failures are reported loudly and per-table so the operator can see
32
+ * exactly which collection is not yet servable and why.
33
+ */
34
+ import { type CollectionConfig } from "@rebasepro/types";
35
+ import { type Queryable } from "./ensure-collection-tables";
36
+ export interface PolicyEnsureResult {
37
+ /** `CREATE POLICY` statements that ran successfully. */
38
+ policiesApplied: number;
39
+ /** Tables that had RLS enabled. */
40
+ tablesSecured: number;
41
+ /** Declared tables absent from the database — left to a real migration. */
42
+ skipped: {
43
+ table: string;
44
+ reason: string;
45
+ }[];
46
+ /** Tables whose RLS could not be fully applied (fail closed). */
47
+ failures: {
48
+ table: string;
49
+ error: string;
50
+ }[];
51
+ }
52
+ /**
53
+ * Bring the declared collections' RLS policies up to date. Returns what it did.
54
+ *
55
+ * Only tables that already exist are touched: the boot-time table creator runs
56
+ * first, so anything still missing is a table this additive path is not allowed
57
+ * to create (a junction, or a relation left to a migration). Enabling RLS on a
58
+ * non-existent table would error, so those are recorded as skipped, not failed.
59
+ */
60
+ export declare function ensureCollectionPolicies(client: Queryable, collections: CollectionConfig[], log?: (message: string) => void): Promise<PolicyEnsureResult>;
@@ -1,8 +1,50 @@
1
- import { CollectionConfig, Property } from "@rebasepro/types";
1
+ import { CollectionConfig, Property, SecurityRule } from "@rebasepro/types";
2
2
  export declare const resolveColumnName: (propName: string, prop?: Property | null) => string;
3
3
  export declare const isIdProperty: (propName: string, prop: Property, collection: CollectionConfig) => boolean;
4
+ type ResolveCollection = (slug: string) => CollectionConfig | undefined;
5
+ /**
6
+ * The individual SQL statements a single security rule compiles to: a
7
+ * `DROP POLICY IF EXISTS` / `CREATE POLICY` pair per operation, each a complete
8
+ * statement (terminated by `;`, no trailing newline).
9
+ *
10
+ * This is the primitive the boot-time RLS applier runs one statement at a time
11
+ * (the runtime's DB handle speaks the extended query protocol, which forbids
12
+ * multiple commands in one execute), while `db push` writes the joined string.
13
+ */
14
+ export declare const generatePolicyStatements: (collection: CollectionConfig, rule: SecurityRule, resolveCollection: ResolveCollection) => string[];
4
15
  export declare const getSqlColumnType: (propName: string, prop: Property, collection: CollectionConfig, collections: CollectionConfig[]) => string;
5
16
  export declare const generatePostgresDdl: (collections: CollectionConfig[], options?: {
6
17
  includePolicies?: boolean;
7
18
  }) => Promise<string>;
19
+ /** The RLS statements one declared collection's table needs, ready to run. */
20
+ export interface CollectionPolicyPlan {
21
+ /** The table's schema (e.g. `public`, `rebase`). */
22
+ schema: string;
23
+ /** The bare table name, no schema prefix. */
24
+ table: string;
25
+ /** `schema.table` — matches the keys `readExistingSchema` returns. */
26
+ qualified: string;
27
+ /** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — locked by default. */
28
+ enableRls: string;
29
+ /** `DROP POLICY IF EXISTS` / `CREATE POLICY` statements, in order. */
30
+ policyStatements: string[];
31
+ }
32
+ /**
33
+ * The per-table RLS plan for the *declared* collections, as executable
34
+ * statements — what the managed runtime applies at boot so a freshly
35
+ * provisioned tenant database serves data instead of 401ing every read.
36
+ *
37
+ * Mirrors the non-junction half of {@link generatePostgresPoliciesDdl} exactly
38
+ * (same `generatePolicyStatements`, same enable-RLS, same effective rules), so
39
+ * boot and `db push` produce identical policies from identical collections.
40
+ *
41
+ * Junction tables are deliberately excluded: they are derived from `through`
42
+ * relations, not declared collections, and the boot-time *table* creator
43
+ * (`ensureCollectionTables`) does not create them either — enabling RLS on a
44
+ * table that boot never created would fail. Their RLS stays a `db push` /
45
+ * `db migrate` concern, which is where those tables get created in the first
46
+ * place. `db push` still applies junction policies via the string generator.
47
+ */
48
+ export declare const planCollectionPolicies: (collections: CollectionConfig[]) => CollectionPolicyPlan[];
8
49
  export declare const generatePostgresPoliciesDdl: (collections: CollectionConfig[]) => string;
50
+ export {};
@@ -7,7 +7,7 @@
7
7
  * single config file.
8
8
  *
9
9
  * Distinct from `introspect-db.ts`, which runs the same queries but emits
10
- * TypeScript *source* for a developer to edit and commit (CMS mode). The two
10
+ * TypeScript *source* for a developer to edit and commit (declared collections). The two
11
11
  * share the mapping helpers in `introspect-db-logic.ts` so a table is described
12
12
  * the same way whether it was generated or introspected.
13
13
  */
@@ -20,6 +20,32 @@ export declare class FetchService {
20
20
  * Safely narrows the DrizzleClient union type to access db.query[tableName].
21
21
  */
22
22
  private getQueryBuilder;
23
+ /**
24
+ * The context the condition builder needs to compile a filter key that is
25
+ * not a column name outright.
26
+ *
27
+ * Two such keys. An owning relation's key resolves through the collection's
28
+ * relations to its foreign-key column; a relation whose link lives on the
29
+ * target table or in a junction resolves to a correlated `EXISTS`, which
30
+ * needs the registry to reach that other table and this table's key column
31
+ * to correlate back.
32
+ *
33
+ * Looked up rather than passed: every read path already has the path, only
34
+ * some have the collection, and a path that names no registered collection
35
+ * (a nested/derived one) is not an error here — the builder simply falls
36
+ * back to guessing the default key shapes, and a relation filter it cannot
37
+ * compile stays unresolvable and so fails closed.
38
+ */
39
+ private filterContext;
40
+ /**
41
+ * The table column this collection's rows are keyed by, or `undefined`.
42
+ *
43
+ * `getPrimaryKeys` rather than `requirePrimaryKeys`: a collection with no
44
+ * resolvable key is not an error on the filter path — it only means the
45
+ * relation filters that would correlate on it cannot be compiled, which
46
+ * the builder already handles by failing that field closed.
47
+ */
48
+ private resolveIdColumn;
23
49
  /**
24
50
  * Build filter conditions from FilterValues
25
51
  * Delegates to DrizzleConditionBuilder.buildFilterConditions
@@ -28,6 +54,15 @@ export declare class FetchService {
28
54
  /**
29
55
  * Resolves the correct Drizzle column for sorting.
30
56
  * Automatically maps owning relation property keys to their underlying foreign key column.
57
+ *
58
+ * The relation's own `localKey` is the authority for that foreign key, not
59
+ * `<field>_id`. The default local key comes from `generateForeignKeyName`,
60
+ * which snake-cases *and singularises* — `userProfile` → `user_profile_id`,
61
+ * `users` → `user_id` — and an author can override it outright. A wrong
62
+ * guess resolves to nothing, the caller drops the `ORDER BY`, and the rows
63
+ * come back in whatever order Postgres pleases: paging over that repeats
64
+ * and skips rows rather than erroring. The guesses stay, last, for a
65
+ * caller that hands over no collection to resolve against.
31
66
  */
32
67
  private resolveOrderByField;
33
68
  /**
@@ -35,7 +70,7 @@ export declare class FetchService {
35
70
  * Converts collection relations to a Drizzle-compatible `with` object.
36
71
  *
37
72
  * When `include` is provided, only those relations are loaded.
38
- * When `include` is absent, ALL relations are loaded (CMS path).
73
+ * When `include` is absent, ALL relations are loaded (the admin path).
39
74
  *
40
75
  * Automatically detects many-to-many junction tables and nests
41
76
  * the target relation so actual row data is returned.
@@ -10,7 +10,9 @@ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionReg
10
10
  *
11
11
  * - `"ref"` — a `{ id, path, __type: "relation" }` reference carrying the
12
12
  * target's values. This is what the admin renders.
13
- * - `"inline"` — the target's own columns, flat. This is what REST serves.
13
+ * - `"inline"` — the target's own columns, flat. This is what REST serves, and
14
+ * — since the in-process SDK reads through the same pipeline — what
15
+ * `rebase.data` / `context.data` serve too. A developer never sees a ref.
14
16
  *
15
17
  * They used to be two functions that happened to agree, and the agreement was
16
18
  * not enforced by anything: the row-identity bug had to be fixed five times