@rebasepro/server-postgres 0.16.1-canary.gef08a6e → 0.17.0
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.
- package/dist/{backup-service-BL5x6Fj5.js → backup-service-BtgHxfFm.js} +2 -1
- package/dist/{backup-service-BL5x6Fj5.js.map → backup-service-BtgHxfFm.js.map} +1 -1
- package/dist/cli-helpers.d.ts +41 -0
- package/dist/{ensure-collection-tables-DpGX_25A.js → collection-index-DxJBvVTH.js} +240 -1913
- package/dist/collection-index-DxJBvVTH.js.map +1 -0
- package/dist/{ensure-collection-policies-RK8-SFLs.js → ensure-collection-policies-DFpOl8SM.js} +3 -3
- package/dist/{ensure-collection-policies-RK8-SFLs.js.map → ensure-collection-policies-DFpOl8SM.js.map} +1 -1
- package/dist/ensure-collection-tables-DMjOkeRy.js +1952 -0
- package/dist/ensure-collection-tables-DMjOkeRy.js.map +1 -0
- package/dist/index.es.js +10 -9
- package/dist/index.es.js.map +1 -1
- package/dist/{rls-enforcement-da7ekLw-.js → rls-enforcement-CInuYj1-.js} +2 -2
- package/dist/{rls-enforcement-da7ekLw-.js.map → rls-enforcement-CInuYj1-.js.map} +1 -1
- package/dist/schema/collection-index.d.ts +182 -0
- package/dist/schema/introspect-db-inference.d.ts +1 -1
- package/dist/schema/introspect-db-logic.d.ts +4 -4
- package/dist/schema/introspect-db-project.d.ts +2 -2
- package/dist/src-DiDgtX8P.js.map +1 -1
- package/dist/{websocket-6b7Iy4TP.js → websocket-HcyLl1ZM.js} +5 -4
- package/dist/{websocket-6b7Iy4TP.js.map → websocket-HcyLl1ZM.js.map} +1 -1
- package/package.json +6 -6
- package/src/cli-helpers.ts +114 -0
- package/src/cli.ts +22 -0
- package/src/schema/collection-index.ts +427 -0
- package/src/schema/ensure-collection-tables.ts +21 -0
- package/src/schema/generate-postgres-ddl-logic.ts +17 -5
- package/src/schema/introspect-db-inference.ts +1 -1
- package/src/schema/introspect-db-logic.ts +4 -4
- package/src/schema/introspect-db-project.ts +2 -2
- package/src/schema/introspect-db.ts +2 -2
- package/src/services/realtimeService.ts +17 -4
- package/src/websocket.ts +1 -1
- package/dist/ensure-collection-tables-DpGX_25A.js.map +0 -1
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
vectorIndexStatements,
|
|
21
21
|
type VectorIndexPlan
|
|
22
22
|
} from "./vector-index";
|
|
23
|
+
import { buildCollectionIndexSpecs, collectionIndexStatements } from "./collection-index";
|
|
23
24
|
import { REBASE_SCHEMA } from "@rebasepro/types";
|
|
24
25
|
|
|
25
26
|
// --- Helper Functions ---
|
|
@@ -734,6 +735,13 @@ export const generatePostgresDdl = async (
|
|
|
734
735
|
indexStatements.push(`-- No ANN index on "${skip.schema}"."${skip.table}"."${skip.column}": ${skip.reason}`);
|
|
735
736
|
}
|
|
736
737
|
|
|
738
|
+
// The collection's own `indexes:` block. Last of the three index
|
|
739
|
+
// producers, and the only one the developer wrote deliberately
|
|
740
|
+
// rather than getting as a side effect of another feature.
|
|
741
|
+
indexStatements.push(...collectionIndexStatements(
|
|
742
|
+
buildCollectionIndexSpecs(collection, resolveColumnName)
|
|
743
|
+
));
|
|
744
|
+
|
|
737
745
|
// The implicit primary key, for a collection that declares none.
|
|
738
746
|
const hasPk = columns.some(c => c.includes("PRIMARY KEY"));
|
|
739
747
|
if (!hasPk) {
|
|
@@ -762,16 +770,20 @@ export const generatePostgresDdl = async (
|
|
|
762
770
|
}
|
|
763
771
|
}
|
|
764
772
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
773
|
+
// Indexes before foreign keys. Today every FK targets a primary key created
|
|
774
|
+
// inline, so the order is safe by accident; the moment a declared
|
|
775
|
+
// `unique: true` index can back an FK target, a constraint emitted first
|
|
776
|
+
// would reference an index that does not exist yet.
|
|
770
777
|
if (indexStatements.length > 0) {
|
|
771
778
|
ddl += "-- Indexes\n";
|
|
772
779
|
ddl += indexStatements.join("\n") + "\n\n";
|
|
773
780
|
}
|
|
774
781
|
|
|
782
|
+
if (fkStatements.length > 0) {
|
|
783
|
+
ddl += "-- Foreign Key Constraints\n";
|
|
784
|
+
ddl += fkStatements.join("\n") + "\n\n";
|
|
785
|
+
}
|
|
786
|
+
|
|
775
787
|
if (policyStatements.length > 0) {
|
|
776
788
|
ddl += "-- Row Level Security Policies\n";
|
|
777
789
|
ddl += policyStatements.join("");
|
|
@@ -17,7 +17,7 @@ export function inferPropertyFromData(
|
|
|
17
17
|
sampleValues: unknown[],
|
|
18
18
|
isPk: boolean,
|
|
19
19
|
/**
|
|
20
|
-
* False when generating for a project without `@rebasepro/
|
|
20
|
+
* False when generating for a project without `@rebasepro/cms-types`, where
|
|
21
21
|
* `BaseProperty` declares no `admin` field and the block below would not
|
|
22
22
|
* compile. The type-level inferences (`propType`, `url`, `storage`) are
|
|
23
23
|
* unaffected — only the form-widget hints are dropped.
|
|
@@ -594,7 +594,7 @@ export interface GeneratedFile {
|
|
|
594
594
|
*
|
|
595
595
|
* There are two of them and they are not interchangeable:
|
|
596
596
|
*
|
|
597
|
-
* - `admin-types` — `@rebasepro/
|
|
597
|
+
* - `admin-types` — `@rebasepro/cms-types`. Its index side-effect-imports
|
|
598
598
|
* `augment.ts`, so importing it is also what *declares* the `admin` block. Only a
|
|
599
599
|
* project that depends on the package can resolve it.
|
|
600
600
|
* - `common` — `@rebasepro/common`. Same key inference, no admin surface, no React
|
|
@@ -607,7 +607,7 @@ export interface GeneratedFile {
|
|
|
607
607
|
* The last two emit **no admin block, on the collection or on any property**. That is
|
|
608
608
|
* not a downgrade: `@rebasepro/types` declares no `admin` field at all, so the block
|
|
609
609
|
* introspection used to emit was a type error in every headless project it was
|
|
610
|
-
* written into. See `packages/
|
|
610
|
+
* written into. See `packages/cms-types/src/augment.ts`.
|
|
611
611
|
*/
|
|
612
612
|
export type CollectionBuilder = "admin-types" | "common" | "annotation";
|
|
613
613
|
|
|
@@ -616,12 +616,12 @@ export type CollectionBuilder = "admin-types" | "common" | "annotation";
|
|
|
616
616
|
*
|
|
617
617
|
* Written as constants rather than inline in the import templates below because
|
|
618
618
|
* `scripts/headless-guard/check-types.mjs` scans core sources for `from
|
|
619
|
-
* "@rebasepro/
|
|
619
|
+
* "@rebasepro/cms-types"` and cannot tell a real import from one this module
|
|
620
620
|
* *writes*. It is right to be that blunt — the guard's whole value is that it
|
|
621
621
|
* cannot be reasoned around — so the string simply never appears in that shape
|
|
622
622
|
* here. Inlining them back into the templates re-breaks `check:types-headless`.
|
|
623
623
|
*/
|
|
624
|
-
export const ADMIN_TYPES_PACKAGE = "@rebasepro/
|
|
624
|
+
export const ADMIN_TYPES_PACKAGE = "@rebasepro/cms-types";
|
|
625
625
|
export const COMMON_PACKAGE = "@rebasepro/common";
|
|
626
626
|
export const TYPES_PACKAGE = "@rebasepro/types";
|
|
627
627
|
|
|
@@ -49,10 +49,10 @@ function declaredDependencies(manifestPath: string): Set<string> {
|
|
|
49
49
|
* at the moment of generation. The alternatives are all proxies for it: `rebase.json`'s
|
|
50
50
|
* `apps` block says a CMS scaffold declared an admin app, and a `frontend/` directory
|
|
51
51
|
* says one was scaffolded, but neither is what the compiler consults, and either can be
|
|
52
|
-
* true of a project whose `config` package does not depend on `@rebasepro/
|
|
52
|
+
* true of a project whose `config` package does not depend on `@rebasepro/cms-types`.
|
|
53
53
|
*
|
|
54
54
|
* Ambiguity resolves towards the admin panel: a project that declares both packages has
|
|
55
|
-
* a panel, and `@rebasepro/
|
|
55
|
+
* a panel, and `@rebasepro/cms-types` is the flavour that keeps the `admin` block.
|
|
56
56
|
*/
|
|
57
57
|
export function detectCollectionBuilder(outDir: string): CollectionBuilder {
|
|
58
58
|
let dir = path.resolve(outDir);
|
|
@@ -110,12 +110,12 @@ async function main() {
|
|
|
110
110
|
const checkFacts = parseCheckConstraints(metadata.checks);
|
|
111
111
|
|
|
112
112
|
// Which builder the generated files may import — read from the manifests
|
|
113
|
-
// above `outDir`, because a project without `@rebasepro/
|
|
113
|
+
// above `outDir`, because a project without `@rebasepro/cms-types` cannot
|
|
114
114
|
// resolve that import and has no `admin` block to write into either.
|
|
115
115
|
const builder = detectCollectionBuilder(outDir);
|
|
116
116
|
if (builder === "annotation") {
|
|
117
117
|
outWarn(chalk.yellow(
|
|
118
|
-
"⚠ Neither @rebasepro/
|
|
118
|
+
"⚠ Neither @rebasepro/cms-types nor @rebasepro/common is declared above " +
|
|
119
119
|
`${outDir}.`
|
|
120
120
|
));
|
|
121
121
|
outWarn(chalk.gray(
|
|
@@ -462,6 +462,23 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
// The shared rows go before the announcement, not after it. Every
|
|
466
|
+
// `removePresence` below publishes a departure — to local members and
|
|
467
|
+
// over the bus — while `sendPresenceState` answers a *newly arriving*
|
|
468
|
+
// subscriber from this table. Announcing first leaves a window where the
|
|
469
|
+
// table still lists someone who has left: a client hydrating inside it is
|
|
470
|
+
// handed the ghost, and the diff that would have corrected it was
|
|
471
|
+
// broadcast before that client existed, so it holds the ghost until the
|
|
472
|
+
// TTL sweep rather than for the length of one statement.
|
|
473
|
+
//
|
|
474
|
+
// One statement for every channel the client was in, rather than one per
|
|
475
|
+
// channel below — a disconnect is the common case, not a rare one. The
|
|
476
|
+
// subscription and timer cleanup above stays synchronous on purpose: it
|
|
477
|
+
// is what leaks if the database is slow, and it owes nothing to the
|
|
478
|
+
// shared table. With no bus configured this is a no-op that never awaits
|
|
479
|
+
// a query.
|
|
480
|
+
await this.presenceStoreOp(() => this.presenceStore!.removeClient(clientId), "client removal");
|
|
481
|
+
|
|
465
482
|
// Remove from all broadcast channels
|
|
466
483
|
for (const [channel, members] of this.channels.entries()) {
|
|
467
484
|
if (members.has(clientId)) {
|
|
@@ -475,10 +492,6 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
475
492
|
for (const [channel] of this.presence) {
|
|
476
493
|
this.removePresence(clientId, channel, { skipStore: true });
|
|
477
494
|
}
|
|
478
|
-
|
|
479
|
-
// One statement for every channel the client was in, rather than one
|
|
480
|
-
// per channel above — a disconnect is the common case, not a rare one.
|
|
481
|
-
void this.presenceStoreOp(() => this.presenceStore!.removeClient(clientId), "client removal");
|
|
482
495
|
}
|
|
483
496
|
|
|
484
497
|
private async handleMessage(clientId: string, message: WebSocketMessage, authContext?: SubscriptionAuthContext) {
|
package/src/websocket.ts
CHANGED
|
@@ -273,7 +273,7 @@ code } }
|
|
|
273
273
|
verifiedUser = { uid: "service", roles: ["admin"], isAdmin: true };
|
|
274
274
|
} else {
|
|
275
275
|
// Standard JWT path
|
|
276
|
-
const jwtPayload = extractUserFromToken(token);
|
|
276
|
+
const jwtPayload = await extractUserFromToken(token);
|
|
277
277
|
if (jwtPayload) {
|
|
278
278
|
verifiedUser = {
|
|
279
279
|
uid: jwtPayload.uid,
|