@rebasepro/common 0.20.0 → 0.21.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/index.es.js +217 -10
- package/dist/index.es.js.map +1 -1
- package/dist/table-classification.d.ts +0 -13
- package/dist/util/callback-errors.d.ts +46 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/junction-policies.d.ts +17 -0
- package/dist/util/sql-rows.d.ts +27 -0
- package/package.json +3 -3
|
@@ -10,19 +10,6 @@ export type TableCategory = "rebase-internal" | "junction" | "user";
|
|
|
10
10
|
export declare const REBASE_INTERNAL_SCHEMAS: readonly string[];
|
|
11
11
|
/** Table-name prefixes that mark a table as Rebase-internal regardless of schema. */
|
|
12
12
|
export declare const REBASE_INTERNAL_PREFIXES: readonly string[];
|
|
13
|
-
/**
|
|
14
|
-
* Synchronously classify a table based on naming conventions.
|
|
15
|
-
*
|
|
16
|
-
* @param tableName - The unqualified name of the table.
|
|
17
|
-
* @param schemaName - The schema the table belongs to (e.g. `"public"`, `"rebase"`).
|
|
18
|
-
* @returns `"rebase-internal"` when the table belongs to a reserved schema or
|
|
19
|
-
* carries a reserved prefix; `"user"` otherwise.
|
|
20
|
-
*
|
|
21
|
-
* @remarks
|
|
22
|
-
* Junction-table detection requires an async database query and is therefore
|
|
23
|
-
* **not** handled by this function. Use {@link detectJunctionTables} to obtain
|
|
24
|
-
* the set of junction tables, then reclassify as needed.
|
|
25
|
-
*/
|
|
26
13
|
export declare function classifyTable(tableName: string, schemaName: string): TableCategory;
|
|
27
14
|
/**
|
|
28
15
|
* Convenience predicate that checks whether a table is Rebase-internal.
|
|
@@ -75,3 +75,49 @@ export declare function toCallbackError(error: unknown, stage: string, path: str
|
|
|
75
75
|
* @param path The collection path, for `details.path`.
|
|
76
76
|
*/
|
|
77
77
|
export declare function callbackRefusal(stage: string, path: string): RebaseApiError;
|
|
78
|
+
/**
|
|
79
|
+
* The collection a callback tier is about to be handed — or a refusal.
|
|
80
|
+
*
|
|
81
|
+
* Every callback props type declares `collection: CollectionConfig`,
|
|
82
|
+
* non-optional, and the documented global-callback examples dereference it
|
|
83
|
+
* (`if (collection.slug === "audit_log") return;`). The driver resolves that
|
|
84
|
+
* value from the registry, which answers `undefined` for a path it does not
|
|
85
|
+
* know, so the tiers used to receive it through a cast that quietly dropped the
|
|
86
|
+
* `| undefined`. A global `beforeSave` reading `collection.slug` then threw a
|
|
87
|
+
* `TypeError` that `toCallbackError` reported as a 400 `CALLBACK_REJECTED` —
|
|
88
|
+
* the author's own rule blamed for a value the framework failed to supply.
|
|
89
|
+
*
|
|
90
|
+
* Skipping the tier instead is not available. `afterRead` is documented as the
|
|
91
|
+
* place for "security-critical redaction (PII masking, row filtering) — no read
|
|
92
|
+
* path bypasses it", and a tier that silently does not run on the paths the
|
|
93
|
+
* registry cannot resolve is precisely such a bypass.
|
|
94
|
+
*
|
|
95
|
+
* So the contract is the third option: **a callback tier never runs without a
|
|
96
|
+
* collection**, because a path that has none is refused before one can. That
|
|
97
|
+
* costs nothing, because it is already true — every read and every write
|
|
98
|
+
* reaches the database through `getCollectionByPath` in the driver's collection
|
|
99
|
+
* helpers, which raises this same "not found" for the same paths. Asking here
|
|
100
|
+
* only asks earlier, while the answer is still a 404 about the request instead
|
|
101
|
+
* of a `TypeError` attributed to the application's hook.
|
|
102
|
+
*
|
|
103
|
+
* @param collection The collection the driver resolved, if it resolved one.
|
|
104
|
+
* @param path The collection path, for the message and `details`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function requireCallbackCollection<C>(collection: C | undefined, path: string): C;
|
|
107
|
+
/**
|
|
108
|
+
* The client a callback reads as `context.client` — or a refusal naming why
|
|
109
|
+
* there is none.
|
|
110
|
+
*
|
|
111
|
+
* A driver is constructed before the server client exists, and
|
|
112
|
+
* `initializeRebaseBackend` hands it the client afterwards. So a driver can
|
|
113
|
+
* run callbacks without one: constructed on its own, or missed by that
|
|
114
|
+
* injection, which is how a second database's callbacks once ran with
|
|
115
|
+
* `context.client === undefined` while the type said it was there.
|
|
116
|
+
*
|
|
117
|
+
* Called from a getter on the context rather than when the context is built,
|
|
118
|
+
* because most callbacks never touch `client` and must not fail for its
|
|
119
|
+
* absence. The one that does gets this sentence instead of "Cannot read
|
|
120
|
+
* properties of undefined". A 500, not the 400 `toCallbackError` makes of a
|
|
121
|
+
* plain throw: the callback is not at fault, the server's wiring is.
|
|
122
|
+
*/
|
|
123
|
+
export declare function requireCallbackClient<C>(client: C | undefined): C;
|
package/dist/util/index.d.ts
CHANGED
|
@@ -103,6 +103,23 @@ export declare function resolveJunctionSpecs(collections: CollectionConfig[]): M
|
|
|
103
103
|
* payload column anywhere is a second description that can disagree.
|
|
104
104
|
*/
|
|
105
105
|
export declare function getJunctionCollectionConfig(spec: JunctionSpec): CollectionConfig;
|
|
106
|
+
/**
|
|
107
|
+
* The same synthetic collection, reached from a resolved relation rather than
|
|
108
|
+
* from a spec.
|
|
109
|
+
*
|
|
110
|
+
* The spec is built by walking every collection, which the schema planner does
|
|
111
|
+
* once at boot and no request path can afford. A read or a write already holds
|
|
112
|
+
* the relation, and a relation's `through` carries the table, both key columns
|
|
113
|
+
* and the payload — everything the config is made of. One builder underneath
|
|
114
|
+
* both, so the shape the planner emitted columns from is the shape the write
|
|
115
|
+
* path validates a `_pivot` against and the read path strips it with.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getJunctionConfigForRelation(through: {
|
|
118
|
+
table: string;
|
|
119
|
+
sourceColumn: string;
|
|
120
|
+
targetColumn: string;
|
|
121
|
+
properties: Properties;
|
|
122
|
+
}): CollectionConfig;
|
|
106
123
|
/**
|
|
107
124
|
* Whether a parent-rule expression keeps its meaning when moved inside the
|
|
108
125
|
* junction's `EXISTS` subquery — and the re-scoped copy if it does.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the rows out of whatever a SQL driver actually returned.
|
|
3
|
+
*
|
|
4
|
+
* Two shapes reach this repository's stores through the same call, and which
|
|
5
|
+
* one arrives is a property of the driver rather than of the query:
|
|
6
|
+
* node-postgres hands back a `{ rows }` envelope, and the other paths — a bare
|
|
7
|
+
* `executeSql`, Drizzle's `db.execute()` on some builds — hand back an array.
|
|
8
|
+
* `@rebasepro/server`'s `SqlExec` type declares the array, so the envelope is
|
|
9
|
+
* off-contract every time it turns up, and it still turns up.
|
|
10
|
+
*
|
|
11
|
+
* Reading one shape only is how a store silently sees nothing: the rate limiter
|
|
12
|
+
* that did it counted every caller as being on their first request — a limiter
|
|
13
|
+
* that never limits, with no error anywhere to say so.
|
|
14
|
+
*
|
|
15
|
+
* Written inline it came to `result as unknown as { rows?: T[] } | T[]`, five
|
|
16
|
+
* times across three packages. That is the problem restated as an assertion: a
|
|
17
|
+
* union the caller has to re-test at runtime anyway, with `unknown` in the
|
|
18
|
+
* middle only because neither half overlaps what the signature promised. The
|
|
19
|
+
* `Array.isArray` below is that same test done once, and it *narrows* — so
|
|
20
|
+
* these branches are checked rather than claimed.
|
|
21
|
+
*
|
|
22
|
+
* The element type is the one claim that stays a claim: these are rows from
|
|
23
|
+
* hand-written SQL, and nothing at this layer can check a column list.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sqlRows<T = Record<string, unknown>>(result: unknown): T[];
|
|
26
|
+
/** The first row of {@link sqlRows}, or `undefined` when there were none. */
|
|
27
|
+
export declare function firstSqlRow<T = Record<string, unknown>>(result: unknown): T | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/common",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Rebase shared core — collection registry, data driver adapter and fluent query builder. No React dependency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rebase",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"fast-equals": "6.0.2",
|
|
46
46
|
"json-logic-js": "^2.0.5",
|
|
47
|
-
"@rebasepro/types": "0.
|
|
48
|
-
"@rebasepro/utils": "0.
|
|
47
|
+
"@rebasepro/types": "0.21.0",
|
|
48
|
+
"@rebasepro/utils": "0.21.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@jest/globals": "^30.4.1",
|