@objectstack/objectql 9.10.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +224 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -2225,15 +2225,22 @@ declare class ObjectQL implements IDataEngine {
|
|
|
2225
2225
|
* owns the value, not the client.
|
|
2226
2226
|
*
|
|
2227
2227
|
* In the fallback path the next value is `max(existing) + 1`, seeded once per
|
|
2228
|
-
* `object.field
|
|
2229
|
-
* the process, resilient to deletions). `autonumberFormat`
|
|
2230
|
-
*
|
|
2228
|
+
* `object.field.<scope>` from the store then incremented in memory (monotonic
|
|
2229
|
+
* within the process, resilient to deletions). The shared `autonumberFormat`
|
|
2230
|
+
* renderer is honored end-to-end, so date tokens (`AD{YYYYMMDD}{0000}`), field
|
|
2231
|
+
* interpolation (`{island_zone}{000}`) and per-scope reset behave identically
|
|
2232
|
+
* to the SQL driver's persistent sequence (#1603). NOTE: this in-memory seeding
|
|
2233
|
+
* is single-instance.
|
|
2231
2234
|
*/
|
|
2232
2235
|
private applyAutonumbers;
|
|
2233
|
-
/**
|
|
2236
|
+
/**
|
|
2237
|
+
* Seed the autonumber counter from the current max in store, scoped to
|
|
2238
|
+
* `prefix`. With a non-empty prefix (date/field formats) only rows in the
|
|
2239
|
+
* same scope count, and the counter is the digit-run immediately after the
|
|
2240
|
+
* prefix; with an empty prefix (legacy fixed-prefix formats) the last digit
|
|
2241
|
+
* run of the whole value is used, preserving the original behaviour.
|
|
2242
|
+
*/
|
|
2234
2243
|
private seedAutonumber;
|
|
2235
|
-
/** Apply an autonumber format like `CASE-{0000}`; default to the bare number. */
|
|
2236
|
-
private formatAutonumber;
|
|
2237
2244
|
/**
|
|
2238
2245
|
* Register contribution (Manifest)
|
|
2239
2246
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -2225,15 +2225,22 @@ declare class ObjectQL implements IDataEngine {
|
|
|
2225
2225
|
* owns the value, not the client.
|
|
2226
2226
|
*
|
|
2227
2227
|
* In the fallback path the next value is `max(existing) + 1`, seeded once per
|
|
2228
|
-
* `object.field
|
|
2229
|
-
* the process, resilient to deletions). `autonumberFormat`
|
|
2230
|
-
*
|
|
2228
|
+
* `object.field.<scope>` from the store then incremented in memory (monotonic
|
|
2229
|
+
* within the process, resilient to deletions). The shared `autonumberFormat`
|
|
2230
|
+
* renderer is honored end-to-end, so date tokens (`AD{YYYYMMDD}{0000}`), field
|
|
2231
|
+
* interpolation (`{island_zone}{000}`) and per-scope reset behave identically
|
|
2232
|
+
* to the SQL driver's persistent sequence (#1603). NOTE: this in-memory seeding
|
|
2233
|
+
* is single-instance.
|
|
2231
2234
|
*/
|
|
2232
2235
|
private applyAutonumbers;
|
|
2233
|
-
/**
|
|
2236
|
+
/**
|
|
2237
|
+
* Seed the autonumber counter from the current max in store, scoped to
|
|
2238
|
+
* `prefix`. With a non-empty prefix (date/field formats) only rows in the
|
|
2239
|
+
* same scope count, and the counter is the digit-run immediately after the
|
|
2240
|
+
* prefix; with an empty prefix (legacy fixed-prefix formats) the last digit
|
|
2241
|
+
* run of the whole value is used, preserving the original behaviour.
|
|
2242
|
+
*/
|
|
2234
2243
|
private seedAutonumber;
|
|
2235
|
-
/** Apply an autonumber format like `CASE-{0000}`; default to the bare number. */
|
|
2236
|
-
private formatAutonumber;
|
|
2237
2244
|
/**
|
|
2238
2245
|
* Register contribution (Manifest)
|
|
2239
2246
|
*
|
package/dist/index.js
CHANGED
|
@@ -3929,7 +3929,9 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3929
3929
|
["$skip", "skip"],
|
|
3930
3930
|
["$orderby", "orderBy"],
|
|
3931
3931
|
["$select", "select"],
|
|
3932
|
-
["$count", "count"]
|
|
3932
|
+
["$count", "count"],
|
|
3933
|
+
["$search", "search"],
|
|
3934
|
+
["$searchFields", "searchFields"]
|
|
3933
3935
|
]) {
|
|
3934
3936
|
if (options[dollar] != null && options[bare] == null) {
|
|
3935
3937
|
options[bare] = options[dollar];
|
|
@@ -4027,6 +4029,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4027
4029
|
"aggregations",
|
|
4028
4030
|
"groupBy",
|
|
4029
4031
|
"search",
|
|
4032
|
+
"searchFields",
|
|
4030
4033
|
"context",
|
|
4031
4034
|
"cursor"
|
|
4032
4035
|
]);
|
|
@@ -6192,6 +6195,7 @@ var ObjectStackProtocolImplementation = _ObjectStackProtocolImplementation;
|
|
|
6192
6195
|
|
|
6193
6196
|
// src/engine.ts
|
|
6194
6197
|
var import_node_async_hooks = require("async_hooks");
|
|
6198
|
+
var import_data4 = require("@objectstack/spec/data");
|
|
6195
6199
|
var import_kernel6 = require("@objectstack/spec/kernel");
|
|
6196
6200
|
var import_core2 = require("@objectstack/core");
|
|
6197
6201
|
var import_system2 = require("@objectstack/spec/system");
|
|
@@ -6220,6 +6224,118 @@ function collectSecretFields(schema) {
|
|
|
6220
6224
|
|
|
6221
6225
|
// src/engine.ts
|
|
6222
6226
|
var import_shared5 = require("@objectstack/spec/shared");
|
|
6227
|
+
|
|
6228
|
+
// src/search-filter.ts
|
|
6229
|
+
var TEXTUAL_TYPES = /* @__PURE__ */ new Set(["text", "email", "phone", "url", "autonumber", "textarea", "markdown"]);
|
|
6230
|
+
var ENUM_TYPES = /* @__PURE__ */ new Set(["select", "status"]);
|
|
6231
|
+
var EXCLUDED_FIELDS = /* @__PURE__ */ new Set([
|
|
6232
|
+
"id",
|
|
6233
|
+
"_id",
|
|
6234
|
+
"created",
|
|
6235
|
+
"modified",
|
|
6236
|
+
"created_at",
|
|
6237
|
+
"updated_at",
|
|
6238
|
+
"created_by",
|
|
6239
|
+
"updated_by",
|
|
6240
|
+
"owner_id",
|
|
6241
|
+
"organization_id",
|
|
6242
|
+
"space",
|
|
6243
|
+
"company_id"
|
|
6244
|
+
]);
|
|
6245
|
+
var EXCLUDED_TYPES = /* @__PURE__ */ new Set([
|
|
6246
|
+
"json",
|
|
6247
|
+
"object",
|
|
6248
|
+
"grid",
|
|
6249
|
+
"image",
|
|
6250
|
+
"file",
|
|
6251
|
+
"avatar",
|
|
6252
|
+
"vector",
|
|
6253
|
+
"location",
|
|
6254
|
+
"geometry",
|
|
6255
|
+
"secret",
|
|
6256
|
+
"password",
|
|
6257
|
+
"encrypted",
|
|
6258
|
+
"boolean",
|
|
6259
|
+
"lookup",
|
|
6260
|
+
"master_detail"
|
|
6261
|
+
]);
|
|
6262
|
+
function normalizeSearch(raw) {
|
|
6263
|
+
if (raw == null) return { query: "" };
|
|
6264
|
+
if (typeof raw === "string") return { query: raw };
|
|
6265
|
+
if (typeof raw === "object") {
|
|
6266
|
+
const o = raw;
|
|
6267
|
+
const q = typeof o.query === "string" ? o.query : typeof o.q === "string" ? o.q : "";
|
|
6268
|
+
const fields = Array.isArray(o.fields) ? o.fields : void 0;
|
|
6269
|
+
return { query: q, fields };
|
|
6270
|
+
}
|
|
6271
|
+
return { query: "" };
|
|
6272
|
+
}
|
|
6273
|
+
function autoDefaultFields(fields, displayField) {
|
|
6274
|
+
const names = Object.keys(fields).filter((f) => {
|
|
6275
|
+
if (EXCLUDED_FIELDS.has(f)) return false;
|
|
6276
|
+
const meta = fields[f];
|
|
6277
|
+
if (!meta || meta.hidden) return false;
|
|
6278
|
+
const t = meta.type;
|
|
6279
|
+
if (!t) return false;
|
|
6280
|
+
if (EXCLUDED_TYPES.has(t)) return false;
|
|
6281
|
+
return TEXTUAL_TYPES.has(t) || ENUM_TYPES.has(t);
|
|
6282
|
+
});
|
|
6283
|
+
const lead = displayField && fields[displayField] ? displayField : fields.name ? "name" : fields.title ? "title" : void 0;
|
|
6284
|
+
if (!lead) return names;
|
|
6285
|
+
return [lead, ...names.filter((f) => f !== lead)];
|
|
6286
|
+
}
|
|
6287
|
+
function resolveSearchFields(opts) {
|
|
6288
|
+
const all = opts.fields || {};
|
|
6289
|
+
const declared = opts.searchableFields?.filter((f) => all[f]);
|
|
6290
|
+
const allowed = declared && declared.length > 0 ? declared : autoDefaultFields(all, opts.displayField);
|
|
6291
|
+
const requested = typeof opts.requestedFields === "string" ? opts.requestedFields.split(",").map((f) => f.trim()).filter(Boolean) : opts.requestedFields;
|
|
6292
|
+
if (requested && requested.length > 0) {
|
|
6293
|
+
const allowSet = new Set(allowed);
|
|
6294
|
+
const validated = requested.filter((f) => allowSet.has(f));
|
|
6295
|
+
if (validated.length > 0) return validated;
|
|
6296
|
+
}
|
|
6297
|
+
return allowed;
|
|
6298
|
+
}
|
|
6299
|
+
function optionValuesMatching(meta, term) {
|
|
6300
|
+
if (!Array.isArray(meta.options)) return [];
|
|
6301
|
+
const lc = term.toLowerCase();
|
|
6302
|
+
const out = [];
|
|
6303
|
+
for (const opt of meta.options) {
|
|
6304
|
+
if (opt == null) continue;
|
|
6305
|
+
if (typeof opt === "string") {
|
|
6306
|
+
if (opt.toLowerCase().includes(lc)) out.push(opt);
|
|
6307
|
+
continue;
|
|
6308
|
+
}
|
|
6309
|
+
const label = String(opt.label ?? opt.value ?? "");
|
|
6310
|
+
if (label.toLowerCase().includes(lc)) out.push(opt.value);
|
|
6311
|
+
}
|
|
6312
|
+
return out;
|
|
6313
|
+
}
|
|
6314
|
+
function fieldClausesForTerm(field, term, meta) {
|
|
6315
|
+
if (ENUM_TYPES.has(meta?.type ?? "")) {
|
|
6316
|
+
const values = optionValuesMatching(meta, term);
|
|
6317
|
+
if (values.length > 0) return [{ [field]: { $in: values } }];
|
|
6318
|
+
return [{ [field]: { $contains: term } }];
|
|
6319
|
+
}
|
|
6320
|
+
return [{ [field]: { $contains: term } }];
|
|
6321
|
+
}
|
|
6322
|
+
function expandSearchToFilter(raw, opts) {
|
|
6323
|
+
const { query, fields: requested } = normalizeSearch(raw);
|
|
6324
|
+
if (!query || !query.trim()) return null;
|
|
6325
|
+
const searchFields = resolveSearchFields({
|
|
6326
|
+
...opts,
|
|
6327
|
+
requestedFields: requested ?? opts.requestedFields
|
|
6328
|
+
});
|
|
6329
|
+
if (searchFields.length === 0) return null;
|
|
6330
|
+
const terms = query.trim().split(/\s+/).filter(Boolean);
|
|
6331
|
+
const andClauses = terms.map((term) => ({
|
|
6332
|
+
$or: searchFields.flatMap((f) => fieldClausesForTerm(f, term, opts.fields[f] || {}))
|
|
6333
|
+
}));
|
|
6334
|
+
if (andClauses.length === 0) return null;
|
|
6335
|
+
return andClauses.length === 1 ? andClauses[0] : { $and: andClauses };
|
|
6336
|
+
}
|
|
6337
|
+
|
|
6338
|
+
// src/engine.ts
|
|
6223
6339
|
var import_formula4 = require("@objectstack/formula");
|
|
6224
6340
|
var import_spec = require("@objectstack/spec");
|
|
6225
6341
|
|
|
@@ -7617,8 +7733,9 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7617
7733
|
const tx = execCtx?.transaction !== void 0 ? execCtx.transaction : this.txStore.getStore()?.transaction;
|
|
7618
7734
|
const hasTx = tx !== void 0;
|
|
7619
7735
|
const hasTenant = execCtx?.tenantId !== void 0;
|
|
7736
|
+
const hasTz = execCtx?.timezone !== void 0;
|
|
7620
7737
|
const isSystem = execCtx?.isSystem === true;
|
|
7621
|
-
if (!hasTx && !hasTenant && !isSystem) return base;
|
|
7738
|
+
if (!hasTx && !hasTenant && !isSystem && !hasTz) return base;
|
|
7622
7739
|
const opts = base && typeof base === "object" ? { ...base } : {};
|
|
7623
7740
|
if (hasTx && opts.transaction === void 0) {
|
|
7624
7741
|
opts.transaction = tx;
|
|
@@ -7626,6 +7743,9 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7626
7743
|
if (hasTenant && opts.tenantId === void 0) {
|
|
7627
7744
|
opts.tenantId = execCtx.tenantId;
|
|
7628
7745
|
}
|
|
7746
|
+
if (hasTz && opts.timezone === void 0) {
|
|
7747
|
+
opts.timezone = execCtx.timezone;
|
|
7748
|
+
}
|
|
7629
7749
|
if (isSystem && opts.bypassTenantAudit === void 0) {
|
|
7630
7750
|
opts.bypassTenantAudit = true;
|
|
7631
7751
|
}
|
|
@@ -7699,29 +7819,48 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7699
7819
|
* owns the value, not the client.
|
|
7700
7820
|
*
|
|
7701
7821
|
* In the fallback path the next value is `max(existing) + 1`, seeded once per
|
|
7702
|
-
* `object.field
|
|
7703
|
-
* the process, resilient to deletions). `autonumberFormat`
|
|
7704
|
-
*
|
|
7822
|
+
* `object.field.<scope>` from the store then incremented in memory (monotonic
|
|
7823
|
+
* within the process, resilient to deletions). The shared `autonumberFormat`
|
|
7824
|
+
* renderer is honored end-to-end, so date tokens (`AD{YYYYMMDD}{0000}`), field
|
|
7825
|
+
* interpolation (`{island_zone}{000}`) and per-scope reset behave identically
|
|
7826
|
+
* to the SQL driver's persistent sequence (#1603). NOTE: this in-memory seeding
|
|
7827
|
+
* is single-instance.
|
|
7705
7828
|
*/
|
|
7706
7829
|
async applyAutonumbers(object, record, execCtx, driverOwnsAutonumber) {
|
|
7707
7830
|
if (driverOwnsAutonumber) return;
|
|
7708
7831
|
const fields = this.getSchema(object)?.fields;
|
|
7709
7832
|
if (!fields || typeof fields !== "object" || Array.isArray(fields)) return;
|
|
7833
|
+
const now = /* @__PURE__ */ new Date();
|
|
7834
|
+
const timezone = execCtx?.timezone;
|
|
7710
7835
|
for (const [name, def] of Object.entries(fields)) {
|
|
7711
7836
|
if (def?.type !== "autonumber") continue;
|
|
7712
7837
|
const current = record[name];
|
|
7713
7838
|
if (current != null && current !== "") continue;
|
|
7714
|
-
const key = `${object}.${name}`;
|
|
7715
|
-
let next = this.autonumberCounters.get(key);
|
|
7716
|
-
if (next == null) next = await this.seedAutonumber(object, name, execCtx);
|
|
7717
|
-
next += 1;
|
|
7718
|
-
this.autonumberCounters.set(key, next);
|
|
7719
7839
|
const fmt = def.autonumberFormat ?? def.format;
|
|
7720
|
-
|
|
7840
|
+
const tokens = (0, import_data4.parseAutonumberFormat)(typeof fmt === "string" ? fmt : "");
|
|
7841
|
+
const missing = (0, import_data4.missingFieldValues)(tokens, record);
|
|
7842
|
+
if (missing.length > 0) {
|
|
7843
|
+
throw new Error(
|
|
7844
|
+
`Cannot generate autonumber "${object}.${name}" (format "${fmt}"): referenced field(s) [${missing.join(", ")}] are empty on the record. Fields interpolated into an autonumber format must be set before the record is created.`
|
|
7845
|
+
);
|
|
7846
|
+
}
|
|
7847
|
+
const probe = (0, import_data4.renderAutonumber)({ tokens, seq: 0, record, now, timezone });
|
|
7848
|
+
const counterKey = `${object}.${name}.${probe.scope}`;
|
|
7849
|
+
let next = this.autonumberCounters.get(counterKey);
|
|
7850
|
+
if (next == null) next = await this.seedAutonumber(object, name, probe.prefix, execCtx);
|
|
7851
|
+
next += 1;
|
|
7852
|
+
this.autonumberCounters.set(counterKey, next);
|
|
7853
|
+
record[name] = (0, import_data4.renderAutonumber)({ tokens, seq: next, record, now, timezone }).value;
|
|
7721
7854
|
}
|
|
7722
7855
|
}
|
|
7723
|
-
/**
|
|
7724
|
-
|
|
7856
|
+
/**
|
|
7857
|
+
* Seed the autonumber counter from the current max in store, scoped to
|
|
7858
|
+
* `prefix`. With a non-empty prefix (date/field formats) only rows in the
|
|
7859
|
+
* same scope count, and the counter is the digit-run immediately after the
|
|
7860
|
+
* prefix; with an empty prefix (legacy fixed-prefix formats) the last digit
|
|
7861
|
+
* run of the whole value is used, preserving the original behaviour.
|
|
7862
|
+
*/
|
|
7863
|
+
async seedAutonumber(object, field, prefix, execCtx) {
|
|
7725
7864
|
try {
|
|
7726
7865
|
const rows = await this.find(object, {
|
|
7727
7866
|
select: ["id", field],
|
|
@@ -7732,22 +7871,24 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7732
7871
|
for (const r of rows || []) {
|
|
7733
7872
|
const v = r?.[field];
|
|
7734
7873
|
if (v == null) continue;
|
|
7735
|
-
const
|
|
7736
|
-
if (
|
|
7874
|
+
const s = String(v);
|
|
7875
|
+
if (prefix && !s.startsWith(prefix)) continue;
|
|
7876
|
+
const tail = prefix ? s.slice(prefix.length) : s;
|
|
7877
|
+
let digits;
|
|
7878
|
+
if (prefix) {
|
|
7879
|
+
const head = tail.match(/^\d+/);
|
|
7880
|
+
digits = head ? head[0] : void 0;
|
|
7881
|
+
} else {
|
|
7882
|
+
const runs = tail.match(/\d+/g);
|
|
7883
|
+
digits = runs ? runs[runs.length - 1] : void 0;
|
|
7884
|
+
}
|
|
7885
|
+
if (digits) max = Math.max(max, parseInt(digits, 10) || 0);
|
|
7737
7886
|
}
|
|
7738
7887
|
return max;
|
|
7739
7888
|
} catch {
|
|
7740
7889
|
return 0;
|
|
7741
7890
|
}
|
|
7742
7891
|
}
|
|
7743
|
-
/** Apply an autonumber format like `CASE-{0000}`; default to the bare number. */
|
|
7744
|
-
formatAutonumber(format, value) {
|
|
7745
|
-
if (!format) return String(value);
|
|
7746
|
-
const m = format.match(/\{(0+)\}/);
|
|
7747
|
-
if (!m) return format.includes("{0}") ? format.replace("{0}", String(value)) : `${format}${value}`;
|
|
7748
|
-
const padded = String(value).padStart(m[1].length, "0");
|
|
7749
|
-
return format.replace(m[0], padded);
|
|
7750
|
-
}
|
|
7751
7892
|
/**
|
|
7752
7893
|
* Register contribution (Manifest)
|
|
7753
7894
|
*
|
|
@@ -8508,11 +8649,18 @@ var _ObjectQL = class _ObjectQL {
|
|
|
8508
8649
|
const uniqueIds = [...new Set(allIds)];
|
|
8509
8650
|
if (uniqueIds.length === 0) continue;
|
|
8510
8651
|
try {
|
|
8652
|
+
const idFilter = { id: { $in: uniqueIds } };
|
|
8653
|
+
const where = nestedAST.where ? { $and: [idFilter, nestedAST.where] } : idFilter;
|
|
8511
8654
|
const relatedQuery = {
|
|
8512
8655
|
object: referenceObject,
|
|
8513
|
-
where
|
|
8656
|
+
where,
|
|
8514
8657
|
...nestedAST.fields ? { fields: nestedAST.fields } : {},
|
|
8515
8658
|
...nestedAST.orderBy ? { orderBy: nestedAST.orderBy } : {}
|
|
8659
|
+
// NOTE: nestedAST.limit/offset are intentionally NOT forwarded here.
|
|
8660
|
+
// This path batch-loads every parent's related records in a single
|
|
8661
|
+
// $in query, so a *per-parent* limit/offset can't be expressed — a
|
|
8662
|
+
// global cap on the batch would silently drop records other parents
|
|
8663
|
+
// need. Paginate by querying the related object directly instead.
|
|
8516
8664
|
};
|
|
8517
8665
|
const driver = this.getDriver(referenceObject);
|
|
8518
8666
|
const expandOpts = this.buildDriverOptions(execCtx);
|
|
@@ -8565,11 +8713,34 @@ var _ObjectQL = class _ObjectQL {
|
|
|
8565
8713
|
const driver = this.getDriver(object);
|
|
8566
8714
|
const ast = { object, ...query };
|
|
8567
8715
|
delete ast.context;
|
|
8716
|
+
if (ast.filter != null && ast.where == null) {
|
|
8717
|
+
ast.where = ast.filter;
|
|
8718
|
+
}
|
|
8719
|
+
delete ast.filter;
|
|
8568
8720
|
if (ast.top != null && ast.limit == null) {
|
|
8569
8721
|
ast.limit = ast.top;
|
|
8570
8722
|
}
|
|
8571
8723
|
delete ast.top;
|
|
8572
8724
|
const _findSchema = this._registry.getObject(object);
|
|
8725
|
+
{
|
|
8726
|
+
const _searchRaw = ast.search ?? ast.$search;
|
|
8727
|
+
if (_searchRaw != null && _findSchema?.fields) {
|
|
8728
|
+
const _reqFields = ast.searchFields ?? ast.$searchFields ?? (typeof ast.search === "object" ? ast.search?.fields : void 0);
|
|
8729
|
+
const _searchFilter = expandSearchToFilter(_searchRaw, {
|
|
8730
|
+
fields: _findSchema.fields,
|
|
8731
|
+
searchableFields: _findSchema.searchableFields,
|
|
8732
|
+
requestedFields: _reqFields,
|
|
8733
|
+
displayField: _findSchema.displayNameField
|
|
8734
|
+
});
|
|
8735
|
+
if (_searchFilter) {
|
|
8736
|
+
ast.where = ast.where ? { $and: [ast.where, _searchFilter] } : _searchFilter;
|
|
8737
|
+
}
|
|
8738
|
+
}
|
|
8739
|
+
delete ast.search;
|
|
8740
|
+
delete ast.$search;
|
|
8741
|
+
delete ast.searchFields;
|
|
8742
|
+
delete ast.$searchFields;
|
|
8743
|
+
}
|
|
8573
8744
|
const _findFormula = planFormulaProjection(_findSchema, ast.fields);
|
|
8574
8745
|
if (_findFormula.projected) ast.fields = _findFormula.projected;
|
|
8575
8746
|
if (_findSchema?.fields && Array.isArray(ast.fields) && ast.fields.length > 0) {
|
|
@@ -9274,7 +9445,14 @@ var _ObjectQL = class _ObjectQL {
|
|
|
9274
9445
|
const obj = this._registry.getObject(objectName);
|
|
9275
9446
|
if (!obj) return;
|
|
9276
9447
|
const driver = this.getDriverForObject(objectName);
|
|
9277
|
-
if (!driver
|
|
9448
|
+
if (!driver) return;
|
|
9449
|
+
if (obj.external != null) {
|
|
9450
|
+
if (typeof driver.registerExternalObject === "function") {
|
|
9451
|
+
await driver.registerExternalObject(obj);
|
|
9452
|
+
}
|
|
9453
|
+
return;
|
|
9454
|
+
}
|
|
9455
|
+
if (typeof driver.syncSchema !== "function") return;
|
|
9278
9456
|
const tableName = import_system2.StorageNameMapping.resolveTableName(obj);
|
|
9279
9457
|
await driver.syncSchema(tableName, obj);
|
|
9280
9458
|
}
|
|
@@ -10103,6 +10281,27 @@ var ObjectQLPlugin = class {
|
|
|
10103
10281
|
skipped++;
|
|
10104
10282
|
continue;
|
|
10105
10283
|
}
|
|
10284
|
+
if (obj.external != null) {
|
|
10285
|
+
if (typeof driver.registerExternalObject === "function") {
|
|
10286
|
+
try {
|
|
10287
|
+
await driver.registerExternalObject(obj);
|
|
10288
|
+
synced++;
|
|
10289
|
+
} catch (e) {
|
|
10290
|
+
ctx.logger.warn("Failed to register external object metadata", {
|
|
10291
|
+
object: obj.name,
|
|
10292
|
+
driver: driver.name,
|
|
10293
|
+
error: e instanceof Error ? e.message : String(e)
|
|
10294
|
+
});
|
|
10295
|
+
}
|
|
10296
|
+
} else {
|
|
10297
|
+
ctx.logger.debug("Driver does not support registerExternalObject, skipping external object", {
|
|
10298
|
+
object: obj.name,
|
|
10299
|
+
driver: driver.name
|
|
10300
|
+
});
|
|
10301
|
+
skipped++;
|
|
10302
|
+
}
|
|
10303
|
+
continue;
|
|
10304
|
+
}
|
|
10106
10305
|
if (typeof driver.syncSchema !== "function") {
|
|
10107
10306
|
ctx.logger.debug("Driver does not support syncSchema, skipping", {
|
|
10108
10307
|
object: obj.name,
|