@metaobjectsdev/runtime-ts 0.8.1 → 0.9.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/drizzle-fastify/index.d.ts +1 -1
- package/dist/drizzle-fastify/index.d.ts.map +1 -1
- package/dist/drizzle-fastify/index.js +36 -10
- package/dist/drizzle-fastify/index.js.map +1 -1
- package/dist/drizzle-fastify/mount-read-only.d.ts.map +1 -1
- package/dist/drizzle-fastify/mount-read-only.js +11 -5
- package/dist/drizzle-fastify/mount-read-only.js.map +1 -1
- package/dist/drizzle-fastify/util.d.ts +1 -0
- package/dist/drizzle-fastify/util.d.ts.map +1 -1
- package/dist/drizzle-fastify/util.js +23 -0
- package/dist/drizzle-fastify/util.js.map +1 -1
- package/dist/extract-object.d.ts +44 -0
- package/dist/extract-object.d.ts.map +1 -0
- package/dist/extract-object.js +284 -0
- package/dist/extract-object.js.map +1 -0
- package/dist/fastify/index.d.ts +9 -0
- package/dist/fastify/index.d.ts.map +1 -1
- package/dist/fastify/index.js +43 -3
- package/dist/fastify/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/validator-runner.js +2 -2
- package/dist/validator-runner.js.map +1 -1
- package/package.json +3 -2
- package/src/drizzle-fastify/index.ts +33 -10
- package/src/drizzle-fastify/mount-read-only.ts +12 -6
- package/src/drizzle-fastify/util.ts +24 -0
- package/src/extract-object.ts +367 -0
- package/src/fastify/index.ts +55 -3
- package/src/index.ts +10 -0
- package/src/validator-runner.ts +2 -2
|
@@ -2,7 +2,7 @@ import type { FastifyInstance, RouteShorthandOptions } from "fastify";
|
|
|
2
2
|
import type { ZodTypeAny } from "zod";
|
|
3
3
|
import type { FilterAllowlist, SortAllowlist } from "./filter-allowlist.js";
|
|
4
4
|
export type { FilterAllowlist, SortAllowlist } from "./filter-allowlist.js";
|
|
5
|
-
export { isTruthyFlag } from "./util.js";
|
|
5
|
+
export { isTruthyFlag, contractErrorCode } from "./util.js";
|
|
6
6
|
type AnyDrizzle = any;
|
|
7
7
|
type AnyTable = any;
|
|
8
8
|
export type CrudVerb = "list" | "get" | "create" | "update" | "delete";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGtC,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGtC,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAQ5D,KAAK,UAAU,GAAG,GAAG,CAAC;AAEtB,KAAK,QAAQ,GAAG,GAAG,CAAC;AAEpB,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,4EAA4E;IAC5E,KAAK,EAAE,QAAQ,CAAC;IAChB,yEAAyE;IACzE,YAAY,EAAE,UAAU,CAAC;IACzB,yEAAyE;IACzE,YAAY,EAAE,UAAU,CAAC;IACzB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B;;;;OAIG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC/B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,aAAa,CAAC,EAAI,aAAa,CAAC;IAChC,+FAA+F;IAC/F,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;CACjC;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAO7D;AAED,KAAK,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AAMrD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CA8DtD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAcrD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAUxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAmCxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAaxD;AAcD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAGpD;AAED,OAAO,EAAE,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
// POST {path} create, 400 on Zod validation error
|
|
14
14
|
// PATCH {path}/:id update, 400 on validation, 404 if missing
|
|
15
15
|
// DELETE {path}/:id delete, 204 on success, 404 if missing
|
|
16
|
-
import { eq, count, and } from "drizzle-orm";
|
|
16
|
+
import { eq, count, and, asc } from "drizzle-orm";
|
|
17
17
|
import qs from "qs";
|
|
18
18
|
import { parseFilterParams, FilterParseError } from "./filter-parser.js";
|
|
19
|
-
import { isTruthyFlag } from "./util.js";
|
|
20
|
-
export { isTruthyFlag } from "./util.js";
|
|
19
|
+
import { isTruthyFlag, contractErrorCode } from "./util.js";
|
|
20
|
+
export { isTruthyFlag, contractErrorCode } from "./util.js";
|
|
21
21
|
const ALL_VERBS = ["list", "get", "create", "update", "delete"];
|
|
22
22
|
export function mountCrudRoutes(opts) {
|
|
23
23
|
const verbs = new Set(opts.expose ?? ALL_VERBS);
|
|
@@ -62,8 +62,14 @@ export function mountListRoute(opts) {
|
|
|
62
62
|
q = q.where(combinedWhere);
|
|
63
63
|
where = combinedWhere;
|
|
64
64
|
}
|
|
65
|
+
// Default to stable id-ascending order when the caller specifies no
|
|
66
|
+
// sort — the cross-port contract asserts deterministic ordering for
|
|
67
|
+
// pagination + filter scenarios (Postgres otherwise returns rows in an
|
|
68
|
+
// unspecified order).
|
|
65
69
|
if (parsed.orderBy)
|
|
66
70
|
q = q.orderBy(...parsed.orderBy);
|
|
71
|
+
else if (opts.table.id !== undefined)
|
|
72
|
+
q = q.orderBy(asc(opts.table.id));
|
|
67
73
|
if (parsed.limit !== undefined)
|
|
68
74
|
q = q.limit(parsed.limit);
|
|
69
75
|
if (parsed.offset !== undefined)
|
|
@@ -72,25 +78,31 @@ export function mountListRoute(opts) {
|
|
|
72
78
|
else {
|
|
73
79
|
// Legacy path — no allowlists configured. Only limit/offset.
|
|
74
80
|
const { limit, offset } = req.query;
|
|
81
|
+
if (opts.table.id !== undefined)
|
|
82
|
+
q = q.orderBy(asc(opts.table.id));
|
|
75
83
|
if (limit !== undefined)
|
|
76
84
|
q = q.limit(Number(limit));
|
|
77
85
|
if (offset !== undefined)
|
|
78
86
|
q = q.offset(Number(offset));
|
|
79
87
|
}
|
|
80
|
-
|
|
88
|
+
// Await the query directly rather than calling `.all()`: the
|
|
89
|
+
// drizzle-orm node-postgres query builder is thenable but has no `.all()`
|
|
90
|
+
// method (that is a libsql/better-sqlite3-only API). Awaiting works on
|
|
91
|
+
// both dialects and is what makes this helper genuinely Postgres-capable.
|
|
92
|
+
const rows = await q;
|
|
81
93
|
if (!withCount)
|
|
82
94
|
return rows;
|
|
83
95
|
// Count query: same WHERE, no limit/offset/orderBy.
|
|
84
96
|
let cq = opts.db.select({ c: count() }).from(opts.table).$dynamic();
|
|
85
97
|
if (where)
|
|
86
98
|
cq = cq.where(where);
|
|
87
|
-
const countRow = (await cq
|
|
99
|
+
const countRow = (await cq)[0];
|
|
88
100
|
const total = countRow?.c ?? 0;
|
|
89
101
|
return { rows, total };
|
|
90
102
|
}
|
|
91
103
|
catch (err) {
|
|
92
104
|
if (err instanceof FilterParseError) {
|
|
93
|
-
return reply.code(400).send({ error: err.code, ...(err.details ?? {}) });
|
|
105
|
+
return reply.code(400).send({ error: contractErrorCode(err.code), ...(err.details ?? {}) });
|
|
94
106
|
}
|
|
95
107
|
throw err;
|
|
96
108
|
}
|
|
@@ -99,11 +111,15 @@ export function mountListRoute(opts) {
|
|
|
99
111
|
export function mountGetRoute(opts) {
|
|
100
112
|
opts.fastify.get(`${opts.path}/:id`, routeOpts(opts), async (req, reply) => {
|
|
101
113
|
const { id } = req.params;
|
|
102
|
-
|
|
114
|
+
// Await + take the first row rather than `.get()` — `.get()` is a
|
|
115
|
+
// libsql/better-sqlite3-only method; the node-postgres builder is thenable
|
|
116
|
+
// but has no `.get()`. Awaiting works on both dialects.
|
|
117
|
+
const rows = await opts.db
|
|
103
118
|
.select()
|
|
104
119
|
.from(opts.table)
|
|
105
120
|
.where(eq(opts.table.id, parseId(id)))
|
|
106
|
-
.
|
|
121
|
+
.limit(1);
|
|
122
|
+
const row = rows[0];
|
|
107
123
|
return row ?? reply.code(404).send({ error: "not_found" });
|
|
108
124
|
});
|
|
109
125
|
}
|
|
@@ -135,12 +151,22 @@ export function mountUpdateRoute(opts) {
|
|
|
135
151
|
};
|
|
136
152
|
const path = `${opts.path}/:id`;
|
|
137
153
|
const ro = routeOpts(opts);
|
|
154
|
+
// Cross-port REST contract (FR-008): the update verb is reachable via BOTH
|
|
155
|
+
// PATCH and PUT, each routed to the same handler (the Java/Kotlin/C#/Python
|
|
156
|
+
// controllers map both methods to the update path). Mount both by default so
|
|
157
|
+
// the generated TS routes match. `updateMethod` remains an explicit
|
|
158
|
+
// single-verb override for a consumer that wants to restrict the surface.
|
|
138
159
|
// biome-ignore lint/suspicious/noExplicitAny: handler signature is generic
|
|
160
|
+
const h = handler;
|
|
139
161
|
if (opts.updateMethod === "put") {
|
|
140
|
-
opts.fastify.put(path, ro,
|
|
162
|
+
opts.fastify.put(path, ro, h);
|
|
163
|
+
}
|
|
164
|
+
else if (opts.updateMethod === "patch") {
|
|
165
|
+
opts.fastify.patch(path, ro, h);
|
|
141
166
|
}
|
|
142
167
|
else {
|
|
143
|
-
opts.fastify.patch(path, ro,
|
|
168
|
+
opts.fastify.patch(path, ro, h);
|
|
169
|
+
opts.fastify.put(path, ro, h);
|
|
144
170
|
}
|
|
145
171
|
}
|
|
146
172
|
export function mountDeleteRoute(opts) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drizzle-fastify/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,mEAAmE;AACnE,EAAE;AACF,0EAA0E;AAC1E,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,2EAA2E;AAC3E,kDAAkD;AAClD,EAAE;AACF,kDAAkD;AAClD,gDAAgD;AAChD,2DAA2D;AAC3D,iEAAiE;AACjE,8DAA8D;AAI9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drizzle-fastify/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-read-only.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI5E,KAAK,OAAO,GAAG,GAAG,CAAC;AAUnB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AA6DD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"mount-read-only.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI5E,KAAK,OAAO,GAAG,GAAG,CAAC;AAUnB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AA6DD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAsGxE"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sql, eq, and, count } from "drizzle-orm";
|
|
2
2
|
import qs from "qs";
|
|
3
3
|
import { parseFilterParams, FilterParseError } from "./filter-parser.js";
|
|
4
|
-
import { isTruthyFlag } from "./util.js";
|
|
4
|
+
import { isTruthyFlag, contractErrorCode } from "./util.js";
|
|
5
5
|
/**
|
|
6
6
|
* Drizzle v0.45 stores view config under this well-known Symbol.
|
|
7
7
|
* Accessing `view._` on a proxy-wrapped view (empty-column .existing()) throws
|
|
@@ -114,20 +114,24 @@ export function mountReadOnlyCrudRoutes(opts) {
|
|
|
114
114
|
q = q.limit(result.limit);
|
|
115
115
|
if (result.offset !== undefined)
|
|
116
116
|
q = q.offset(result.offset);
|
|
117
|
-
|
|
117
|
+
// Await directly — `.all()` is libsql/better-sqlite3-only; the
|
|
118
|
+
// node-postgres builder is thenable. (The useRawSql branch above keeps
|
|
119
|
+
// db.all(sql.raw(...)) because that is the libsql raw-exec API, only
|
|
120
|
+
// reachable for `.existing()` empty-column views which are sqlite-only.)
|
|
121
|
+
const rows = await q;
|
|
118
122
|
if (!withCount)
|
|
119
123
|
return rows;
|
|
120
124
|
// Count query: same WHERE, no limit/offset/orderBy.
|
|
121
125
|
let cq = db.select({ c: count() }).from(view);
|
|
122
126
|
if (combinedWhere)
|
|
123
127
|
cq = cq.where(combinedWhere);
|
|
124
|
-
const countRow = (await cq
|
|
128
|
+
const countRow = (await cq)[0];
|
|
125
129
|
const total = countRow?.c ?? 0;
|
|
126
130
|
return { rows, total };
|
|
127
131
|
}
|
|
128
132
|
catch (err) {
|
|
129
133
|
if (err instanceof FilterParseError) {
|
|
130
|
-
reply.code(400).send({ error: err.code, message: err.message });
|
|
134
|
+
reply.code(400).send({ error: contractErrorCode(err.code), message: err.message });
|
|
131
135
|
return;
|
|
132
136
|
}
|
|
133
137
|
throw err;
|
|
@@ -148,7 +152,9 @@ export function mountReadOnlyCrudRoutes(opts) {
|
|
|
148
152
|
}
|
|
149
153
|
// biome-ignore lint/suspicious/noExplicitAny: Drizzle table/view column ref
|
|
150
154
|
const colRef = view[idCol];
|
|
151
|
-
|
|
155
|
+
// Await + first row rather than `.get()` (libsql/better-sqlite3-only).
|
|
156
|
+
const rows = await db.select().from(view).where(colRef !== undefined ? eq(colRef, Number(id)) : undefined).limit(1);
|
|
157
|
+
const row = rows[0];
|
|
152
158
|
return row ?? reply.code(404).send({ error: "not_found" });
|
|
153
159
|
});
|
|
154
160
|
// ── Mutations explicitly rejected (405) ───────────────────────────────────
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-read-only.js","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"mount-read-only.js","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIhD"}
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIhD;AAQD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB1D"}
|
|
@@ -9,4 +9,27 @@ export function isTruthyFlag(v) {
|
|
|
9
9
|
return v;
|
|
10
10
|
return String(v) === "1";
|
|
11
11
|
}
|
|
12
|
+
// Cross-port REST contract error envelope (FR-008/009). The internal
|
|
13
|
+
// FilterParseError carries fine-grained dotted codes (filter.unknown_field,
|
|
14
|
+
// sort.unknown_field, ...); the HTTP wire contract that every port's generated
|
|
15
|
+
// API must emit uses the coarser invalid_filter_field / invalid_filter_op /
|
|
16
|
+
// invalid_filter_value / invalid_sort envelope. Map at the HTTP boundary so the
|
|
17
|
+
// internal codes stay stable while the response conforms cross-port.
|
|
18
|
+
export function contractErrorCode(internal) {
|
|
19
|
+
switch (internal) {
|
|
20
|
+
case "filter.unknown_field":
|
|
21
|
+
return "invalid_filter_field";
|
|
22
|
+
case "filter.unsupported_op":
|
|
23
|
+
return "invalid_filter_op";
|
|
24
|
+
case "filter.invalid_value":
|
|
25
|
+
return "invalid_filter_value";
|
|
26
|
+
case "sort.unknown_field":
|
|
27
|
+
case "sort.invalid_order":
|
|
28
|
+
return "invalid_sort";
|
|
29
|
+
default:
|
|
30
|
+
// Implementation-specific guards (nesting depth, in-list size, leading
|
|
31
|
+
// wildcard) are not part of the cross-port contract surface; pass through.
|
|
32
|
+
return internal;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
12
35
|
//# sourceMappingURL=util.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AAC3B,CAAC"}
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AAC3B,CAAC;AAED,qEAAqE;AACrE,4EAA4E;AAC5E,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,qEAAqE;AACrE,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,sBAAsB;YACzB,OAAO,sBAAsB,CAAC;QAChC,KAAK,uBAAuB;YAC1B,OAAO,mBAAmB,CAAC;QAC7B,KAAK,sBAAsB;YACzB,OAAO,sBAAsB,CAAC;QAChC,KAAK,oBAAoB,CAAC;QAC1B,KAAK,oBAAoB;YACvB,OAAO,cAAc,CAAC;QACxB;YACE,uEAAuE;YACvE,2EAA2E;YAC3E,OAAO,QAAQ,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MetaObject } from "@metaobjectsdev/metadata";
|
|
2
|
+
import { Format, type ExtractOptions, type ExtractSchema, type ExtractionResult } from "@metaobjectsdev/render";
|
|
3
|
+
/**
|
|
4
|
+
* Maximum nested-object recursion depth. Mirrors the render OutputFormatRenderer.MAX_NEST_DEPTH
|
|
5
|
+
* (and FR-012) — must stay identical cross-port (Java MetaObjectExtractor.MAX_NEST_DEPTH = 8). At or
|
|
6
|
+
* beyond this depth a nested OBJECT field becomes an opaque STRING leaf instead of recursing.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_NEST_DEPTH = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Extract `text` into a typed object graph described by `mo`.
|
|
11
|
+
*
|
|
12
|
+
* Pipeline: build a ExtractSchema from `mo` (driven entirely by metadata), run the engine to get
|
|
13
|
+
* a forgiving record/array tree + report, then assemble that tree into a populated object graph.
|
|
14
|
+
*
|
|
15
|
+
* @param mo the object describing the expected shape (the single source of truth)
|
|
16
|
+
* @param text the dirty model output
|
|
17
|
+
* @param format JSON (default) or XML — drives the engine's locate/parse
|
|
18
|
+
* @param opts bounded runtime overrides (aliases/normalizers/onField/tolerance)
|
|
19
|
+
* @returns the assembled object (a ValueObject unless a type is bound for the FQN) + report.
|
|
20
|
+
* NEVER throws.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractObject(mo: MetaObject, text: string | null | undefined, format?: Format, opts?: Partial<ExtractOptions> | null): ExtractionResult<object>;
|
|
23
|
+
/**
|
|
24
|
+
* Build a ExtractSchema for `mo` by walking its effective fields and mapping each MetaField to a
|
|
25
|
+
* FieldSpec. Recurses into nested OBJECT fields via `@objectRef`, guarded against cycles/over-depth
|
|
26
|
+
* by an identity visited-set keyed on MetaObject + a depth counter bounded by {@link MAX_NEST_DEPTH}.
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractSchemaFor(mo: MetaObject, format?: Format): ExtractSchema;
|
|
29
|
+
/**
|
|
30
|
+
* Assemble a extracted `Record<string, unknown>` (the engine's forgiving tree) into a populated
|
|
31
|
+
* object graph described by `mo`.
|
|
32
|
+
*
|
|
33
|
+
* `mo.newInstance()` yields the bound type (or a ValueObject) with the back-ref already set. Each
|
|
34
|
+
* field's value is written via the MetaField setValue SPI:
|
|
35
|
+
* • scalar / enum / scalar-array / enum-array → setValue (engine already coerced; arrays arrive
|
|
36
|
+
* as a list);
|
|
37
|
+
* • OBJECT non-array → recursively assemble the child record, then setValue;
|
|
38
|
+
* • OBJECT array → recursively assemble each element record into a list, then setValue.
|
|
39
|
+
*
|
|
40
|
+
* Cycle/depth-guarded identically to extractSchemaFor. NEVER throws on lost/malformed data — those
|
|
41
|
+
* were classified in the report during the engine pass.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assemble(mo: MetaObject, data: Record<string, unknown> | null | undefined): object;
|
|
44
|
+
//# sourceMappingURL=extract-object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-object.d.ts","sourceRoot":"","sources":["../src/extract-object.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,UAAU,EA+BX,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,MAAM,EASN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAMhC;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,GAAE,MAAoB,EAC5B,IAAI,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,GACpC,gBAAgB,CAAC,MAAM,CAAC,CAK1B;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,GAAE,MAAoB,GAAG,aAAa,CAE5F;AA8DD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEjG"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Phase B (metadata-driven extract) runtime entry point — the TS keystone that turns dirty
|
|
2
|
+
// LLM text into a populated, typed object graph.
|
|
3
|
+
//
|
|
4
|
+
// This is the runtime bridge between the two halves of the standard:
|
|
5
|
+
// • the extract ENGINE (@metaobjectsdev/render: ExtractSchema / FieldSpec / extract) — a
|
|
6
|
+
// zero-core-dependency, descriptor-driven module that parses dirty JSON/XML into a forgiving
|
|
7
|
+
// record/array tree + a ExtractionReport. It knows nothing of the runtime object model.
|
|
8
|
+
// • the Phase A runtime OBJECT MODEL (@metaobjectsdev/metadata: MetaObject.newInstance() +
|
|
9
|
+
// the MetaField get/set SPI + @objectRef) — instantiates the right backing type
|
|
10
|
+
// (ValueObject or a registered/bound class) with the correct back-reference.
|
|
11
|
+
//
|
|
12
|
+
// Siting. render stays metadata-free (it is a published render package; coupling it to the
|
|
13
|
+
// metadata model would regress every render consumer). The metadata-driven bridge therefore lives
|
|
14
|
+
// HERE, in @metaobjectsdev/runtime-ts — the lowest package that already depends on metadata and
|
|
15
|
+
// that can also take a one-way dependency on render. This mirrors the JVM layering, where the
|
|
16
|
+
// extract engine sits in the metadata-free `render` module and the runtime extract bridge lives in
|
|
17
|
+
// the `om` runtime module (which depends on BOTH metadata and render). The edges are one-way:
|
|
18
|
+
// runtime-ts → render and runtime-ts → metadata; render depends on neither, so there is no cycle.
|
|
19
|
+
//
|
|
20
|
+
// Reflection-free. Assembly uses MetaObject.newInstance() (the ObjectClassRegistry resolves the
|
|
21
|
+
// bound type, else a ValueObject) and the MetaField setValue SPI — no eval / dynamic import.
|
|
22
|
+
//
|
|
23
|
+
// Never throws. Lost/malformed fields are classified in the report, never raised. Opt into
|
|
24
|
+
// strictness with orThrow() (re-exported from @metaobjectsdev/render), which throws a ExtractError
|
|
25
|
+
// iff a required field was lost.
|
|
26
|
+
import { MetaObject, MetaField, MetaRoot, PACKAGE_SEPARATOR, FIELD_SUBTYPE_ENUM, FIELD_SUBTYPE_OBJECT, FIELD_SUBTYPE_STRING, FIELD_SUBTYPE_CLASS, FIELD_SUBTYPE_UUID, FIELD_SUBTYPE_DATE, FIELD_SUBTYPE_TIME, FIELD_SUBTYPE_TIMESTAMP, FIELD_SUBTYPE_INT, FIELD_SUBTYPE_SHORT, FIELD_SUBTYPE_BYTE, FIELD_SUBTYPE_LONG, FIELD_SUBTYPE_CURRENCY, FIELD_SUBTYPE_DOUBLE, FIELD_SUBTYPE_FLOAT, FIELD_SUBTYPE_DECIMAL, FIELD_SUBTYPE_BOOLEAN, FIELD_ATTR_REQUIRED, FIELD_ATTR_VALUES, FIELD_ATTR_ENUM_ALIAS, FIELD_ATTR_COERCE_DEFAULT, FIELD_ATTR_DEFAULT, FIELD_ATTR_NORMALIZE, FIELD_ATTR_OBJECT_REF, NORMALIZE_DEFAULT, } from "@metaobjectsdev/metadata";
|
|
27
|
+
import { Format, FieldKind, scalar, enumField, enumArray, object, extractSchema, extract, } from "@metaobjectsdev/render";
|
|
28
|
+
/**
|
|
29
|
+
* Maximum nested-object recursion depth. Mirrors the render OutputFormatRenderer.MAX_NEST_DEPTH
|
|
30
|
+
* (and FR-012) — must stay identical cross-port (Java MetaObjectExtractor.MAX_NEST_DEPTH = 8). At or
|
|
31
|
+
* beyond this depth a nested OBJECT field becomes an opaque STRING leaf instead of recursing.
|
|
32
|
+
*/
|
|
33
|
+
export const MAX_NEST_DEPTH = 8;
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// Public API
|
|
36
|
+
// =============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Extract `text` into a typed object graph described by `mo`.
|
|
39
|
+
*
|
|
40
|
+
* Pipeline: build a ExtractSchema from `mo` (driven entirely by metadata), run the engine to get
|
|
41
|
+
* a forgiving record/array tree + report, then assemble that tree into a populated object graph.
|
|
42
|
+
*
|
|
43
|
+
* @param mo the object describing the expected shape (the single source of truth)
|
|
44
|
+
* @param text the dirty model output
|
|
45
|
+
* @param format JSON (default) or XML — drives the engine's locate/parse
|
|
46
|
+
* @param opts bounded runtime overrides (aliases/normalizers/onField/tolerance)
|
|
47
|
+
* @returns the assembled object (a ValueObject unless a type is bound for the FQN) + report.
|
|
48
|
+
* NEVER throws.
|
|
49
|
+
*/
|
|
50
|
+
export function extractObject(mo, text, format = Format.JSON, opts) {
|
|
51
|
+
const schema = extractSchemaFor(mo, format);
|
|
52
|
+
const outcome = extract(text, schema, opts);
|
|
53
|
+
const obj = assemble(mo, outcome.data);
|
|
54
|
+
return { data: obj, report: outcome.report };
|
|
55
|
+
}
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// extractSchemaFor — metadata -> ExtractSchema
|
|
58
|
+
// =============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Build a ExtractSchema for `mo` by walking its effective fields and mapping each MetaField to a
|
|
61
|
+
* FieldSpec. Recurses into nested OBJECT fields via `@objectRef`, guarded against cycles/over-depth
|
|
62
|
+
* by an identity visited-set keyed on MetaObject + a depth counter bounded by {@link MAX_NEST_DEPTH}.
|
|
63
|
+
*/
|
|
64
|
+
export function extractSchemaFor(mo, format = Format.JSON) {
|
|
65
|
+
return extractSchemaForInner(mo, format, new Set(), 0);
|
|
66
|
+
}
|
|
67
|
+
function extractSchemaForInner(mo, format, visited, depth) {
|
|
68
|
+
visited.add(mo);
|
|
69
|
+
const fields = mo.fields().map((f) => fieldSpecFor(f, mo, format, visited, depth));
|
|
70
|
+
visited.delete(mo);
|
|
71
|
+
// rootName is the simple (short) name; the engine's XML locate uses it as the root tag.
|
|
72
|
+
return extractSchema(format, mo.name, fields);
|
|
73
|
+
}
|
|
74
|
+
function fieldSpecFor(field, owner, format, visited, depth) {
|
|
75
|
+
const name = field.name;
|
|
76
|
+
const required = isRequired(field);
|
|
77
|
+
// --- Enum (scalar or array) ----------------------------------------------
|
|
78
|
+
if (field.subType === FIELD_SUBTYPE_ENUM) {
|
|
79
|
+
const values = enumValues(field);
|
|
80
|
+
const aliases = enumAliases(field);
|
|
81
|
+
const cd = ownAttrString(field, FIELD_ATTR_COERCE_DEFAULT);
|
|
82
|
+
const dv = ownAttrString(field, FIELD_ATTR_DEFAULT);
|
|
83
|
+
const normalize = resolveNormalize(field, owner);
|
|
84
|
+
const build = field.isArray === true ? enumArray : enumField;
|
|
85
|
+
return build(name, required, values, aliases, cd, normalize, dv);
|
|
86
|
+
}
|
|
87
|
+
// --- Nested object (single or array) --------------------------------------
|
|
88
|
+
if (field.subType === FIELD_SUBTYPE_OBJECT || field.objectRef !== undefined) {
|
|
89
|
+
const ref = resolveObjectRef(field);
|
|
90
|
+
const cyclicOrDeep = ref === undefined || visited.has(ref) || depth + 1 >= MAX_NEST_DEPTH;
|
|
91
|
+
if (cyclicOrDeep) {
|
|
92
|
+
// Opaque leaf — never recurse into a cycle / past the depth bound.
|
|
93
|
+
return scalar(name, FieldKind.STRING, required);
|
|
94
|
+
}
|
|
95
|
+
const nested = extractSchemaForInner(ref, format, visited, depth + 1);
|
|
96
|
+
return object(name, required, field.isArray === true, nested);
|
|
97
|
+
}
|
|
98
|
+
// --- Scalar (carry generalized @default) ----------------------------------
|
|
99
|
+
const kind = scalarKind(field.subType);
|
|
100
|
+
const dv = ownAttrString(field, FIELD_ATTR_DEFAULT);
|
|
101
|
+
if (field.isArray === true) {
|
|
102
|
+
// Scalar array: the engine coerces each element; no per-element default fill.
|
|
103
|
+
return scalarArray(name, kind, required);
|
|
104
|
+
}
|
|
105
|
+
return scalar(name, kind, required, dv);
|
|
106
|
+
}
|
|
107
|
+
// =============================================================================
|
|
108
|
+
// assemble — extracted record/array tree -> object graph
|
|
109
|
+
// =============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Assemble a extracted `Record<string, unknown>` (the engine's forgiving tree) into a populated
|
|
112
|
+
* object graph described by `mo`.
|
|
113
|
+
*
|
|
114
|
+
* `mo.newInstance()` yields the bound type (or a ValueObject) with the back-ref already set. Each
|
|
115
|
+
* field's value is written via the MetaField setValue SPI:
|
|
116
|
+
* • scalar / enum / scalar-array / enum-array → setValue (engine already coerced; arrays arrive
|
|
117
|
+
* as a list);
|
|
118
|
+
* • OBJECT non-array → recursively assemble the child record, then setValue;
|
|
119
|
+
* • OBJECT array → recursively assemble each element record into a list, then setValue.
|
|
120
|
+
*
|
|
121
|
+
* Cycle/depth-guarded identically to extractSchemaFor. NEVER throws on lost/malformed data — those
|
|
122
|
+
* were classified in the report during the engine pass.
|
|
123
|
+
*/
|
|
124
|
+
export function assemble(mo, data) {
|
|
125
|
+
return assembleInner(mo, data, new Set(), 0);
|
|
126
|
+
}
|
|
127
|
+
function assembleInner(mo, data, visited, depth) {
|
|
128
|
+
const o = mo.newInstance();
|
|
129
|
+
if (data == null) {
|
|
130
|
+
return o;
|
|
131
|
+
}
|
|
132
|
+
visited.add(mo);
|
|
133
|
+
for (const field of mo.fields()) {
|
|
134
|
+
const name = field.name;
|
|
135
|
+
const value = data[name];
|
|
136
|
+
const isObjectField = field.subType === FIELD_SUBTYPE_OBJECT || field.objectRef !== undefined;
|
|
137
|
+
// Enum fields are string-backed scalars/arrays — treat them as scalar assignment.
|
|
138
|
+
if (!isObjectField || field.subType === FIELD_SUBTYPE_ENUM) {
|
|
139
|
+
// scalar / enum / scalar-array / enum-array: the engine already coerced the value.
|
|
140
|
+
if (value !== undefined && value !== null) {
|
|
141
|
+
field.setValue(o, value);
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Nested object — guard cycles/depth (mirror the schema guard).
|
|
146
|
+
const ref = resolveObjectRef(field);
|
|
147
|
+
const cyclicOrDeep = ref === undefined || visited.has(ref) || depth + 1 >= MAX_NEST_DEPTH;
|
|
148
|
+
if (field.isArray === true) {
|
|
149
|
+
// Array-of-objects: map each element record -> assembled child.
|
|
150
|
+
if (Array.isArray(value)) {
|
|
151
|
+
const children = [];
|
|
152
|
+
for (const elem of value) {
|
|
153
|
+
if (!cyclicOrDeep && isPlainObject(elem)) {
|
|
154
|
+
children.push(assembleInner(ref, elem, visited, depth + 1));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
field.setValue(o, children);
|
|
158
|
+
}
|
|
159
|
+
// absent array stays absent (the engine reports it).
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Single object.
|
|
163
|
+
if (!cyclicOrDeep && isPlainObject(value)) {
|
|
164
|
+
const child = assembleInner(ref, value, visited, depth + 1);
|
|
165
|
+
field.setValue(o, child);
|
|
166
|
+
}
|
|
167
|
+
// else leave unset.
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
visited.delete(mo);
|
|
171
|
+
return o;
|
|
172
|
+
}
|
|
173
|
+
// =============================================================================
|
|
174
|
+
// Field-spec helpers (mirror codegen-ts fr010-field-mapping + the Java reader)
|
|
175
|
+
// =============================================================================
|
|
176
|
+
/** A scalar-array FieldSpec: scalar() builds array:false, so set array:true explicitly. */
|
|
177
|
+
function scalarArray(name, kind, required) {
|
|
178
|
+
return { ...scalar(name, kind, required), array: true };
|
|
179
|
+
}
|
|
180
|
+
/** The engine FieldKind for a scalar field subtype. Unknown subtypes fall back to STRING. */
|
|
181
|
+
function scalarKind(subType) {
|
|
182
|
+
switch (subType) {
|
|
183
|
+
case FIELD_SUBTYPE_STRING:
|
|
184
|
+
case FIELD_SUBTYPE_CLASS:
|
|
185
|
+
case FIELD_SUBTYPE_UUID:
|
|
186
|
+
case FIELD_SUBTYPE_DATE:
|
|
187
|
+
case FIELD_SUBTYPE_TIME:
|
|
188
|
+
case FIELD_SUBTYPE_TIMESTAMP:
|
|
189
|
+
// decimal's wire form is an exact decimal STRING (parsing it as a float would be
|
|
190
|
+
// lossy) — matching the codegen sibling (fr010-field-mapping) + C# runtime extract.
|
|
191
|
+
case FIELD_SUBTYPE_DECIMAL:
|
|
192
|
+
return FieldKind.STRING;
|
|
193
|
+
case FIELD_SUBTYPE_INT:
|
|
194
|
+
case FIELD_SUBTYPE_SHORT:
|
|
195
|
+
case FIELD_SUBTYPE_BYTE:
|
|
196
|
+
return FieldKind.INT;
|
|
197
|
+
case FIELD_SUBTYPE_LONG:
|
|
198
|
+
case FIELD_SUBTYPE_CURRENCY:
|
|
199
|
+
return FieldKind.LONG;
|
|
200
|
+
case FIELD_SUBTYPE_DOUBLE:
|
|
201
|
+
case FIELD_SUBTYPE_FLOAT:
|
|
202
|
+
return FieldKind.DOUBLE;
|
|
203
|
+
case FIELD_SUBTYPE_BOOLEAN:
|
|
204
|
+
return FieldKind.BOOLEAN;
|
|
205
|
+
default:
|
|
206
|
+
return FieldKind.STRING;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** True iff `@required` is explicitly true (or the string "true"). */
|
|
210
|
+
function isRequired(field) {
|
|
211
|
+
const v = field.ownAttr(FIELD_ATTR_REQUIRED);
|
|
212
|
+
if (v === true)
|
|
213
|
+
return true;
|
|
214
|
+
return typeof v === "string" && v.toLowerCase() === "true";
|
|
215
|
+
}
|
|
216
|
+
/** The string members of an enum field's `@values` attr (empty when absent). */
|
|
217
|
+
function enumValues(field) {
|
|
218
|
+
const v = field.ownAttr(FIELD_ATTR_VALUES);
|
|
219
|
+
if (Array.isArray(v))
|
|
220
|
+
return v.map((e) => String(e));
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
/** The `@enumAlias` map (an object literal) of an enum field, or {} when absent/empty. */
|
|
224
|
+
function enumAliases(field) {
|
|
225
|
+
const raw = field.ownAttr(FIELD_ATTR_ENUM_ALIAS);
|
|
226
|
+
if (raw == null || typeof raw !== "object" || Array.isArray(raw))
|
|
227
|
+
return {};
|
|
228
|
+
const out = {};
|
|
229
|
+
for (const [k, val] of Object.entries(raw)) {
|
|
230
|
+
if (val != null)
|
|
231
|
+
out[k] = String(val);
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Resolve the enum normalization mode: field-level `@normalize`, else the owning object's
|
|
237
|
+
* `@normalize`, else the global NORMALIZE_DEFAULT ("strip"). Mirrors the cross-port resolution.
|
|
238
|
+
*/
|
|
239
|
+
function resolveNormalize(field, owner) {
|
|
240
|
+
const fieldMode = normalizeAttrOf(field);
|
|
241
|
+
if (fieldMode != null)
|
|
242
|
+
return fieldMode;
|
|
243
|
+
const objMode = owner == null ? null : normalizeAttrOf(owner);
|
|
244
|
+
if (objMode != null)
|
|
245
|
+
return objMode;
|
|
246
|
+
return NORMALIZE_DEFAULT;
|
|
247
|
+
}
|
|
248
|
+
function normalizeAttrOf(node) {
|
|
249
|
+
const v = node.ownAttr(FIELD_ATTR_NORMALIZE);
|
|
250
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
251
|
+
}
|
|
252
|
+
/** The own (non-inherited) string value of an attr on a node, or null when absent/empty. */
|
|
253
|
+
function ownAttrString(node, attr) {
|
|
254
|
+
const v = node.ownAttr(attr);
|
|
255
|
+
if (typeof v === "string")
|
|
256
|
+
return v.length > 0 ? v : null;
|
|
257
|
+
return v == null ? null : String(v);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Resolve a field's `@objectRef` FQN to its MetaObject by walking to the tree root and looking it
|
|
261
|
+
* up. The objectRef may be a bare name or a `pkg::Name` FQN; we try the value as-is, then the short
|
|
262
|
+
* name after the last package separator. Returns undefined when unresolvable (→ opaque-leaf guard).
|
|
263
|
+
*/
|
|
264
|
+
function resolveObjectRef(field) {
|
|
265
|
+
const ref = ownAttrString(field, FIELD_ATTR_OBJECT_REF) ?? field.objectRef;
|
|
266
|
+
if (ref === undefined || ref === null)
|
|
267
|
+
return undefined;
|
|
268
|
+
const root = field.root();
|
|
269
|
+
if (!(root instanceof MetaRoot))
|
|
270
|
+
return undefined;
|
|
271
|
+
const direct = root.findObject(ref);
|
|
272
|
+
if (direct !== undefined)
|
|
273
|
+
return direct;
|
|
274
|
+
const sep = ref.lastIndexOf(PACKAGE_SEPARATOR);
|
|
275
|
+
if (sep >= 0) {
|
|
276
|
+
const short = ref.slice(sep + PACKAGE_SEPARATOR.length);
|
|
277
|
+
return root.findObject(short);
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
function isPlainObject(o) {
|
|
282
|
+
return typeof o === "object" && o !== null && !Array.isArray(o);
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=extract-object.js.map
|