@objectstack/objectql 7.5.0 → 7.7.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 +18 -12
- package/dist/index.d.ts +18 -12
- package/dist/index.js +164 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +159 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -6
package/dist/index.mjs
CHANGED
|
@@ -41,7 +41,7 @@ function applySystemFields(schema, opts) {
|
|
|
41
41
|
if (schema.managedBy === "better-auth") return schema;
|
|
42
42
|
const sf = typeof schema.systemFields === "object" && schema.systemFields !== null ? schema.systemFields : void 0;
|
|
43
43
|
const tenancyDisabled = schema.tenancy?.enabled === false;
|
|
44
|
-
const wantTenant =
|
|
44
|
+
const wantTenant = sf?.tenant !== false && !tenancyDisabled;
|
|
45
45
|
const wantAudit = sf?.audit !== false;
|
|
46
46
|
const additions = {};
|
|
47
47
|
if (wantTenant && !schema.fields?.organization_id) {
|
|
@@ -50,11 +50,11 @@ function applySystemFields(schema, opts) {
|
|
|
50
50
|
reference: "sys_organization",
|
|
51
51
|
label: "Organization",
|
|
52
52
|
required: false,
|
|
53
|
-
indexed:
|
|
53
|
+
indexed: opts.multiTenant,
|
|
54
54
|
hidden: true,
|
|
55
55
|
readonly: true,
|
|
56
56
|
system: true,
|
|
57
|
-
description: "Tenant scope (auto-populated by
|
|
57
|
+
description: "Tenant scope (auto-populated by org-scoping on insert; NULL on single-tenant stacks)."
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
if (wantAudit) {
|
|
@@ -5279,12 +5279,25 @@ function validateRecord(objectSchema, data, mode) {
|
|
|
5279
5279
|
|
|
5280
5280
|
// src/validation/rule-validator.ts
|
|
5281
5281
|
import { ExpressionEngine as ExpressionEngine2 } from "@objectstack/formula";
|
|
5282
|
+
import Ajv from "ajv";
|
|
5283
|
+
var ajv = new Ajv({ allErrors: true, strict: false });
|
|
5284
|
+
var jsonSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
5282
5285
|
function needsPriorRecord(objectSchema) {
|
|
5283
5286
|
const rules = objectSchema?.validations;
|
|
5284
5287
|
if (!Array.isArray(rules)) return false;
|
|
5285
|
-
return rules.some(
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
+
return rules.some((r) => ruleNeedsPrior(r));
|
|
5289
|
+
}
|
|
5290
|
+
function ruleNeedsPrior(r) {
|
|
5291
|
+
if (r == null || typeof r !== "object") return false;
|
|
5292
|
+
const type = r.type;
|
|
5293
|
+
if (type === "state_machine" || type === "cross_field" || type === "script") {
|
|
5294
|
+
return true;
|
|
5295
|
+
}
|
|
5296
|
+
if (type === "conditional") {
|
|
5297
|
+
const c = r;
|
|
5298
|
+
return ruleNeedsPrior(c.then) || ruleNeedsPrior(c.otherwise);
|
|
5299
|
+
}
|
|
5300
|
+
return false;
|
|
5288
5301
|
}
|
|
5289
5302
|
function toExpression(cond) {
|
|
5290
5303
|
return typeof cond === "string" ? { dialect: "cel", source: cond } : cond;
|
|
@@ -5294,6 +5307,7 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5294
5307
|
if (!Array.isArray(rules) || rules.length === 0 || !data) return;
|
|
5295
5308
|
const previous = opts.previous ?? void 0;
|
|
5296
5309
|
const merged = { ...previous ?? {}, ...data };
|
|
5310
|
+
const ctx = { data, merged, previous, mode, logger: opts.logger };
|
|
5297
5311
|
const errors = [];
|
|
5298
5312
|
const ordered = rules.filter((r) => r != null && typeof r === "object").filter((r) => r.active !== false).filter((r) => {
|
|
5299
5313
|
const events = r.events ?? ["insert", "update"];
|
|
@@ -5302,11 +5316,7 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5302
5316
|
for (const rule of ordered) {
|
|
5303
5317
|
let violation = null;
|
|
5304
5318
|
try {
|
|
5305
|
-
|
|
5306
|
-
violation = checkStateMachine(rule, mode, data, previous);
|
|
5307
|
-
} else if (rule.type === "script" || rule.type === "cross_field") {
|
|
5308
|
-
violation = checkPredicate(rule, merged, previous, opts.logger);
|
|
5309
|
-
}
|
|
5319
|
+
violation = evaluateRule(rule, ctx);
|
|
5310
5320
|
} catch (err) {
|
|
5311
5321
|
opts.logger?.warn?.(`Validation rule '${rule.name}' threw \u2014 skipped`, err);
|
|
5312
5322
|
continue;
|
|
@@ -5323,6 +5333,23 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5323
5333
|
}
|
|
5324
5334
|
if (errors.length > 0) throw new ValidationError(errors);
|
|
5325
5335
|
}
|
|
5336
|
+
function evaluateRule(rule, ctx) {
|
|
5337
|
+
switch (rule.type) {
|
|
5338
|
+
case "state_machine":
|
|
5339
|
+
return checkStateMachine(rule, ctx.mode, ctx.data, ctx.previous);
|
|
5340
|
+
case "script":
|
|
5341
|
+
case "cross_field":
|
|
5342
|
+
return checkPredicate(rule, ctx.merged, ctx.previous, ctx.logger);
|
|
5343
|
+
case "format":
|
|
5344
|
+
return checkFormat(rule, ctx.data, ctx.logger);
|
|
5345
|
+
case "json_schema":
|
|
5346
|
+
return checkJsonSchema(rule, ctx.data, ctx.logger);
|
|
5347
|
+
case "conditional":
|
|
5348
|
+
return checkConditional(rule, ctx);
|
|
5349
|
+
default:
|
|
5350
|
+
return null;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5326
5353
|
function checkStateMachine(rule, mode, data, previous) {
|
|
5327
5354
|
if (mode === "insert" || !previous) return null;
|
|
5328
5355
|
if (!(rule.field in data)) return null;
|
|
@@ -5362,6 +5389,99 @@ function checkPredicate(rule, record, previous, logger) {
|
|
|
5362
5389
|
}
|
|
5363
5390
|
return null;
|
|
5364
5391
|
}
|
|
5392
|
+
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5393
|
+
var PHONE_RE2 = /^\+?[\d\s().-]{7,20}$/;
|
|
5394
|
+
function checkFormat(rule, data, logger) {
|
|
5395
|
+
if (!(rule.field in data)) return null;
|
|
5396
|
+
const value = data[rule.field];
|
|
5397
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
5398
|
+
const str = String(value);
|
|
5399
|
+
if (rule.regex) {
|
|
5400
|
+
let re;
|
|
5401
|
+
try {
|
|
5402
|
+
re = new RegExp(rule.regex);
|
|
5403
|
+
} catch {
|
|
5404
|
+
logger?.warn?.(`Validation rule '${rule.name}' has an invalid regex \u2014 skipped`);
|
|
5405
|
+
return null;
|
|
5406
|
+
}
|
|
5407
|
+
if (!re.test(str)) return formatViolation(rule);
|
|
5408
|
+
}
|
|
5409
|
+
if (rule.format && !matchesNamedFormat(rule.format, str)) {
|
|
5410
|
+
return formatViolation(rule);
|
|
5411
|
+
}
|
|
5412
|
+
return null;
|
|
5413
|
+
}
|
|
5414
|
+
function matchesNamedFormat(format, str) {
|
|
5415
|
+
switch (format) {
|
|
5416
|
+
case "email":
|
|
5417
|
+
return EMAIL_RE2.test(str);
|
|
5418
|
+
case "phone":
|
|
5419
|
+
return PHONE_RE2.test(str);
|
|
5420
|
+
case "url":
|
|
5421
|
+
try {
|
|
5422
|
+
new URL(str);
|
|
5423
|
+
return true;
|
|
5424
|
+
} catch {
|
|
5425
|
+
return false;
|
|
5426
|
+
}
|
|
5427
|
+
case "json":
|
|
5428
|
+
try {
|
|
5429
|
+
JSON.parse(str);
|
|
5430
|
+
return true;
|
|
5431
|
+
} catch {
|
|
5432
|
+
return false;
|
|
5433
|
+
}
|
|
5434
|
+
default:
|
|
5435
|
+
return true;
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
function formatViolation(rule) {
|
|
5439
|
+
return { field: rule.field, code: "invalid_format", message: rule.message };
|
|
5440
|
+
}
|
|
5441
|
+
function checkJsonSchema(rule, data, logger) {
|
|
5442
|
+
if (!(rule.field in data)) return null;
|
|
5443
|
+
let value = data[rule.field];
|
|
5444
|
+
if (value === null || value === void 0) return null;
|
|
5445
|
+
if (typeof value === "string") {
|
|
5446
|
+
try {
|
|
5447
|
+
value = JSON.parse(value);
|
|
5448
|
+
} catch {
|
|
5449
|
+
return { field: rule.field, code: "invalid_json", message: rule.message };
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
let validate = jsonSchemaCache.get(rule.schema);
|
|
5453
|
+
if (!validate) {
|
|
5454
|
+
try {
|
|
5455
|
+
validate = ajv.compile(rule.schema);
|
|
5456
|
+
} catch (err) {
|
|
5457
|
+
logger?.warn?.(
|
|
5458
|
+
`Validation rule '${rule.name}' has an uncompilable JSON Schema \u2014 skipped`,
|
|
5459
|
+
err
|
|
5460
|
+
);
|
|
5461
|
+
return null;
|
|
5462
|
+
}
|
|
5463
|
+
jsonSchemaCache.set(rule.schema, validate);
|
|
5464
|
+
}
|
|
5465
|
+
if (!validate(value)) {
|
|
5466
|
+
return { field: rule.field, code: "json_schema_violation", message: rule.message };
|
|
5467
|
+
}
|
|
5468
|
+
return null;
|
|
5469
|
+
}
|
|
5470
|
+
function checkConditional(rule, ctx) {
|
|
5471
|
+
const result = ExpressionEngine2.evaluate(toExpression(rule.when), {
|
|
5472
|
+
record: ctx.merged,
|
|
5473
|
+
previous: ctx.previous ?? void 0
|
|
5474
|
+
});
|
|
5475
|
+
if (!result.ok) {
|
|
5476
|
+
ctx.logger?.warn?.(
|
|
5477
|
+
`Validation rule '${rule.name}' when-predicate failed to evaluate (${result.error.kind}: ${result.error.message}) \u2014 skipped`
|
|
5478
|
+
);
|
|
5479
|
+
return null;
|
|
5480
|
+
}
|
|
5481
|
+
const branch = result.value === true ? rule.then : rule.otherwise;
|
|
5482
|
+
if (!branch || branch.active === false) return null;
|
|
5483
|
+
return evaluateRule(branch, ctx);
|
|
5484
|
+
}
|
|
5365
5485
|
function legalNextStates(objectSchema, field, currentState) {
|
|
5366
5486
|
const rules = objectSchema?.validations;
|
|
5367
5487
|
if (!Array.isArray(rules)) return null;
|
|
@@ -6322,7 +6442,7 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6322
6442
|
* **fail-closed** — the write throws rather than persist cleartext.
|
|
6323
6443
|
*
|
|
6324
6444
|
* Mirrors the Settings subsystem's ICryptoProvider wiring; the host (e.g.
|
|
6325
|
-
* `serve`) injects `
|
|
6445
|
+
* `serve`) injects `LocalCryptoProvider` in dev and a KMS/Vault-backed
|
|
6326
6446
|
* provider in production.
|
|
6327
6447
|
*/
|
|
6328
6448
|
setCryptoProvider(provider) {
|
|
@@ -6360,7 +6480,7 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6360
6480
|
}
|
|
6361
6481
|
if (!this.cryptoProvider) {
|
|
6362
6482
|
throw new Error(
|
|
6363
|
-
`Cannot persist secret field "${object}.${field}": no CryptoProvider is registered. Wire one via engine.setCryptoProvider(...) (e.g.
|
|
6483
|
+
`Cannot persist secret field "${object}.${field}": no CryptoProvider is registered. Wire one via engine.setCryptoProvider(...) (e.g. LocalCryptoProvider in dev, a KMS/Vault provider in production). Refusing to store cleartext (fail-closed).`
|
|
6364
6484
|
);
|
|
6365
6485
|
}
|
|
6366
6486
|
const plain = typeof value === "string" ? value : JSON.stringify(value);
|
|
@@ -6894,7 +7014,11 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6894
7014
|
const driver = this.getDriver(object);
|
|
6895
7015
|
let id = data.id;
|
|
6896
7016
|
if (!id && options?.where && typeof options.where === "object" && "id" in options.where) {
|
|
6897
|
-
|
|
7017
|
+
const whereId = options.where.id;
|
|
7018
|
+
const t = typeof whereId;
|
|
7019
|
+
if (whereId !== null && (t === "string" || t === "number" || t === "bigint")) {
|
|
7020
|
+
id = whereId;
|
|
7021
|
+
}
|
|
6898
7022
|
}
|
|
6899
7023
|
const opCtx = {
|
|
6900
7024
|
object,
|
|
@@ -7619,6 +7743,12 @@ var MetadataFacade = class {
|
|
|
7619
7743
|
|
|
7620
7744
|
// src/plugin.ts
|
|
7621
7745
|
import { StorageNameMapping as StorageNameMapping2 } from "@objectstack/spec/system";
|
|
7746
|
+
import {
|
|
7747
|
+
SysMetadataObject,
|
|
7748
|
+
SysMetadataHistoryObject,
|
|
7749
|
+
SysMetadataAuditObject,
|
|
7750
|
+
SysViewDefinitionObject
|
|
7751
|
+
} from "@objectstack/metadata-core";
|
|
7622
7752
|
function hasLoadMetaFromDb(service) {
|
|
7623
7753
|
return typeof service === "object" && service !== null && typeof service["loadMetaFromDb"] === "function";
|
|
7624
7754
|
}
|
|
@@ -7655,6 +7785,21 @@ var ObjectQLPlugin = class {
|
|
|
7655
7785
|
ctx.logger.info("ObjectQL engine registered", {
|
|
7656
7786
|
services: ["objectql", "data", "manifest"]
|
|
7657
7787
|
});
|
|
7788
|
+
if (this.environmentId === void 0) {
|
|
7789
|
+
this.ql.registerApp({
|
|
7790
|
+
id: "com.objectstack.metadata-objects",
|
|
7791
|
+
name: "Metadata Platform Objects",
|
|
7792
|
+
version: "1.0.0",
|
|
7793
|
+
type: "plugin",
|
|
7794
|
+
scope: "system",
|
|
7795
|
+
objects: [
|
|
7796
|
+
SysMetadataObject,
|
|
7797
|
+
SysMetadataHistoryObject,
|
|
7798
|
+
SysMetadataAuditObject,
|
|
7799
|
+
SysViewDefinitionObject
|
|
7800
|
+
]
|
|
7801
|
+
});
|
|
7802
|
+
}
|
|
7658
7803
|
const protocolShim = new ObjectStackProtocolImplementation(
|
|
7659
7804
|
this.ql,
|
|
7660
7805
|
() => ctx.getServices ? ctx.getServices() : /* @__PURE__ */ new Map(),
|