@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.d.mts
CHANGED
|
@@ -81,11 +81,13 @@ type RegistryLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
|
81
81
|
*/
|
|
82
82
|
interface SchemaRegistryOptions {
|
|
83
83
|
/**
|
|
84
|
-
* Whether the host kernel runs in multi-tenant mode.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* isn't `managedBy` an external subsystem
|
|
88
|
-
* `systemFields:
|
|
84
|
+
* Whether the host kernel runs in multi-tenant mode. The `organization_id`
|
|
85
|
+
* column itself is auto-injected regardless of this flag (lookup →
|
|
86
|
+
* sys_organization, on every registered object that doesn't already declare
|
|
87
|
+
* it, isn't `managedBy` an external subsystem, and hasn't opted out via
|
|
88
|
+
* `systemFields`/`tenancy.enabled:false`). When `true` the injected column
|
|
89
|
+
* is additionally INDEXED — single-tenant stacks skip the index since
|
|
90
|
+
* nothing ever filters by organization.
|
|
89
91
|
*
|
|
90
92
|
* Sourced from the `OS_MULTI_TENANT` env var when not explicitly set —
|
|
91
93
|
* matches how the SecurityPlugin and CLI startup banner pick the mode.
|
|
@@ -107,9 +109,12 @@ interface SchemaRegistryOptions {
|
|
|
107
109
|
* via the natural `{ ...sys, ...authored }` merge.
|
|
108
110
|
*
|
|
109
111
|
* Currently injects:
|
|
110
|
-
* - `organization_id` —
|
|
111
|
-
*
|
|
112
|
-
*
|
|
112
|
+
* - `organization_id` — always provisioned (unless the object opts out via
|
|
113
|
+
* `systemFields`/`tenancy.enabled:false` or is `better-auth` managed) so
|
|
114
|
+
* the column never depends on the global multi-tenant flag. Required-false;
|
|
115
|
+
* org-scoping populates it on insert in multi-tenant mode, and it stays
|
|
116
|
+
* NULL on single-tenant stacks. Only the column's INDEX is gated on
|
|
117
|
+
* `multiTenant` (no per-tenant filtering exists single-tenant).
|
|
113
118
|
* - `created_at` / `created_by` / `updated_at` / `updated_by` — audit
|
|
114
119
|
* fields. Marked `system: true, readonly: true` so detail views can
|
|
115
120
|
* surface them in a dedicated "System Information" section while
|
|
@@ -419,6 +424,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
419
424
|
graphql?: string | undefined;
|
|
420
425
|
packages?: string | undefined;
|
|
421
426
|
workflow?: string | undefined;
|
|
427
|
+
approvals?: string | undefined;
|
|
422
428
|
realtime?: string | undefined;
|
|
423
429
|
notifications?: string | undefined;
|
|
424
430
|
ai?: string | undefined;
|
|
@@ -549,14 +555,14 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
549
555
|
provider: "api";
|
|
550
556
|
read?: {
|
|
551
557
|
url: string;
|
|
552
|
-
method: "
|
|
558
|
+
method: "POST" | "PATCH" | "PUT" | "DELETE" | "GET";
|
|
553
559
|
headers?: Record<string, string> | undefined;
|
|
554
560
|
params?: Record<string, unknown> | undefined;
|
|
555
561
|
body?: unknown;
|
|
556
562
|
} | undefined;
|
|
557
563
|
write?: {
|
|
558
564
|
url: string;
|
|
559
|
-
method: "
|
|
565
|
+
method: "POST" | "PATCH" | "PUT" | "DELETE" | "GET";
|
|
560
566
|
headers?: Record<string, string> | undefined;
|
|
561
567
|
params?: Record<string, unknown> | undefined;
|
|
562
568
|
body?: unknown;
|
|
@@ -1849,7 +1855,7 @@ declare class ObjectQL implements IDataEngine {
|
|
|
1849
1855
|
* **fail-closed** — the write throws rather than persist cleartext.
|
|
1850
1856
|
*
|
|
1851
1857
|
* Mirrors the Settings subsystem's ICryptoProvider wiring; the host (e.g.
|
|
1852
|
-
* `serve`) injects `
|
|
1858
|
+
* `serve`) injects `LocalCryptoProvider` in dev and a KMS/Vault-backed
|
|
1853
1859
|
* provider in production.
|
|
1854
1860
|
*/
|
|
1855
1861
|
setCryptoProvider(provider: ICryptoProvider): void;
|
|
@@ -2379,7 +2385,7 @@ declare function wrapDeclarativeHook(meta: Hook, handler: HookHandler, opts?: Wr
|
|
|
2379
2385
|
|
|
2380
2386
|
interface FieldValidationError {
|
|
2381
2387
|
field: string;
|
|
2382
|
-
code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option' | 'invalid_transition' | 'rule_violation';
|
|
2388
|
+
code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option' | 'invalid_transition' | 'rule_violation' | 'invalid_format' | 'invalid_json' | 'json_schema_violation';
|
|
2383
2389
|
message: string;
|
|
2384
2390
|
/** Allowed values for select/multiselect, when applicable. */
|
|
2385
2391
|
options?: string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -81,11 +81,13 @@ type RegistryLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
|
81
81
|
*/
|
|
82
82
|
interface SchemaRegistryOptions {
|
|
83
83
|
/**
|
|
84
|
-
* Whether the host kernel runs in multi-tenant mode.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* isn't `managedBy` an external subsystem
|
|
88
|
-
* `systemFields:
|
|
84
|
+
* Whether the host kernel runs in multi-tenant mode. The `organization_id`
|
|
85
|
+
* column itself is auto-injected regardless of this flag (lookup →
|
|
86
|
+
* sys_organization, on every registered object that doesn't already declare
|
|
87
|
+
* it, isn't `managedBy` an external subsystem, and hasn't opted out via
|
|
88
|
+
* `systemFields`/`tenancy.enabled:false`). When `true` the injected column
|
|
89
|
+
* is additionally INDEXED — single-tenant stacks skip the index since
|
|
90
|
+
* nothing ever filters by organization.
|
|
89
91
|
*
|
|
90
92
|
* Sourced from the `OS_MULTI_TENANT` env var when not explicitly set —
|
|
91
93
|
* matches how the SecurityPlugin and CLI startup banner pick the mode.
|
|
@@ -107,9 +109,12 @@ interface SchemaRegistryOptions {
|
|
|
107
109
|
* via the natural `{ ...sys, ...authored }` merge.
|
|
108
110
|
*
|
|
109
111
|
* Currently injects:
|
|
110
|
-
* - `organization_id` —
|
|
111
|
-
*
|
|
112
|
-
*
|
|
112
|
+
* - `organization_id` — always provisioned (unless the object opts out via
|
|
113
|
+
* `systemFields`/`tenancy.enabled:false` or is `better-auth` managed) so
|
|
114
|
+
* the column never depends on the global multi-tenant flag. Required-false;
|
|
115
|
+
* org-scoping populates it on insert in multi-tenant mode, and it stays
|
|
116
|
+
* NULL on single-tenant stacks. Only the column's INDEX is gated on
|
|
117
|
+
* `multiTenant` (no per-tenant filtering exists single-tenant).
|
|
113
118
|
* - `created_at` / `created_by` / `updated_at` / `updated_by` — audit
|
|
114
119
|
* fields. Marked `system: true, readonly: true` so detail views can
|
|
115
120
|
* surface them in a dedicated "System Information" section while
|
|
@@ -419,6 +424,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
419
424
|
graphql?: string | undefined;
|
|
420
425
|
packages?: string | undefined;
|
|
421
426
|
workflow?: string | undefined;
|
|
427
|
+
approvals?: string | undefined;
|
|
422
428
|
realtime?: string | undefined;
|
|
423
429
|
notifications?: string | undefined;
|
|
424
430
|
ai?: string | undefined;
|
|
@@ -549,14 +555,14 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
549
555
|
provider: "api";
|
|
550
556
|
read?: {
|
|
551
557
|
url: string;
|
|
552
|
-
method: "
|
|
558
|
+
method: "POST" | "PATCH" | "PUT" | "DELETE" | "GET";
|
|
553
559
|
headers?: Record<string, string> | undefined;
|
|
554
560
|
params?: Record<string, unknown> | undefined;
|
|
555
561
|
body?: unknown;
|
|
556
562
|
} | undefined;
|
|
557
563
|
write?: {
|
|
558
564
|
url: string;
|
|
559
|
-
method: "
|
|
565
|
+
method: "POST" | "PATCH" | "PUT" | "DELETE" | "GET";
|
|
560
566
|
headers?: Record<string, string> | undefined;
|
|
561
567
|
params?: Record<string, unknown> | undefined;
|
|
562
568
|
body?: unknown;
|
|
@@ -1849,7 +1855,7 @@ declare class ObjectQL implements IDataEngine {
|
|
|
1849
1855
|
* **fail-closed** — the write throws rather than persist cleartext.
|
|
1850
1856
|
*
|
|
1851
1857
|
* Mirrors the Settings subsystem's ICryptoProvider wiring; the host (e.g.
|
|
1852
|
-
* `serve`) injects `
|
|
1858
|
+
* `serve`) injects `LocalCryptoProvider` in dev and a KMS/Vault-backed
|
|
1853
1859
|
* provider in production.
|
|
1854
1860
|
*/
|
|
1855
1861
|
setCryptoProvider(provider: ICryptoProvider): void;
|
|
@@ -2379,7 +2385,7 @@ declare function wrapDeclarativeHook(meta: Hook, handler: HookHandler, opts?: Wr
|
|
|
2379
2385
|
|
|
2380
2386
|
interface FieldValidationError {
|
|
2381
2387
|
field: string;
|
|
2382
|
-
code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option' | 'invalid_transition' | 'rule_violation';
|
|
2388
|
+
code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option' | 'invalid_transition' | 'rule_violation' | 'invalid_format' | 'invalid_json' | 'json_schema_violation';
|
|
2383
2389
|
message: string;
|
|
2384
2390
|
/** Allowed values for select/multiselect, when applicable. */
|
|
2385
2391
|
options?: string[];
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -100,7 +110,7 @@ function applySystemFields(schema, opts) {
|
|
|
100
110
|
if (schema.managedBy === "better-auth") return schema;
|
|
101
111
|
const sf = typeof schema.systemFields === "object" && schema.systemFields !== null ? schema.systemFields : void 0;
|
|
102
112
|
const tenancyDisabled = schema.tenancy?.enabled === false;
|
|
103
|
-
const wantTenant =
|
|
113
|
+
const wantTenant = sf?.tenant !== false && !tenancyDisabled;
|
|
104
114
|
const wantAudit = sf?.audit !== false;
|
|
105
115
|
const additions = {};
|
|
106
116
|
if (wantTenant && !schema.fields?.organization_id) {
|
|
@@ -109,11 +119,11 @@ function applySystemFields(schema, opts) {
|
|
|
109
119
|
reference: "sys_organization",
|
|
110
120
|
label: "Organization",
|
|
111
121
|
required: false,
|
|
112
|
-
indexed:
|
|
122
|
+
indexed: opts.multiTenant,
|
|
113
123
|
hidden: true,
|
|
114
124
|
readonly: true,
|
|
115
125
|
system: true,
|
|
116
|
-
description: "Tenant scope (auto-populated by
|
|
126
|
+
description: "Tenant scope (auto-populated by org-scoping on insert; NULL on single-tenant stacks)."
|
|
117
127
|
};
|
|
118
128
|
}
|
|
119
129
|
if (wantAudit) {
|
|
@@ -5333,12 +5343,25 @@ function validateRecord(objectSchema, data, mode) {
|
|
|
5333
5343
|
|
|
5334
5344
|
// src/validation/rule-validator.ts
|
|
5335
5345
|
var import_formula2 = require("@objectstack/formula");
|
|
5346
|
+
var import_ajv = __toESM(require("ajv"));
|
|
5347
|
+
var ajv = new import_ajv.default({ allErrors: true, strict: false });
|
|
5348
|
+
var jsonSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
5336
5349
|
function needsPriorRecord(objectSchema) {
|
|
5337
5350
|
const rules = objectSchema?.validations;
|
|
5338
5351
|
if (!Array.isArray(rules)) return false;
|
|
5339
|
-
return rules.some(
|
|
5340
|
-
|
|
5341
|
-
|
|
5352
|
+
return rules.some((r) => ruleNeedsPrior(r));
|
|
5353
|
+
}
|
|
5354
|
+
function ruleNeedsPrior(r) {
|
|
5355
|
+
if (r == null || typeof r !== "object") return false;
|
|
5356
|
+
const type = r.type;
|
|
5357
|
+
if (type === "state_machine" || type === "cross_field" || type === "script") {
|
|
5358
|
+
return true;
|
|
5359
|
+
}
|
|
5360
|
+
if (type === "conditional") {
|
|
5361
|
+
const c = r;
|
|
5362
|
+
return ruleNeedsPrior(c.then) || ruleNeedsPrior(c.otherwise);
|
|
5363
|
+
}
|
|
5364
|
+
return false;
|
|
5342
5365
|
}
|
|
5343
5366
|
function toExpression(cond) {
|
|
5344
5367
|
return typeof cond === "string" ? { dialect: "cel", source: cond } : cond;
|
|
@@ -5348,6 +5371,7 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5348
5371
|
if (!Array.isArray(rules) || rules.length === 0 || !data) return;
|
|
5349
5372
|
const previous = opts.previous ?? void 0;
|
|
5350
5373
|
const merged = { ...previous ?? {}, ...data };
|
|
5374
|
+
const ctx = { data, merged, previous, mode, logger: opts.logger };
|
|
5351
5375
|
const errors = [];
|
|
5352
5376
|
const ordered = rules.filter((r) => r != null && typeof r === "object").filter((r) => r.active !== false).filter((r) => {
|
|
5353
5377
|
const events = r.events ?? ["insert", "update"];
|
|
@@ -5356,11 +5380,7 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5356
5380
|
for (const rule of ordered) {
|
|
5357
5381
|
let violation = null;
|
|
5358
5382
|
try {
|
|
5359
|
-
|
|
5360
|
-
violation = checkStateMachine(rule, mode, data, previous);
|
|
5361
|
-
} else if (rule.type === "script" || rule.type === "cross_field") {
|
|
5362
|
-
violation = checkPredicate(rule, merged, previous, opts.logger);
|
|
5363
|
-
}
|
|
5383
|
+
violation = evaluateRule(rule, ctx);
|
|
5364
5384
|
} catch (err) {
|
|
5365
5385
|
opts.logger?.warn?.(`Validation rule '${rule.name}' threw \u2014 skipped`, err);
|
|
5366
5386
|
continue;
|
|
@@ -5377,6 +5397,23 @@ function evaluateValidationRules(objectSchema, data, mode, opts = {}) {
|
|
|
5377
5397
|
}
|
|
5378
5398
|
if (errors.length > 0) throw new ValidationError(errors);
|
|
5379
5399
|
}
|
|
5400
|
+
function evaluateRule(rule, ctx) {
|
|
5401
|
+
switch (rule.type) {
|
|
5402
|
+
case "state_machine":
|
|
5403
|
+
return checkStateMachine(rule, ctx.mode, ctx.data, ctx.previous);
|
|
5404
|
+
case "script":
|
|
5405
|
+
case "cross_field":
|
|
5406
|
+
return checkPredicate(rule, ctx.merged, ctx.previous, ctx.logger);
|
|
5407
|
+
case "format":
|
|
5408
|
+
return checkFormat(rule, ctx.data, ctx.logger);
|
|
5409
|
+
case "json_schema":
|
|
5410
|
+
return checkJsonSchema(rule, ctx.data, ctx.logger);
|
|
5411
|
+
case "conditional":
|
|
5412
|
+
return checkConditional(rule, ctx);
|
|
5413
|
+
default:
|
|
5414
|
+
return null;
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5380
5417
|
function checkStateMachine(rule, mode, data, previous) {
|
|
5381
5418
|
if (mode === "insert" || !previous) return null;
|
|
5382
5419
|
if (!(rule.field in data)) return null;
|
|
@@ -5416,6 +5453,99 @@ function checkPredicate(rule, record, previous, logger) {
|
|
|
5416
5453
|
}
|
|
5417
5454
|
return null;
|
|
5418
5455
|
}
|
|
5456
|
+
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5457
|
+
var PHONE_RE2 = /^\+?[\d\s().-]{7,20}$/;
|
|
5458
|
+
function checkFormat(rule, data, logger) {
|
|
5459
|
+
if (!(rule.field in data)) return null;
|
|
5460
|
+
const value = data[rule.field];
|
|
5461
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
5462
|
+
const str = String(value);
|
|
5463
|
+
if (rule.regex) {
|
|
5464
|
+
let re;
|
|
5465
|
+
try {
|
|
5466
|
+
re = new RegExp(rule.regex);
|
|
5467
|
+
} catch {
|
|
5468
|
+
logger?.warn?.(`Validation rule '${rule.name}' has an invalid regex \u2014 skipped`);
|
|
5469
|
+
return null;
|
|
5470
|
+
}
|
|
5471
|
+
if (!re.test(str)) return formatViolation(rule);
|
|
5472
|
+
}
|
|
5473
|
+
if (rule.format && !matchesNamedFormat(rule.format, str)) {
|
|
5474
|
+
return formatViolation(rule);
|
|
5475
|
+
}
|
|
5476
|
+
return null;
|
|
5477
|
+
}
|
|
5478
|
+
function matchesNamedFormat(format, str) {
|
|
5479
|
+
switch (format) {
|
|
5480
|
+
case "email":
|
|
5481
|
+
return EMAIL_RE2.test(str);
|
|
5482
|
+
case "phone":
|
|
5483
|
+
return PHONE_RE2.test(str);
|
|
5484
|
+
case "url":
|
|
5485
|
+
try {
|
|
5486
|
+
new URL(str);
|
|
5487
|
+
return true;
|
|
5488
|
+
} catch {
|
|
5489
|
+
return false;
|
|
5490
|
+
}
|
|
5491
|
+
case "json":
|
|
5492
|
+
try {
|
|
5493
|
+
JSON.parse(str);
|
|
5494
|
+
return true;
|
|
5495
|
+
} catch {
|
|
5496
|
+
return false;
|
|
5497
|
+
}
|
|
5498
|
+
default:
|
|
5499
|
+
return true;
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
function formatViolation(rule) {
|
|
5503
|
+
return { field: rule.field, code: "invalid_format", message: rule.message };
|
|
5504
|
+
}
|
|
5505
|
+
function checkJsonSchema(rule, data, logger) {
|
|
5506
|
+
if (!(rule.field in data)) return null;
|
|
5507
|
+
let value = data[rule.field];
|
|
5508
|
+
if (value === null || value === void 0) return null;
|
|
5509
|
+
if (typeof value === "string") {
|
|
5510
|
+
try {
|
|
5511
|
+
value = JSON.parse(value);
|
|
5512
|
+
} catch {
|
|
5513
|
+
return { field: rule.field, code: "invalid_json", message: rule.message };
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
let validate = jsonSchemaCache.get(rule.schema);
|
|
5517
|
+
if (!validate) {
|
|
5518
|
+
try {
|
|
5519
|
+
validate = ajv.compile(rule.schema);
|
|
5520
|
+
} catch (err) {
|
|
5521
|
+
logger?.warn?.(
|
|
5522
|
+
`Validation rule '${rule.name}' has an uncompilable JSON Schema \u2014 skipped`,
|
|
5523
|
+
err
|
|
5524
|
+
);
|
|
5525
|
+
return null;
|
|
5526
|
+
}
|
|
5527
|
+
jsonSchemaCache.set(rule.schema, validate);
|
|
5528
|
+
}
|
|
5529
|
+
if (!validate(value)) {
|
|
5530
|
+
return { field: rule.field, code: "json_schema_violation", message: rule.message };
|
|
5531
|
+
}
|
|
5532
|
+
return null;
|
|
5533
|
+
}
|
|
5534
|
+
function checkConditional(rule, ctx) {
|
|
5535
|
+
const result = import_formula2.ExpressionEngine.evaluate(toExpression(rule.when), {
|
|
5536
|
+
record: ctx.merged,
|
|
5537
|
+
previous: ctx.previous ?? void 0
|
|
5538
|
+
});
|
|
5539
|
+
if (!result.ok) {
|
|
5540
|
+
ctx.logger?.warn?.(
|
|
5541
|
+
`Validation rule '${rule.name}' when-predicate failed to evaluate (${result.error.kind}: ${result.error.message}) \u2014 skipped`
|
|
5542
|
+
);
|
|
5543
|
+
return null;
|
|
5544
|
+
}
|
|
5545
|
+
const branch = result.value === true ? rule.then : rule.otherwise;
|
|
5546
|
+
if (!branch || branch.active === false) return null;
|
|
5547
|
+
return evaluateRule(branch, ctx);
|
|
5548
|
+
}
|
|
5419
5549
|
function legalNextStates(objectSchema, field, currentState) {
|
|
5420
5550
|
const rules = objectSchema?.validations;
|
|
5421
5551
|
if (!Array.isArray(rules)) return null;
|
|
@@ -6376,7 +6506,7 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6376
6506
|
* **fail-closed** — the write throws rather than persist cleartext.
|
|
6377
6507
|
*
|
|
6378
6508
|
* Mirrors the Settings subsystem's ICryptoProvider wiring; the host (e.g.
|
|
6379
|
-
* `serve`) injects `
|
|
6509
|
+
* `serve`) injects `LocalCryptoProvider` in dev and a KMS/Vault-backed
|
|
6380
6510
|
* provider in production.
|
|
6381
6511
|
*/
|
|
6382
6512
|
setCryptoProvider(provider) {
|
|
@@ -6414,7 +6544,7 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6414
6544
|
}
|
|
6415
6545
|
if (!this.cryptoProvider) {
|
|
6416
6546
|
throw new Error(
|
|
6417
|
-
`Cannot persist secret field "${object}.${field}": no CryptoProvider is registered. Wire one via engine.setCryptoProvider(...) (e.g.
|
|
6547
|
+
`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).`
|
|
6418
6548
|
);
|
|
6419
6549
|
}
|
|
6420
6550
|
const plain = typeof value === "string" ? value : JSON.stringify(value);
|
|
@@ -6948,7 +7078,11 @@ var _ObjectQL = class _ObjectQL {
|
|
|
6948
7078
|
const driver = this.getDriver(object);
|
|
6949
7079
|
let id = data.id;
|
|
6950
7080
|
if (!id && options?.where && typeof options.where === "object" && "id" in options.where) {
|
|
6951
|
-
|
|
7081
|
+
const whereId = options.where.id;
|
|
7082
|
+
const t = typeof whereId;
|
|
7083
|
+
if (whereId !== null && (t === "string" || t === "number" || t === "bigint")) {
|
|
7084
|
+
id = whereId;
|
|
7085
|
+
}
|
|
6952
7086
|
}
|
|
6953
7087
|
const opCtx = {
|
|
6954
7088
|
object,
|
|
@@ -7673,6 +7807,7 @@ var MetadataFacade = class {
|
|
|
7673
7807
|
|
|
7674
7808
|
// src/plugin.ts
|
|
7675
7809
|
var import_system3 = require("@objectstack/spec/system");
|
|
7810
|
+
var import_metadata_core3 = require("@objectstack/metadata-core");
|
|
7676
7811
|
function hasLoadMetaFromDb(service) {
|
|
7677
7812
|
return typeof service === "object" && service !== null && typeof service["loadMetaFromDb"] === "function";
|
|
7678
7813
|
}
|
|
@@ -7709,6 +7844,21 @@ var ObjectQLPlugin = class {
|
|
|
7709
7844
|
ctx.logger.info("ObjectQL engine registered", {
|
|
7710
7845
|
services: ["objectql", "data", "manifest"]
|
|
7711
7846
|
});
|
|
7847
|
+
if (this.environmentId === void 0) {
|
|
7848
|
+
this.ql.registerApp({
|
|
7849
|
+
id: "com.objectstack.metadata-objects",
|
|
7850
|
+
name: "Metadata Platform Objects",
|
|
7851
|
+
version: "1.0.0",
|
|
7852
|
+
type: "plugin",
|
|
7853
|
+
scope: "system",
|
|
7854
|
+
objects: [
|
|
7855
|
+
import_metadata_core3.SysMetadataObject,
|
|
7856
|
+
import_metadata_core3.SysMetadataHistoryObject,
|
|
7857
|
+
import_metadata_core3.SysMetadataAuditObject,
|
|
7858
|
+
import_metadata_core3.SysViewDefinitionObject
|
|
7859
|
+
]
|
|
7860
|
+
});
|
|
7861
|
+
}
|
|
7712
7862
|
const protocolShim = new ObjectStackProtocolImplementation(
|
|
7713
7863
|
this.ql,
|
|
7714
7864
|
() => ctx.getServices ? ctx.getServices() : /* @__PURE__ */ new Map(),
|