@metaobjectsdev/runtime-ts 0.9.0 → 0.10.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/drivers/drizzle-driver.d.ts.map +1 -1
- package/dist/drivers/drizzle-driver.js +5 -1
- package/dist/drivers/drizzle-driver.js.map +1 -1
- package/dist/drivers/in-memory-driver.js +2 -0
- package/dist/drivers/in-memory-driver.js.map +1 -1
- package/dist/drivers/kysely-driver.js +1 -0
- package/dist/drivers/kysely-driver.js.map +1 -1
- package/dist/drizzle-fastify/index.d.ts +16 -2
- package/dist/drizzle-fastify/index.d.ts.map +1 -1
- package/dist/drizzle-fastify/index.js +45 -13
- package/dist/drizzle-fastify/index.js.map +1 -1
- package/dist/drizzle-fastify/mount-m2m.d.ts +29 -0
- package/dist/drizzle-fastify/mount-m2m.d.ts.map +1 -0
- package/dist/drizzle-fastify/mount-m2m.js +94 -0
- package/dist/drizzle-fastify/mount-m2m.js.map +1 -0
- package/dist/drizzle-fastify/util.d.ts +2 -0
- package/dist/drizzle-fastify/util.d.ts.map +1 -1
- package/dist/drizzle-fastify/util.js +5 -0
- package/dist/drizzle-fastify/util.js.map +1 -1
- package/dist/extract-object.d.ts.map +1 -1
- package/dist/extract-object.js +14 -5
- package/dist/extract-object.js.map +1 -1
- package/dist/identity-strategy.d.ts.map +1 -1
- package/dist/identity-strategy.js +2 -1
- package/dist/identity-strategy.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/llm-recorder.d.ts +72 -0
- package/dist/llm-recorder.d.ts.map +1 -0
- package/dist/llm-recorder.js +82 -0
- package/dist/llm-recorder.js.map +1 -0
- package/dist/n2m-resolver.d.ts +7 -8
- package/dist/n2m-resolver.d.ts.map +1 -1
- package/dist/n2m-resolver.js +115 -38
- package/dist/n2m-resolver.js.map +1 -1
- package/dist/object-manager.d.ts +4 -0
- package/dist/object-manager.d.ts.map +1 -1
- package/dist/object-manager.js +71 -18
- package/dist/object-manager.js.map +1 -1
- package/dist/persistence-driver.d.ts +3 -0
- package/dist/persistence-driver.d.ts.map +1 -1
- package/dist/query-builder.d.ts +13 -3
- package/dist/query-builder.d.ts.map +1 -1
- package/dist/query-builder.js +19 -10
- package/dist/query-builder.js.map +1 -1
- package/dist/tph.d.ts +14 -0
- package/dist/tph.d.ts.map +1 -0
- package/dist/tph.js +37 -0
- package/dist/tph.js.map +1 -0
- package/dist/type-coercer.d.ts.map +1 -1
- package/dist/type-coercer.js +91 -8
- package/dist/type-coercer.js.map +1 -1
- package/dist/validator-runner.d.ts.map +1 -1
- package/dist/validator-runner.js +24 -3
- package/dist/validator-runner.js.map +1 -1
- package/package.json +62 -51
- package/src/drivers/drizzle-driver.ts +5 -0
- package/src/drivers/in-memory-driver.ts +2 -0
- package/src/drivers/kysely-driver.ts +1 -0
- package/src/drizzle-fastify/index.ts +55 -14
- package/src/drizzle-fastify/mount-m2m.ts +126 -0
- package/src/drizzle-fastify/util.ts +6 -0
- package/src/extract-object.ts +16 -6
- package/src/identity-strategy.ts +2 -1
- package/src/index.ts +7 -0
- package/src/llm-recorder.ts +166 -0
- package/src/n2m-resolver.ts +143 -57
- package/src/object-manager.ts +67 -18
- package/src/persistence-driver.ts +2 -1
- package/src/query-builder.ts +33 -8
- package/src/tph.ts +46 -0
- package/src/type-coercer.ts +94 -8
- package/src/validator-runner.ts +23 -3
|
@@ -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;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"}
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-object.d.ts","sourceRoot":"","sources":["../src/extract-object.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,UAAU,
|
|
1
|
+
{"version":3,"file":"extract-object.d.ts","sourceRoot":"","sources":["../src/extract-object.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,UAAU,EA8BX,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,MAAM,EAWN,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;AA0ED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEjG"}
|
package/dist/extract-object.js
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
// Never throws. Lost/malformed fields are classified in the report, never raised. Opt into
|
|
24
24
|
// strictness with orThrow() (re-exported from @metaobjectsdev/render), which throws a ExtractError
|
|
25
25
|
// iff a required field was lost.
|
|
26
|
-
import { MetaObject, MetaField, MetaRoot, PACKAGE_SEPARATOR, FIELD_SUBTYPE_ENUM, FIELD_SUBTYPE_OBJECT, FIELD_SUBTYPE_STRING,
|
|
27
|
-
import { Format, FieldKind, scalar, enumField, enumArray, object, extractSchema, extract, } from "@metaobjectsdev/render";
|
|
26
|
+
import { MetaObject, MetaField, MetaRoot, PACKAGE_SEPARATOR, FIELD_SUBTYPE_ENUM, FIELD_SUBTYPE_OBJECT, FIELD_SUBTYPE_STRING, FIELD_SUBTYPE_UUID, FIELD_SUBTYPE_DATE, FIELD_SUBTYPE_TIME, FIELD_SUBTYPE_TIMESTAMP, FIELD_SUBTYPE_INT, 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, FIELD_ATTR_XML_TEXT, VALIDATOR_SUBTYPE_NUMERIC, NORMALIZE_DEFAULT, } from "@metaobjectsdev/metadata";
|
|
27
|
+
import { Format, FieldKind, scalar, range, textContentField, enumField, enumArray, object, extractSchema, extract, } from "@metaobjectsdev/render";
|
|
28
28
|
/**
|
|
29
29
|
* Maximum nested-object recursion depth. Mirrors the render OutputFormatRenderer.MAX_NEST_DEPTH
|
|
30
30
|
* (and FR-012) — must stay identical cross-port (Java MetaObjectExtractor.MAX_NEST_DEPTH = 8). At or
|
|
@@ -102,6 +102,18 @@ function fieldSpecFor(field, owner, format, visited, depth) {
|
|
|
102
102
|
// Scalar array: the engine coerces each element; no per-element default fill.
|
|
103
103
|
return scalarArray(name, kind, required);
|
|
104
104
|
}
|
|
105
|
+
// @xmlText: a (non-array) scalar field marked to receive its element's XML text content.
|
|
106
|
+
if (ownAttrString(field, FIELD_ATTR_XML_TEXT) === "true") {
|
|
107
|
+
return textContentField(name, kind, required);
|
|
108
|
+
}
|
|
109
|
+
// Numeric range: source the bound from the field's numeric validator (@min/@max) — the single
|
|
110
|
+
// source of truth — so the engine clamps (lenient) / rejects (strict) out-of-range values.
|
|
111
|
+
if (kind === FieldKind.INT || kind === FieldKind.LONG || kind === FieldKind.DOUBLE) {
|
|
112
|
+
const numeric = field.validators().find((v) => v.subType === VALIDATOR_SUBTYPE_NUMERIC);
|
|
113
|
+
if (numeric !== undefined && (numeric.min !== undefined || numeric.max !== undefined)) {
|
|
114
|
+
return range(name, kind, required, numeric.min ?? null, numeric.max ?? null);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
105
117
|
return scalar(name, kind, required, dv);
|
|
106
118
|
}
|
|
107
119
|
// =============================================================================
|
|
@@ -181,7 +193,6 @@ function scalarArray(name, kind, required) {
|
|
|
181
193
|
function scalarKind(subType) {
|
|
182
194
|
switch (subType) {
|
|
183
195
|
case FIELD_SUBTYPE_STRING:
|
|
184
|
-
case FIELD_SUBTYPE_CLASS:
|
|
185
196
|
case FIELD_SUBTYPE_UUID:
|
|
186
197
|
case FIELD_SUBTYPE_DATE:
|
|
187
198
|
case FIELD_SUBTYPE_TIME:
|
|
@@ -191,8 +202,6 @@ function scalarKind(subType) {
|
|
|
191
202
|
case FIELD_SUBTYPE_DECIMAL:
|
|
192
203
|
return FieldKind.STRING;
|
|
193
204
|
case FIELD_SUBTYPE_INT:
|
|
194
|
-
case FIELD_SUBTYPE_SHORT:
|
|
195
|
-
case FIELD_SUBTYPE_BYTE:
|
|
196
205
|
return FieldKind.INT;
|
|
197
206
|
case FIELD_SUBTYPE_LONG:
|
|
198
207
|
case FIELD_SUBTYPE_CURRENCY:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-object.js","sourceRoot":"","sources":["../src/extract-object.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,iDAAiD;AACjD,EAAE;AACF,qEAAqE;AACrE,2FAA2F;AAC3F,iGAAiG;AACjG,4FAA4F;AAC5F,6FAA6F;AAC7F,oFAAoF;AACpF,iFAAiF;AACjF,EAAE;AACF,2FAA2F;AAC3F,kGAAkG;AAClG,gGAAgG;AAChG,8FAA8F;AAC9F,mGAAmG;AACnG,8FAA8F;AAC9F,kGAAkG;AAClG,EAAE;AACF,gGAAgG;AAChG,6FAA6F;AAC7F,EAAE;AACF,2FAA2F;AAC3F,mGAAmG;AACnG,iCAAiC;AAEjC,OAAO,EACL,UAAU,EACV,SAAS,EACT,QAAQ,EAER,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"extract-object.js","sourceRoot":"","sources":["../src/extract-object.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-strategy.d.ts","sourceRoot":"","sources":["../src/identity-strategy.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAExD,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,
|
|
1
|
+
{"version":3,"file":"identity-strategy.d.ts","sourceRoot":"","sources":["../src/identity-strategy.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAExD,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CA0EnG"}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { TYPE_IDENTITY, IDENTITY_SUBTYPE_PRIMARY, IDENTITY_ATTR_FIELDS, IDENTITY_ATTR_GENERATION, GENERATION_INCREMENT, GENERATION_UUID, GENERATION_ASSIGNED, } from "@metaobjectsdev/metadata";
|
|
7
7
|
import { MetadataError, ValidationError } from "./errors.js";
|
|
8
8
|
export function resolveIdentity(entity, data) {
|
|
9
|
-
|
|
9
|
+
// Effective children so a TPH subtype resolves the inherited primary identity.
|
|
10
|
+
const primary = entity.children().find((c) => c.type === TYPE_IDENTITY && c.subType === IDENTITY_SUBTYPE_PRIMARY);
|
|
10
11
|
if (!primary) {
|
|
11
12
|
throw new MetadataError(`Entity '${entity.name}' has no primary identity — cannot resolve PK for create`, { entity: entity.name });
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-strategy.js","sourceRoot":"","sources":["../src/identity-strategy.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,kHAAkH;AAClH,kFAAkF;AAClF,uCAAuC;AACvC,gFAAgF;AAGhF,OAAO,EACL,aAAa,EAAE,wBAAwB,EACvC,oBAAoB,EAAE,wBAAwB,EAC9C,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,GAC3D,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAM7D,MAAM,UAAU,eAAe,CAAC,MAAgB,EAAE,IAA6B;IAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"identity-strategy.js","sourceRoot":"","sources":["../src/identity-strategy.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ export type { ValidationResult } from "./validator-runner.js";
|
|
|
7
7
|
export type { ValidationFailure } from "./errors.js";
|
|
8
8
|
export { RuntimeError, ValidationError, NotFoundError, ConstraintViolationError, MetadataError, UnsafeNameError, } from "./errors.js";
|
|
9
9
|
export { extractObject, extractSchemaFor, assemble, MAX_NEST_DEPTH, } from "./extract-object.js";
|
|
10
|
+
export { Format } from "@metaobjectsdev/render";
|
|
11
|
+
export { LlmCallDbRecorder, NullRecorder, recordLlmCall, buildLlmCallRow, persistLlmCallRow, truncateRow } from "./llm-recorder.js";
|
|
12
|
+
export type { LlmRecorder, LlmCallRow, LlmCallInput, RecordLlmCallOptions, RecordLlmCallResult, LlmCallDbRecorderOpts } from "./llm-recorder.js";
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErF,YAAY,EACV,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAC/C,WAAW,EAAE,OAAO,EACpB,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EACjD,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,GACvD,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEzE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErF,YAAY,EACV,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAC/C,WAAW,EAAE,OAAO,EACpB,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EACjD,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,GACvD,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEzE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,cAAc,GACf,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpI,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,9 @@ export { RuntimeError, ValidationError, NotFoundError, ConstraintViolationError,
|
|
|
4
4
|
// the metadata-free @metaobjectsdev/render package; this bridge wires it to the Phase A runtime
|
|
5
5
|
// object model (MetaObject.newInstance() + the MetaField SPI), mirroring the JVM `om` siting.
|
|
6
6
|
export { extractObject, extractSchemaFor, assemble, MAX_NEST_DEPTH, } from "./extract-object.js";
|
|
7
|
+
// LLM call recorder seam + parse-then-persist helper.
|
|
8
|
+
// Format is re-exported here so callers of recordLlmCall can import it from a
|
|
9
|
+
// single location rather than reaching into @metaobjectsdev/render directly.
|
|
10
|
+
export { Format } from "@metaobjectsdev/render";
|
|
11
|
+
export { LlmCallDbRecorder, NullRecorder, recordLlmCall, buildLlmCallRow, persistLlmCallRow, truncateRow } from "./llm-recorder.js";
|
|
7
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAgBpD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,kGAAkG;AAClG,gGAAgG;AAChG,8FAA8F;AAC9F,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAgBpD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,kGAAkG;AAClG,gGAAgG;AAChG,8FAA8F;AAC9F,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B,sDAAsD;AACtD,8EAA8E;AAC9E,6EAA6E;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ObjectManager } from "./object-manager.js";
|
|
2
|
+
export type LlmCallRow = Record<string, unknown>;
|
|
3
|
+
export interface LlmRecorder {
|
|
4
|
+
record(call: LlmCallRow): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class NullRecorder implements LlmRecorder {
|
|
7
|
+
record(_call: LlmCallRow): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface LlmCallDbRecorderOpts {
|
|
10
|
+
/** Called when om.create throws. Default: swallow. Telemetry never breaks the app. */
|
|
11
|
+
onError?: (error: unknown) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare class LlmCallDbRecorder implements LlmRecorder {
|
|
14
|
+
private readonly om;
|
|
15
|
+
private readonly entityName;
|
|
16
|
+
private readonly onError;
|
|
17
|
+
constructor(om: ObjectManager, entityName: string, opts?: LlmCallDbRecorderOpts);
|
|
18
|
+
record(call: LlmCallRow): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface LlmCallInput {
|
|
21
|
+
spanId: string;
|
|
22
|
+
traceId: string;
|
|
23
|
+
/** Parent span id; null/absent → this is a root span. */
|
|
24
|
+
parentSpanId?: string;
|
|
25
|
+
/** Logical session/conversation id (gen_ai session grouping). */
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
callType: string;
|
|
28
|
+
/** gen_ai.system — provider name, caller-supplied. */
|
|
29
|
+
system?: string;
|
|
30
|
+
/** ISO 8601 timestamp, supplied by the caller before the LLM call was made. */
|
|
31
|
+
startedAt: string;
|
|
32
|
+
llmRequest: unknown;
|
|
33
|
+
/** Raw response text/body — stored as the raw `llmResponse` column. */
|
|
34
|
+
llmResponseText: string;
|
|
35
|
+
requestModel?: string;
|
|
36
|
+
/** gen_ai.response.model — the model the provider actually used. */
|
|
37
|
+
responseModel?: string;
|
|
38
|
+
inputTokens?: number;
|
|
39
|
+
outputTokens?: number;
|
|
40
|
+
costMinor?: number;
|
|
41
|
+
latencyMs?: number;
|
|
42
|
+
finishReason?: string;
|
|
43
|
+
/** Call outcome, caller-supplied (provider/parse failure → "error"). */
|
|
44
|
+
status: "ok" | "error";
|
|
45
|
+
/** Failure detail (null on success). */
|
|
46
|
+
errorDetail: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface RecordLlmCallOptions {
|
|
49
|
+
recorder: LlmRecorder;
|
|
50
|
+
/** Optional scrub/cap applied immediately before persist (PII/secrets). */
|
|
51
|
+
redact?: (row: LlmCallRow) => LlmCallRow;
|
|
52
|
+
}
|
|
53
|
+
export interface RecordLlmCallResult {
|
|
54
|
+
status: "ok" | "error";
|
|
55
|
+
errorDetail: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** Build the base trace row (envelope + raw llmRequest/llmResponse) — key set
|
|
58
|
+
* is exactly LlmCallBase's fields. Typed voRequest/voResponse are added by the
|
|
59
|
+
* generated typed helper, never here. */
|
|
60
|
+
export declare function buildLlmCallRow(input: LlmCallInput): LlmCallRow;
|
|
61
|
+
/** Shared persist step: redact then record. Used by recordLlmCall AND (later) the
|
|
62
|
+
* generated typed helper, so redaction applies on both paths. */
|
|
63
|
+
export declare function persistLlmCallRow(recorder: LlmRecorder, row: LlmCallRow, opts?: {
|
|
64
|
+
redact?: (row: LlmCallRow) => LlmCallRow;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
/** Cap the raw `llmRequest`/`llmResponse` string columns to `maxChars`.
|
|
67
|
+
* Adopters compose this into a `redact` to bound trace-row size. Only the two
|
|
68
|
+
* raw string columns are touched; all other fields pass through unchanged. */
|
|
69
|
+
export declare function truncateRow(row: LlmCallRow, maxChars: number): LlmCallRow;
|
|
70
|
+
/** Persist one base trace row (envelope + raw I/O). Generic — does not extract. */
|
|
71
|
+
export declare function recordLlmCall(input: LlmCallInput, opts: RecordLlmCallOptions): Promise<RecordLlmCallResult>;
|
|
72
|
+
//# sourceMappingURL=llm-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-recorder.d.ts","sourceRoot":"","sources":["../src/llm-recorder.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAMD,qBAAa,YAAa,YAAW,WAAW;IACxC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AAMD,MAAM,WAAW,qBAAqB;IACpC,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;gBAEvC,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,qBAAqB;IAMzE,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAO9C;AAMD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,wCAAwC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,WAAW,CAAC;IACtB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,UAAU,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;0CAE0C;AAC1C,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,CAqB/D;AAED;kEACkE;AAClE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,WAAW,EACrB,GAAG,EAAE,UAAU,EACf,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,UAAU,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;+EAE+E;AAC/E,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAIzE;AAED,mFAAmF;AACnF,wBAAsB,aAAa,CACjC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAO9B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// LLM call recorder seam + base-row factory + shared persist helper.
|
|
2
|
+
//
|
|
3
|
+
// LlmRecorder is a thin write-side interface for persisting LLM call rows.
|
|
4
|
+
// LlmCallDbRecorder writes via ObjectManager.create (using an entity declared
|
|
5
|
+
// in the caller's own metadata). NullRecorder is the no-op implementation
|
|
6
|
+
// used in unit tests or when tracing is disabled.
|
|
7
|
+
//
|
|
8
|
+
// recordLlmCall is the GENERIC trace path: it builds the base trace row (the
|
|
9
|
+
// envelope + the raw `llmRequest`/`llmResponse` columns declared on the shipped
|
|
10
|
+
// `LlmCallBase`) and persists it. It does NOT parse the response into a typed
|
|
11
|
+
// VO — that extract step + the typed voRequest/voResponse columns live on the
|
|
12
|
+
// generated typed helper (a later layer), so this generic path only ever writes
|
|
13
|
+
// the base field set.
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// NullRecorder — no-op (testing / disabled tracing)
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export class NullRecorder {
|
|
18
|
+
async record(_call) {
|
|
19
|
+
// deliberate no-op
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class LlmCallDbRecorder {
|
|
23
|
+
om;
|
|
24
|
+
entityName;
|
|
25
|
+
onError;
|
|
26
|
+
constructor(om, entityName, opts) {
|
|
27
|
+
this.om = om;
|
|
28
|
+
this.entityName = entityName;
|
|
29
|
+
this.onError = opts?.onError ?? (() => { });
|
|
30
|
+
}
|
|
31
|
+
async record(call) {
|
|
32
|
+
try {
|
|
33
|
+
await this.om.create(this.entityName, call);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
this.onError(err);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Build the base trace row (envelope + raw llmRequest/llmResponse) — key set
|
|
41
|
+
* is exactly LlmCallBase's fields. Typed voRequest/voResponse are added by the
|
|
42
|
+
* generated typed helper, never here. */
|
|
43
|
+
export function buildLlmCallRow(input) {
|
|
44
|
+
return {
|
|
45
|
+
traceId: input.traceId,
|
|
46
|
+
spanId: input.spanId,
|
|
47
|
+
parentSpanId: input.parentSpanId ?? null,
|
|
48
|
+
sessionId: input.sessionId ?? null,
|
|
49
|
+
callType: input.callType,
|
|
50
|
+
system: input.system ?? null,
|
|
51
|
+
requestModel: input.requestModel ?? null,
|
|
52
|
+
responseModel: input.responseModel ?? null,
|
|
53
|
+
inputTokens: input.inputTokens ?? null,
|
|
54
|
+
outputTokens: input.outputTokens ?? null,
|
|
55
|
+
costMinor: input.costMinor ?? null,
|
|
56
|
+
latencyMs: input.latencyMs ?? null,
|
|
57
|
+
finishReason: input.finishReason ?? null,
|
|
58
|
+
status: input.status,
|
|
59
|
+
errorDetail: input.errorDetail,
|
|
60
|
+
startedAt: input.startedAt,
|
|
61
|
+
llmRequest: JSON.stringify(input.llmRequest),
|
|
62
|
+
llmResponse: JSON.stringify(input.llmResponseText),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Shared persist step: redact then record. Used by recordLlmCall AND (later) the
|
|
66
|
+
* generated typed helper, so redaction applies on both paths. */
|
|
67
|
+
export async function persistLlmCallRow(recorder, row, opts) {
|
|
68
|
+
await recorder.record(opts?.redact ? opts.redact(row) : row);
|
|
69
|
+
}
|
|
70
|
+
/** Cap the raw `llmRequest`/`llmResponse` string columns to `maxChars`.
|
|
71
|
+
* Adopters compose this into a `redact` to bound trace-row size. Only the two
|
|
72
|
+
* raw string columns are touched; all other fields pass through unchanged. */
|
|
73
|
+
export function truncateRow(row, maxChars) {
|
|
74
|
+
const cap = (v) => typeof v === "string" && v.length > maxChars ? v.slice(0, maxChars) : v;
|
|
75
|
+
return { ...row, llmRequest: cap(row.llmRequest), llmResponse: cap(row.llmResponse) };
|
|
76
|
+
}
|
|
77
|
+
/** Persist one base trace row (envelope + raw I/O). Generic — does not extract. */
|
|
78
|
+
export async function recordLlmCall(input, opts) {
|
|
79
|
+
await persistLlmCallRow(opts.recorder, buildLlmCallRow(input), opts.redact ? { redact: opts.redact } : undefined);
|
|
80
|
+
return { status: input.status, errorDetail: input.errorDetail };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=llm-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-recorder.js","sourceRoot":"","sources":["../src/llm-recorder.ts"],"names":[],"mappings":"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"}
|
package/dist/n2m-resolver.d.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import type { ColumnNamingStrategy, MetaData } from "@metaobjectsdev/metadata";
|
|
2
2
|
import type { SelectSpec, Row } from "./persistence-driver.js";
|
|
3
3
|
export interface N2mDescriptor {
|
|
4
|
-
/** Entity that declares the relationship (source of the lookup; its PK feeds
|
|
4
|
+
/** Entity that declares the relationship (source of the lookup; its PK feeds sourceField). */
|
|
5
5
|
sourceEntityName: string;
|
|
6
6
|
targetEntityName: string;
|
|
7
7
|
joinEntityName: string;
|
|
8
|
-
/**
|
|
8
|
+
/** Junction FK field holding the source-side key (derived from the junction's references). */
|
|
9
9
|
sourceJoinField: string;
|
|
10
|
-
/**
|
|
10
|
+
/** Junction FK field holding the target-side key (derived from the junction's references). */
|
|
11
11
|
targetJoinField: string;
|
|
12
|
+
/** Undirected self-join: union both junction FK columns at read time. */
|
|
13
|
+
symmetric: boolean;
|
|
12
14
|
}
|
|
13
15
|
export interface N2mLazyOutput {
|
|
14
16
|
joinSpec: SelectSpec;
|
|
15
17
|
/** Caller runs joinSpec, then passes the rows here to build the target spec. */
|
|
16
18
|
makeTargetSpec: (joinRows: Row[]) => SelectSpec | null;
|
|
17
19
|
}
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
makeTargetSpec: (joinRows: Row[]) => SelectSpec | null;
|
|
21
|
-
}
|
|
22
|
-
/** Returns null if the named relationship is not N:M — caller should try resolveRelationDescriptor. */
|
|
20
|
+
export type N2mBatchOutput = N2mLazyOutput;
|
|
21
|
+
/** Returns null if the named relationship is not M:N — caller should try resolveRelationDescriptor. */
|
|
23
22
|
export declare function resolveN2mDescriptor(sourceEntity: MetaData, relationName: string, root: MetaData): N2mDescriptor | null;
|
|
24
23
|
export declare function buildN2mLazySpecs(desc: N2mDescriptor, sourceRecord: Row, root: MetaData, strategy?: ColumnNamingStrategy): N2mLazyOutput;
|
|
25
24
|
export declare function buildN2mBatchSpecs(desc: N2mDescriptor, sourceRecords: Row[], root: MetaData, strategy?: ColumnNamingStrategy): N2mBatchOutput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"n2m-resolver.d.ts","sourceRoot":"","sources":["../src/n2m-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"n2m-resolver.d.ts","sourceRoot":"","sources":["../src/n2m-resolver.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAwB,MAAM,0BAA0B,CAAC;AAYrG,OAAO,KAAK,EAAE,UAAU,EAA+B,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAE5F,MAAM,WAAW,aAAa;IAC5B,8FAA8F;IAC9F,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,eAAe,EAAE,MAAM,CAAC;IACxB,8FAA8F;IAC9F,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,UAAU,CAAC;IACrB,gFAAgF;IAChF,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,UAAU,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC;AAE3C,uGAAuG;AACvG,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,QAAQ,EACtB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,QAAQ,GACb,aAAa,GAAG,IAAI,CAuCtB;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,aAAa,EACnB,YAAY,EAAE,GAAG,EACjB,IAAI,EAAE,QAAQ,EACd,QAAQ,GAAE,oBAAqD,GAC9D,aAAa,CAEf;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,aAAa,EACnB,aAAa,EAAE,GAAG,EAAE,EACpB,IAAI,EAAE,QAAQ,EACd,QAAQ,GAAE,oBAAqD,GAC9D,cAAc,CAEhB;AAyHD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EACvC,QAAQ,GAAE,oBAAqD,GAC9D,MAAM,CAIR"}
|
package/dist/n2m-resolver.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
// Two-stage
|
|
2
|
-
//
|
|
3
|
-
|
|
1
|
+
// Two-stage M:N resolution.
|
|
2
|
+
//
|
|
3
|
+
// A M:N relationship declares only the slim FR-018 vocabulary on the source
|
|
4
|
+
// entity: `@cardinality: "many"` + `@objectRef: <target>` + `@through:
|
|
5
|
+
// <junction>` (plus optional `@sourceRefField` / `@symmetric` for self-joins).
|
|
6
|
+
// It does NOT restate the junction FK columns — those are DERIVED from the
|
|
7
|
+
// junction entity's two `identity.reference` children via the shared
|
|
8
|
+
// `deriveM2MFields` helper (the SSOT for FK direction, the same one the loader
|
|
9
|
+
// validator + every other port use). This kills the pre-FR-018 stopgap that
|
|
10
|
+
// read `@joinEntity` / `@joinFields` off the relationship.
|
|
11
|
+
//
|
|
12
|
+
// Resolution has three modes (see the FR-018 design):
|
|
13
|
+
// 1. Hetero (source != target): junction WHERE sourceField (IN|=) source.pk,
|
|
14
|
+
// collect targetField, then target WHERE pk IN (...).
|
|
15
|
+
// 2. Directed self-join (`@sourceRefField`): identical traversal; the helper
|
|
16
|
+
// has already picked which junction FK is the source side.
|
|
17
|
+
// 3. Symmetric self-join (`@symmetric: true`): single-row storage, union on
|
|
18
|
+
// read — junction WHERE sourceField (IN|=) id OR targetField (IN|=) id;
|
|
19
|
+
// for each row the related id is whichever FK column is NOT the source id.
|
|
20
|
+
import { TYPE_OBJECT, TYPE_FIELD, TYPE_RELATIONSHIP, RELATIONSHIP_ATTR_CARDINALITY, RELATIONSHIP_ATTR_OBJECT_REF, RELATIONSHIP_ATTR_THROUGH, CARDINALITY_MANY, DEFAULT_COLUMN_NAMING_STRATEGY, resolveColumnName, deriveM2MFields, } from "@metaobjectsdev/metadata";
|
|
4
21
|
import { MetadataError } from "./errors.js";
|
|
5
22
|
import { buildSelectSpec, resolvePkFields } from "./query-builder.js";
|
|
6
|
-
/** Returns null if the named relationship is not N
|
|
23
|
+
/** Returns null if the named relationship is not M:N — caller should try resolveRelationDescriptor. */
|
|
7
24
|
export function resolveN2mDescriptor(sourceEntity, relationName, root) {
|
|
8
25
|
for (const child of sourceEntity.ownChildren()) {
|
|
9
26
|
if (child.type !== TYPE_RELATIONSHIP)
|
|
@@ -12,60 +29,77 @@ export function resolveN2mDescriptor(sourceEntity, relationName, root) {
|
|
|
12
29
|
continue;
|
|
13
30
|
if (child.ownAttr(RELATIONSHIP_ATTR_CARDINALITY) !== CARDINALITY_MANY)
|
|
14
31
|
continue;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
if (child.ownAttr(RELATIONSHIP_ATTR_THROUGH) === undefined)
|
|
33
|
+
continue; // 1:N many — not M:N.
|
|
34
|
+
const rel = child;
|
|
35
|
+
const targetEntityName = rel.ownAttr(RELATIONSHIP_ATTR_OBJECT_REF);
|
|
36
|
+
const joinEntityName = rel.ownAttr(RELATIONSHIP_ATTR_THROUGH);
|
|
37
|
+
if (!targetEntityName || !joinEntityName) {
|
|
38
|
+
throw new MetadataError(`M:N relationship '${relationName}' on '${sourceEntity.name}' requires @objectRef + @through`, { entity: sourceEntity.name });
|
|
20
39
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
// Derive the [sourceFK, targetFK] junction columns from the junction's two
|
|
41
|
+
// identity.reference children (handles hetero / directed / symmetric).
|
|
42
|
+
let fields;
|
|
43
|
+
try {
|
|
44
|
+
fields = deriveM2MFields(rel, sourceEntity, root);
|
|
24
45
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
throw new MetadataError(`Join entity '${joinEntityName}' not found`, { entity: sourceEntity.name });
|
|
46
|
+
catch (e) {
|
|
47
|
+
throw new MetadataError(`M:N relationship '${relationName}' on '${sourceEntity.name}': ${e.message}`, { entity: sourceEntity.name });
|
|
28
48
|
}
|
|
29
49
|
return {
|
|
30
50
|
sourceEntityName: sourceEntity.name,
|
|
31
51
|
targetEntityName,
|
|
32
52
|
joinEntityName,
|
|
33
|
-
sourceJoinField:
|
|
34
|
-
targetJoinField:
|
|
53
|
+
sourceJoinField: fields.sourceField,
|
|
54
|
+
targetJoinField: fields.targetField,
|
|
55
|
+
symmetric: rel.symmetric,
|
|
35
56
|
};
|
|
36
57
|
}
|
|
37
58
|
return null;
|
|
38
59
|
}
|
|
39
60
|
export function buildN2mLazySpecs(desc, sourceRecord, root, strategy = DEFAULT_COLUMN_NAMING_STRATEGY) {
|
|
40
|
-
|
|
41
|
-
const targetEntity = mustGetEntity(root, desc.targetEntityName);
|
|
42
|
-
const sourcePkField = resolvePkFields(mustGetEntity(root, desc.sourceEntityName))[0];
|
|
43
|
-
const sourcePkValue = sourceRecord[sourcePkField];
|
|
44
|
-
const joinSpec = buildSelectSpec(joinEntity, { [desc.sourceJoinField]: sourcePkValue }, {}, undefined, strategy);
|
|
45
|
-
const makeTargetSpec = (joinRows) => {
|
|
46
|
-
const targetIds = collectTargetIds(joinRows, desc.targetJoinField, joinEntity, strategy);
|
|
47
|
-
if (targetIds.length === 0)
|
|
48
|
-
return null;
|
|
49
|
-
const targetPkField = resolvePkFields(targetEntity)[0];
|
|
50
|
-
return buildSelectSpec(targetEntity, { [targetPkField]: targetIds }, {}, undefined, strategy);
|
|
51
|
-
};
|
|
52
|
-
return { joinSpec, makeTargetSpec };
|
|
61
|
+
return buildSpecs(desc, sourceRecord, root, strategy);
|
|
53
62
|
}
|
|
54
63
|
export function buildN2mBatchSpecs(desc, sourceRecords, root, strategy = DEFAULT_COLUMN_NAMING_STRATEGY) {
|
|
64
|
+
return buildSpecs(desc, sourceRecords, root, strategy);
|
|
65
|
+
}
|
|
66
|
+
// Single + batch share one code path: a single record is the one-element case.
|
|
67
|
+
function buildSpecs(desc, source, root, strategy) {
|
|
55
68
|
const joinEntity = mustGetEntity(root, desc.joinEntityName);
|
|
56
69
|
const targetEntity = mustGetEntity(root, desc.targetEntityName);
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
70
|
+
const sourceEntity = mustGetEntity(root, desc.sourceEntityName);
|
|
71
|
+
const sourcePkField = resolvePkFields(sourceEntity)[0];
|
|
72
|
+
const records = Array.isArray(source) ? source : [source];
|
|
73
|
+
const sourceIds = collectIds(records, sourcePkField);
|
|
74
|
+
const sourceIdSet = new Set(sourceIds);
|
|
75
|
+
const sourceCol = resolveJoinColumnName(joinEntity, desc.sourceJoinField, strategy);
|
|
76
|
+
const targetCol = resolveJoinColumnName(joinEntity, desc.targetJoinField, strategy);
|
|
77
|
+
// buildSelectSpec compiles a filter on the entity's fields; for symmetric we
|
|
78
|
+
// need an OR across two columns, which the filter DSL can't express. So we
|
|
79
|
+
// build the join spec directly off buildSelectSpec then swap in the where.
|
|
80
|
+
const joinSpec = buildSelectSpec(joinEntity, undefined, {}, undefined, strategy);
|
|
81
|
+
joinSpec.where = desc.symmetric
|
|
82
|
+
? { kind: "or", clauses: [inOrEq(sourceCol, sourceIds), inOrEq(targetCol, sourceIds)] }
|
|
83
|
+
: inOrEq(sourceCol, sourceIds);
|
|
60
84
|
const makeTargetSpec = (joinRows) => {
|
|
61
|
-
const targetIds =
|
|
85
|
+
const targetIds = desc.symmetric
|
|
86
|
+
? collectSymmetricTargetIds(joinRows, sourceCol, targetCol, sourceIdSet)
|
|
87
|
+
: collectColumnIds(joinRows, targetCol);
|
|
62
88
|
if (targetIds.length === 0)
|
|
63
89
|
return null;
|
|
64
90
|
const targetPkField = resolvePkFields(targetEntity)[0];
|
|
65
|
-
|
|
91
|
+
// PK values are always string|number; the IN filter type excludes boolean.
|
|
92
|
+
const ids = targetIds.filter((v) => typeof v !== "boolean");
|
|
93
|
+
return buildSelectSpec(targetEntity, { [targetPkField]: ids }, {}, undefined, strategy);
|
|
66
94
|
};
|
|
67
95
|
return { joinSpec, makeTargetSpec };
|
|
68
96
|
}
|
|
97
|
+
/** `= x` for a single id, `IN (...)` for many (degenerate empty → IN [] = no rows). */
|
|
98
|
+
function inOrEq(column, ids) {
|
|
99
|
+
return ids.length === 1
|
|
100
|
+
? { kind: "eq", column, value: ids[0] }
|
|
101
|
+
: { kind: "in", column, values: ids };
|
|
102
|
+
}
|
|
69
103
|
function mustGetEntity(root, name) {
|
|
70
104
|
const e = root.ownChildren().find((c) => c.type === TYPE_OBJECT && c.name === name);
|
|
71
105
|
if (!e)
|
|
@@ -82,9 +116,8 @@ function collectIds(records, pkField) {
|
|
|
82
116
|
}
|
|
83
117
|
return [...seen];
|
|
84
118
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const dbColumn = resolveJoinColumnName(joinEntity, targetJoinField, strategy);
|
|
119
|
+
/** Distinct values of a raw (column-keyed) join column. */
|
|
120
|
+
function collectColumnIds(joinRows, dbColumn) {
|
|
88
121
|
const seen = new Set();
|
|
89
122
|
for (const r of joinRows) {
|
|
90
123
|
const v = r[dbColumn];
|
|
@@ -94,6 +127,50 @@ function collectTargetIds(joinRows, targetJoinField, joinEntity, strategy) {
|
|
|
94
127
|
}
|
|
95
128
|
return [...seen];
|
|
96
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Symmetric union-on-read: this gathers the set of related ids to FETCH for the
|
|
132
|
+
* second-stage query. For each junction row (a,b) that surfaced via the
|
|
133
|
+
* `a IN ids OR b IN ids` join filter, the related endpoint is the column that is
|
|
134
|
+
* NOT a source — EXCEPT when BOTH columns are sources (two mutually-related
|
|
135
|
+
* records queried in the same batch), where both must be fetched so the
|
|
136
|
+
* eager-include grouping can attach a→b AND b→a. A self-loop row (a==b, a a
|
|
137
|
+
* source) yields a itself.
|
|
138
|
+
*
|
|
139
|
+
* Membership is compared by string-coerced key: the source ids come from the
|
|
140
|
+
* in-process source record (e.g. a JS number) while the junction FK values come
|
|
141
|
+
* straight off the driver, where a BIGINT key arrives as a string. Comparing
|
|
142
|
+
* raw would miss the match (number 1 !== string "1"); string keys bridge it.
|
|
143
|
+
*/
|
|
144
|
+
function collectSymmetricTargetIds(joinRows, sourceCol, targetCol, sourceIds) {
|
|
145
|
+
const sourceKeys = new Set([...sourceIds].map(String));
|
|
146
|
+
const seen = new Set();
|
|
147
|
+
const add = (v) => {
|
|
148
|
+
if (v === null || v === undefined)
|
|
149
|
+
return;
|
|
150
|
+
seen.add(v);
|
|
151
|
+
};
|
|
152
|
+
for (const r of joinRows) {
|
|
153
|
+
const a = r[sourceCol];
|
|
154
|
+
const b = r[targetCol];
|
|
155
|
+
const aIsSource = a !== null && a !== undefined && sourceKeys.has(String(a));
|
|
156
|
+
const bIsSource = b !== null && b !== undefined && sourceKeys.has(String(b));
|
|
157
|
+
// When a is a source, b is its related id; when b is a source, a is its
|
|
158
|
+
// related id. Both can hold at once (mutually-related batch members) — fetch
|
|
159
|
+
// both endpoints then. Falls back to "the non-matched column" when only one
|
|
160
|
+
// side matched (the common single-source-lookup case).
|
|
161
|
+
if (aIsSource)
|
|
162
|
+
add(b);
|
|
163
|
+
if (bIsSource)
|
|
164
|
+
add(a);
|
|
165
|
+
if (!aIsSource && !bIsSource) {
|
|
166
|
+
// Row surfaced via the join filter but neither column string-matches a
|
|
167
|
+
// source id (e.g. number/string skew not bridged here) — keep prior
|
|
168
|
+
// behavior: take whichever side is present.
|
|
169
|
+
add(a ?? b);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...seen];
|
|
173
|
+
}
|
|
97
174
|
export function resolveJoinColumnName(joinEntity, fieldName, strategy = DEFAULT_COLUMN_NAMING_STRATEGY) {
|
|
98
175
|
const field = joinEntity.ownChildren().find((c) => c.type === TYPE_FIELD && c.name === fieldName);
|
|
99
176
|
if (!field)
|