@metaobjectsdev/runtime-ts 1.0.5-rc.1 → 1.0.5-rc.3
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.
|
@@ -21,6 +21,30 @@ export interface M2mRouteOptions {
|
|
|
21
21
|
targetPkColumn?: string;
|
|
22
22
|
/** Undirected self-join: union both junction FK columns on read. */
|
|
23
23
|
symmetric: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The target is a TPH subtype: `targetTable` is its discriminator base's table, and
|
|
26
|
+
* stage 2 keeps only rows whose discriminator `column` (physical name) equals `value`.
|
|
27
|
+
* Absent → every related row is returned, behaviour unchanged.
|
|
28
|
+
*/
|
|
29
|
+
targetDiscriminator?: {
|
|
30
|
+
column: string;
|
|
31
|
+
value: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The SOURCE is a TPH subtype: this route is mounted under the subtype's path segment,
|
|
35
|
+
* but the junction FK addresses the shared base table, so an id belonging to a SIBLING
|
|
36
|
+
* subtype would traverse it successfully and return that sibling's relations. Stage 0
|
|
37
|
+
* reads the source row's discriminator from `table` (the base table the row lives in)
|
|
38
|
+
* and yields an empty list when it is not `value` — the id names no row of THIS
|
|
39
|
+
* subtype, so it has no relations, which is a 200 with `[]` rather than an error.
|
|
40
|
+
* Absent → no source check, behaviour unchanged.
|
|
41
|
+
*/
|
|
42
|
+
sourceDiscriminator?: {
|
|
43
|
+
table: AnyTable;
|
|
44
|
+
pkColumn: string;
|
|
45
|
+
column: string;
|
|
46
|
+
value: string;
|
|
47
|
+
};
|
|
24
48
|
/** Fastify route-level hooks (auth, etc.). */
|
|
25
49
|
routeOptions?: RouteShorthandOptions;
|
|
26
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-m2m.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-m2m.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mount-m2m.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-m2m.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAMtE,KAAK,UAAU,GAAG,GAAG,CAAC;AAEtB,KAAK,QAAQ,GAAG,GAAG,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,wDAAwD;IACxD,aAAa,EAAE,QAAQ,CAAC;IACxB,kDAAkD;IAClD,WAAW,EAAE,QAAQ,CAAC;IACtB,wFAAwF;IACxF,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3F,8CAA8C;IAC9C,YAAY,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAyEzD"}
|
|
@@ -18,7 +18,18 @@
|
|
|
18
18
|
// the source side (via @sourceRefField) → sourceColumn/targetColumn.
|
|
19
19
|
// 3. Symmetric self-join: junction WHERE sourceCol = :id OR targetCol = :id;
|
|
20
20
|
// per row the related id is whichever column is NOT the source id.
|
|
21
|
-
|
|
21
|
+
//
|
|
22
|
+
// A target that is a TPH SUBTYPE lives in its discriminator base's table, and the
|
|
23
|
+
// junction FK can only point at that base table — so it can hold the id of a row of
|
|
24
|
+
// another subtype. `targetDiscriminator` ANDs the subtype predicate into stage 2 so
|
|
25
|
+
// only rows of the declared target come back.
|
|
26
|
+
//
|
|
27
|
+
// The SOURCE has the mirror problem. An M:N declared ON a subtype mounts under that
|
|
28
|
+
// subtype's segment (`/auths/bridge/:id/linkedAuths`), but the junction FK still points
|
|
29
|
+
// at the shared base table, so a SIBLING subtype's id traverses it just as well and the
|
|
30
|
+
// subtype segment in the path would be decorative. `sourceDiscriminator` adds a stage 0
|
|
31
|
+
// that checks the source row's own discriminator first.
|
|
32
|
+
import { and, eq, or, inArray } from "drizzle-orm";
|
|
22
33
|
import { coerceIdForColumn } from "./util.js";
|
|
23
34
|
export function mountM2mRoute(opts) {
|
|
24
35
|
const targetPk = opts.targetPkColumn ?? "id";
|
|
@@ -35,6 +46,24 @@ export function mountM2mRoute(opts) {
|
|
|
35
46
|
if (sourceId === undefined) {
|
|
36
47
|
return reply.code(400).send({ error: "invalid_id" });
|
|
37
48
|
}
|
|
49
|
+
// Stage 0 — the source id must name a row of THIS subtype. Skipping it would let
|
|
50
|
+
// `/auths/bridge/{a Copay's id}/linkedAuths` return that Copay's relations, because
|
|
51
|
+
// the junction FK cannot tell the subtypes apart: it points at the shared base table.
|
|
52
|
+
const srcDisc = opts.sourceDiscriminator;
|
|
53
|
+
if (srcDisc !== undefined) {
|
|
54
|
+
const srcPk = columnRef(srcDisc.table, srcDisc.pkColumn);
|
|
55
|
+
const ownId = coerceIdForColumn(srcPk, id);
|
|
56
|
+
if (ownId === undefined) {
|
|
57
|
+
return reply.code(400).send({ error: "invalid_id" });
|
|
58
|
+
}
|
|
59
|
+
const owner = await opts.db
|
|
60
|
+
.select({ d: columnRef(srcDisc.table, srcDisc.column) })
|
|
61
|
+
.from(srcDisc.table)
|
|
62
|
+
.where(eq(srcPk, ownId))
|
|
63
|
+
.limit(1);
|
|
64
|
+
if (owner.length === 0 || String(owner[0]?.d) !== srcDisc.value)
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
38
67
|
// Stage 1 — junction rows for this source id.
|
|
39
68
|
const joinWhere = opts.symmetric
|
|
40
69
|
? or(eq(srcCol, sourceId), eq(tgtCol, sourceId))
|
|
@@ -64,10 +93,12 @@ export function mountM2mRoute(opts) {
|
|
|
64
93
|
return [];
|
|
65
94
|
// Stage 2 — load the target rows.
|
|
66
95
|
const pkCol = columnRef(opts.targetTable, targetPk);
|
|
96
|
+
const byPk = inArray(pkCol, [...relatedIds]);
|
|
97
|
+
const disc = opts.targetDiscriminator;
|
|
67
98
|
return await opts.db
|
|
68
99
|
.select()
|
|
69
100
|
.from(opts.targetTable)
|
|
70
|
-
.where(
|
|
101
|
+
.where(disc ? and(byPk, eq(columnRef(opts.targetTable, disc.column), disc.value)) : byPk);
|
|
71
102
|
});
|
|
72
103
|
}
|
|
73
104
|
function addId(set, v) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-m2m.js","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-m2m.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,wDAAwD;AACxD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,iFAAiF;AACjF,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,0CAA0C;AAC1C,EAAE;AACF,kDAAkD;AAClD,+EAA+E;AAC/E,oDAAoD;AACpD,+EAA+E;AAC/E,0EAA0E;AAC1E,+EAA+E;AAC/E,wEAAwE;
|
|
1
|
+
{"version":3,"file":"mount-m2m.js","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-m2m.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/runtime-ts",
|
|
3
|
-
"version": "1.0.5-rc.
|
|
3
|
+
"version": "1.0.5-rc.3",
|
|
4
4
|
"description": "Node-side runtime helpers for MetaObjects: Fastify route builders, Drizzle filter/sort integration, Kysely drivers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"access": "public"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@metaobjectsdev/metadata": "1.0.5-rc.
|
|
68
|
-
"@metaobjectsdev/render": "1.0.5-rc.
|
|
67
|
+
"@metaobjectsdev/metadata": "1.0.5-rc.3",
|
|
68
|
+
"@metaobjectsdev/render": "1.0.5-rc.3",
|
|
69
69
|
"qs": "^6.13.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
@@ -18,9 +18,20 @@
|
|
|
18
18
|
// the source side (via @sourceRefField) → sourceColumn/targetColumn.
|
|
19
19
|
// 3. Symmetric self-join: junction WHERE sourceCol = :id OR targetCol = :id;
|
|
20
20
|
// per row the related id is whichever column is NOT the source id.
|
|
21
|
+
//
|
|
22
|
+
// A target that is a TPH SUBTYPE lives in its discriminator base's table, and the
|
|
23
|
+
// junction FK can only point at that base table — so it can hold the id of a row of
|
|
24
|
+
// another subtype. `targetDiscriminator` ANDs the subtype predicate into stage 2 so
|
|
25
|
+
// only rows of the declared target come back.
|
|
26
|
+
//
|
|
27
|
+
// The SOURCE has the mirror problem. An M:N declared ON a subtype mounts under that
|
|
28
|
+
// subtype's segment (`/auths/bridge/:id/linkedAuths`), but the junction FK still points
|
|
29
|
+
// at the shared base table, so a SIBLING subtype's id traverses it just as well and the
|
|
30
|
+
// subtype segment in the path would be decorative. `sourceDiscriminator` adds a stage 0
|
|
31
|
+
// that checks the source row's own discriminator first.
|
|
21
32
|
|
|
22
33
|
import type { FastifyInstance, RouteShorthandOptions } from "fastify";
|
|
23
|
-
import { eq, or, inArray } from "drizzle-orm";
|
|
34
|
+
import { and, eq, or, inArray } from "drizzle-orm";
|
|
24
35
|
import { coerceIdForColumn } from "./util.js";
|
|
25
36
|
|
|
26
37
|
// Loose Drizzle types — the helper works across libsql / better-sqlite3 / pg.
|
|
@@ -49,6 +60,22 @@ export interface M2mRouteOptions {
|
|
|
49
60
|
targetPkColumn?: string;
|
|
50
61
|
/** Undirected self-join: union both junction FK columns on read. */
|
|
51
62
|
symmetric: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The target is a TPH subtype: `targetTable` is its discriminator base's table, and
|
|
65
|
+
* stage 2 keeps only rows whose discriminator `column` (physical name) equals `value`.
|
|
66
|
+
* Absent → every related row is returned, behaviour unchanged.
|
|
67
|
+
*/
|
|
68
|
+
targetDiscriminator?: { column: string; value: string };
|
|
69
|
+
/**
|
|
70
|
+
* The SOURCE is a TPH subtype: this route is mounted under the subtype's path segment,
|
|
71
|
+
* but the junction FK addresses the shared base table, so an id belonging to a SIBLING
|
|
72
|
+
* subtype would traverse it successfully and return that sibling's relations. Stage 0
|
|
73
|
+
* reads the source row's discriminator from `table` (the base table the row lives in)
|
|
74
|
+
* and yields an empty list when it is not `value` — the id names no row of THIS
|
|
75
|
+
* subtype, so it has no relations, which is a 200 with `[]` rather than an error.
|
|
76
|
+
* Absent → no source check, behaviour unchanged.
|
|
77
|
+
*/
|
|
78
|
+
sourceDiscriminator?: { table: AnyTable; pkColumn: string; column: string; value: string };
|
|
52
79
|
/** Fastify route-level hooks (auth, etc.). */
|
|
53
80
|
routeOptions?: RouteShorthandOptions;
|
|
54
81
|
}
|
|
@@ -72,6 +99,24 @@ export function mountM2mRoute(opts: M2mRouteOptions): void {
|
|
|
72
99
|
return reply.code(400).send({ error: "invalid_id" });
|
|
73
100
|
}
|
|
74
101
|
|
|
102
|
+
// Stage 0 — the source id must name a row of THIS subtype. Skipping it would let
|
|
103
|
+
// `/auths/bridge/{a Copay's id}/linkedAuths` return that Copay's relations, because
|
|
104
|
+
// the junction FK cannot tell the subtypes apart: it points at the shared base table.
|
|
105
|
+
const srcDisc = opts.sourceDiscriminator;
|
|
106
|
+
if (srcDisc !== undefined) {
|
|
107
|
+
const srcPk = columnRef(srcDisc.table, srcDisc.pkColumn);
|
|
108
|
+
const ownId = coerceIdForColumn(srcPk, id);
|
|
109
|
+
if (ownId === undefined) {
|
|
110
|
+
return reply.code(400).send({ error: "invalid_id" });
|
|
111
|
+
}
|
|
112
|
+
const owner = await opts.db
|
|
113
|
+
.select({ d: columnRef(srcDisc.table, srcDisc.column) })
|
|
114
|
+
.from(srcDisc.table)
|
|
115
|
+
.where(eq(srcPk, ownId))
|
|
116
|
+
.limit(1);
|
|
117
|
+
if (owner.length === 0 || String(owner[0]?.d) !== srcDisc.value) return [];
|
|
118
|
+
}
|
|
119
|
+
|
|
75
120
|
// Stage 1 — junction rows for this source id.
|
|
76
121
|
const joinWhere = opts.symmetric
|
|
77
122
|
? or(eq(srcCol, sourceId), eq(tgtCol, sourceId))
|
|
@@ -101,10 +146,12 @@ export function mountM2mRoute(opts: M2mRouteOptions): void {
|
|
|
101
146
|
|
|
102
147
|
// Stage 2 — load the target rows.
|
|
103
148
|
const pkCol = columnRef(opts.targetTable, targetPk);
|
|
149
|
+
const byPk = inArray(pkCol, [...relatedIds]);
|
|
150
|
+
const disc = opts.targetDiscriminator;
|
|
104
151
|
return await opts.db
|
|
105
152
|
.select()
|
|
106
153
|
.from(opts.targetTable)
|
|
107
|
-
.where(
|
|
154
|
+
.where(disc ? and(byPk, eq(columnRef(opts.targetTable, disc.column), disc.value)) : byPk);
|
|
108
155
|
});
|
|
109
156
|
}
|
|
110
157
|
|