@objectstack/objectql 7.6.0 → 7.8.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 +120 -10
- package/dist/index.d.ts +120 -10
- package/dist/index.js +186 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -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) {
|
|
@@ -1219,6 +1219,30 @@ var SysMetadataRepository = class {
|
|
|
1219
1219
|
yield header;
|
|
1220
1220
|
}
|
|
1221
1221
|
}
|
|
1222
|
+
/**
|
|
1223
|
+
* List pending DRAFT rows (ADR-0033) for this org, optionally narrowed by
|
|
1224
|
+
* `type` and/or `packageId`. Unlike {@link list} (which is hard-scoped to
|
|
1225
|
+
* `state='active'`), this reads `state='draft'` so the console can surface
|
|
1226
|
+
* what an AI authored but a human hasn't published yet. Returns a light
|
|
1227
|
+
* header projection (no body) suitable for a "pending changes" list.
|
|
1228
|
+
*/
|
|
1229
|
+
async listDrafts(filter) {
|
|
1230
|
+
this.assertOpen();
|
|
1231
|
+
const where = {
|
|
1232
|
+
organization_id: this.organizationId,
|
|
1233
|
+
state: "draft"
|
|
1234
|
+
};
|
|
1235
|
+
if (filter?.type) where.type = filter.type;
|
|
1236
|
+
if (filter?.packageId) where.package_id = filter.packageId;
|
|
1237
|
+
const rows = await this.engine.find("sys_metadata", { where });
|
|
1238
|
+
return rows.map((row) => ({
|
|
1239
|
+
type: row.type,
|
|
1240
|
+
name: row.name,
|
|
1241
|
+
packageId: row.package_id ?? null,
|
|
1242
|
+
updatedAt: row.updated_at ?? row.created_at ?? null,
|
|
1243
|
+
updatedBy: row.updated_by ?? row.created_by ?? null
|
|
1244
|
+
}));
|
|
1245
|
+
}
|
|
1222
1246
|
/**
|
|
1223
1247
|
* Yield every history event for `(org, type?, name?)` from the
|
|
1224
1248
|
* durable log, ordered by per-(type,name) `version` ascending. When
|
|
@@ -1733,6 +1757,13 @@ function resolveOverlaySchema(type, _item) {
|
|
|
1733
1757
|
const singular = PLURAL_TO_SINGULAR3[type] ?? type;
|
|
1734
1758
|
return getMetadataTypeSchema2(singular) ?? null;
|
|
1735
1759
|
}
|
|
1760
|
+
function normalizeViewMetadata(type, item, saveName) {
|
|
1761
|
+
const singular = PLURAL_TO_SINGULAR3[type] ?? type;
|
|
1762
|
+
if (singular !== "view") return item;
|
|
1763
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
1764
|
+
const it = item;
|
|
1765
|
+
return it.name ? it : { ...it, name: saveName };
|
|
1766
|
+
}
|
|
1736
1767
|
function mergeArtifactProtection(item, artifactItem) {
|
|
1737
1768
|
if (item === void 0 || item === null) return item;
|
|
1738
1769
|
if (artifactItem === void 0 || artifactItem === null) return item;
|
|
@@ -3769,6 +3800,24 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3769
3800
|
);
|
|
3770
3801
|
}
|
|
3771
3802
|
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Ensure a just-PUBLISHED object's physical table exists so it is usable
|
|
3805
|
+
* for data CRUD immediately — without a server restart. Registering the
|
|
3806
|
+
* object (above) only updates the in-memory registry; the table is created
|
|
3807
|
+
* by the driver's schema sync, which otherwise only runs at boot. Without
|
|
3808
|
+
* this, inserting into a freshly-published object fails with "no such
|
|
3809
|
+
* table" (surfaced as `object_not_found`) until the next restart.
|
|
3810
|
+
* Best-effort + non-fatal: drivers without DDL (or read-only datasources)
|
|
3811
|
+
* simply no-op, and a sync failure must not abort the publish.
|
|
3812
|
+
*/
|
|
3813
|
+
async ensureObjectStorage(type, name) {
|
|
3814
|
+
if (type !== "object" && type !== "objects") return;
|
|
3815
|
+
try {
|
|
3816
|
+
await this.engine.syncObjectSchema(name);
|
|
3817
|
+
} catch (err) {
|
|
3818
|
+
console.warn(`[Protocol] table sync failed for object '${name}': ${err?.message ?? err}`);
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3772
3821
|
async saveMetaItem(request) {
|
|
3773
3822
|
if (!request.item) {
|
|
3774
3823
|
throw new Error("Item data is required");
|
|
@@ -3842,6 +3891,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3842
3891
|
throw err;
|
|
3843
3892
|
}
|
|
3844
3893
|
}
|
|
3894
|
+
request.item = normalizeViewMetadata(request.type, request.item, request.name);
|
|
3845
3895
|
{
|
|
3846
3896
|
const schema = resolveOverlaySchema(request.type, request.item);
|
|
3847
3897
|
if (schema) {
|
|
@@ -3896,6 +3946,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3896
3946
|
});
|
|
3897
3947
|
if (mode === "publish") {
|
|
3898
3948
|
this.applyObjectRegistryMutation(request);
|
|
3949
|
+
await this.ensureObjectStorage(request.type, request.name);
|
|
3899
3950
|
}
|
|
3900
3951
|
await this.recordMetadataAudit({
|
|
3901
3952
|
type: request.type,
|
|
@@ -4066,6 +4117,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4066
4117
|
name: request.name,
|
|
4067
4118
|
item: result.item.body
|
|
4068
4119
|
});
|
|
4120
|
+
await this.ensureObjectStorage(request.type, request.name);
|
|
4069
4121
|
return {
|
|
4070
4122
|
success: true,
|
|
4071
4123
|
version: result.version,
|
|
@@ -4086,6 +4138,66 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4086
4138
|
throw err;
|
|
4087
4139
|
}
|
|
4088
4140
|
}
|
|
4141
|
+
/**
|
|
4142
|
+
* List pending DRAFT metadata (ADR-0033) for the org, optionally narrowed
|
|
4143
|
+
* by `packageId` and/or `type`. The list reads of `getMetaItems` only see
|
|
4144
|
+
* the ACTIVE registry; this exposes what an AI authored but a human hasn't
|
|
4145
|
+
* published yet, so the console can show a "pending changes" surface and a
|
|
4146
|
+
* just-built app package isn't displayed as empty. No body is returned.
|
|
4147
|
+
*/
|
|
4148
|
+
async listDrafts(request) {
|
|
4149
|
+
await this.ensureOverlayIndex();
|
|
4150
|
+
const orgId = request?.organizationId ?? null;
|
|
4151
|
+
const repo = this.getOverlayRepo(orgId);
|
|
4152
|
+
const drafts = await repo.listDrafts({
|
|
4153
|
+
...request?.type ? { type: PLURAL_TO_SINGULAR3[request.type] ?? request.type } : {},
|
|
4154
|
+
...request?.packageId ? { packageId: request.packageId } : {}
|
|
4155
|
+
});
|
|
4156
|
+
return { drafts };
|
|
4157
|
+
}
|
|
4158
|
+
/**
|
|
4159
|
+
* Publish every pending DRAFT bound to a package in one shot (ADR-0033) —
|
|
4160
|
+
* the "publish whole app" action. Promotes each draft→active by reusing the
|
|
4161
|
+
* per-item {@link publishMetaItem} primitive (which runs the overridable /
|
|
4162
|
+
* lock guards and refreshes the runtime registry), so this needs NO
|
|
4163
|
+
* `metadata` service (unlike `MetadataService.publishPackage`, which reads
|
|
4164
|
+
* the in-memory registry and 503s when that service is absent). Per-item
|
|
4165
|
+
* failures are collected and do NOT abort the rest.
|
|
4166
|
+
*/
|
|
4167
|
+
async publishPackageDrafts(request) {
|
|
4168
|
+
await this.ensureOverlayIndex();
|
|
4169
|
+
const orgId = request.organizationId ?? null;
|
|
4170
|
+
const repo = this.getOverlayRepo(orgId);
|
|
4171
|
+
const drafts = await repo.listDrafts({ packageId: request.packageId });
|
|
4172
|
+
const published = [];
|
|
4173
|
+
const failed = [];
|
|
4174
|
+
for (const d of drafts) {
|
|
4175
|
+
try {
|
|
4176
|
+
const r = await this.publishMetaItem({
|
|
4177
|
+
type: d.type,
|
|
4178
|
+
name: d.name,
|
|
4179
|
+
...request.organizationId ? { organizationId: request.organizationId } : {},
|
|
4180
|
+
...request.actor ? { actor: request.actor } : {},
|
|
4181
|
+
message: `publish app package '${request.packageId}'`
|
|
4182
|
+
});
|
|
4183
|
+
published.push({ type: d.type, name: d.name, version: r.version });
|
|
4184
|
+
} catch (e) {
|
|
4185
|
+
failed.push({
|
|
4186
|
+
type: d.type,
|
|
4187
|
+
name: d.name,
|
|
4188
|
+
error: e?.message ?? "publish failed",
|
|
4189
|
+
...e?.code ? { code: e.code } : {}
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
return {
|
|
4194
|
+
success: failed.length === 0 && published.length > 0,
|
|
4195
|
+
publishedCount: published.length,
|
|
4196
|
+
failedCount: failed.length,
|
|
4197
|
+
published,
|
|
4198
|
+
failed
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4089
4201
|
/**
|
|
4090
4202
|
* Restore the body recorded at history `toVersion` as the new
|
|
4091
4203
|
* live row. Writes a history event with `op='revert'`. 404
|
|
@@ -4623,6 +4735,39 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4623
4735
|
const unsubscribed = await svc.unsubscribe(request.object, request.recordId, "current_user");
|
|
4624
4736
|
return { success: true, data: { object: request.object, recordId: request.recordId, unsubscribed } };
|
|
4625
4737
|
}
|
|
4738
|
+
/**
|
|
4739
|
+
* Install a package from a manifest — the single canonical write primitive
|
|
4740
|
+
* for the package subsystem (ADR-0033 consolidation).
|
|
4741
|
+
*
|
|
4742
|
+
* It writes BOTH stores that the runtime keeps for packages, so a package
|
|
4743
|
+
* surfaces consistently no matter which read path is used:
|
|
4744
|
+
* 1. the in-memory `SchemaRegistry` (what the dispatcher's
|
|
4745
|
+
* `/api/v1/packages` list/detail and `getMetaItems({type:'package'})`
|
|
4746
|
+
* read — i.e. what Studio's package selector shows), and
|
|
4747
|
+
* 2. the durable `sys_packages` table via the optional `package` service
|
|
4748
|
+
* (so the package survives a restart; that service re-hydrates these
|
|
4749
|
+
* rows back into the registry on boot).
|
|
4750
|
+
*
|
|
4751
|
+
* The DB write is best-effort and non-fatal: when the `package` service is
|
|
4752
|
+
* absent (e.g. the `marketplace` capability is off) the package is still
|
|
4753
|
+
* registered in-memory and visible for the lifetime of the process.
|
|
4754
|
+
*/
|
|
4755
|
+
async installPackage(request) {
|
|
4756
|
+
const manifest = request.manifest;
|
|
4757
|
+
const pkg = this.engine.registry.installPackage(manifest, request.settings);
|
|
4758
|
+
try {
|
|
4759
|
+
const services = this.getServicesRegistry?.();
|
|
4760
|
+
const pkgSvc = services?.get("package");
|
|
4761
|
+
if (pkgSvc?.publish && manifest?.version) {
|
|
4762
|
+
await pkgSvc.publish({ manifest, metadata: {} });
|
|
4763
|
+
}
|
|
4764
|
+
} catch (e) {
|
|
4765
|
+
console.warn(
|
|
4766
|
+
`[protocol.installPackage] sys_packages persist skipped for '${manifest?.id}': ${e?.message}`
|
|
4767
|
+
);
|
|
4768
|
+
}
|
|
4769
|
+
return { package: pkg, message: `Installed package: ${manifest?.id}` };
|
|
4770
|
+
}
|
|
4626
4771
|
};
|
|
4627
4772
|
/**
|
|
4628
4773
|
* Metadata types that are customer-overridable via {@link saveMetaItem}/
|
|
@@ -7014,7 +7159,11 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7014
7159
|
const driver = this.getDriver(object);
|
|
7015
7160
|
let id = data.id;
|
|
7016
7161
|
if (!id && options?.where && typeof options.where === "object" && "id" in options.where) {
|
|
7017
|
-
|
|
7162
|
+
const whereId = options.where.id;
|
|
7163
|
+
const t = typeof whereId;
|
|
7164
|
+
if (whereId !== null && (t === "string" || t === "number" || t === "bigint")) {
|
|
7165
|
+
id = whereId;
|
|
7166
|
+
}
|
|
7018
7167
|
}
|
|
7019
7168
|
const opCtx = {
|
|
7020
7169
|
object,
|
|
@@ -7413,6 +7562,23 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7413
7562
|
}
|
|
7414
7563
|
}
|
|
7415
7564
|
}
|
|
7565
|
+
/**
|
|
7566
|
+
* Sync a SINGLE object's physical storage (create/alter its table) on
|
|
7567
|
+
* demand. Boot-time {@link syncSchemas} runs once at startup, so an object
|
|
7568
|
+
* that becomes live at runtime (e.g. publishing a drafted object) has a
|
|
7569
|
+
* registry entry but no table — data CRUD then fails with "no such table"
|
|
7570
|
+
* until the next restart. Calling this right after the object is registered
|
|
7571
|
+
* makes it immediately usable. Idempotent: the SQL driver only creates the
|
|
7572
|
+
* table when absent (and alters to add new columns).
|
|
7573
|
+
*/
|
|
7574
|
+
async syncObjectSchema(objectName) {
|
|
7575
|
+
const obj = this._registry.getObject(objectName);
|
|
7576
|
+
if (!obj) return;
|
|
7577
|
+
const driver = this.getDriverForObject(objectName);
|
|
7578
|
+
if (!driver || typeof driver.syncSchema !== "function") return;
|
|
7579
|
+
const tableName = StorageNameMapping.resolveTableName(obj);
|
|
7580
|
+
await driver.syncSchema(tableName, obj);
|
|
7581
|
+
}
|
|
7416
7582
|
/**
|
|
7417
7583
|
* Get a registered driver by datasource name.
|
|
7418
7584
|
* Alias matching @objectql/core datasource() API.
|
|
@@ -7739,6 +7905,12 @@ var MetadataFacade = class {
|
|
|
7739
7905
|
|
|
7740
7906
|
// src/plugin.ts
|
|
7741
7907
|
import { StorageNameMapping as StorageNameMapping2 } from "@objectstack/spec/system";
|
|
7908
|
+
import {
|
|
7909
|
+
SysMetadataObject,
|
|
7910
|
+
SysMetadataHistoryObject,
|
|
7911
|
+
SysMetadataAuditObject,
|
|
7912
|
+
SysViewDefinitionObject
|
|
7913
|
+
} from "@objectstack/metadata-core";
|
|
7742
7914
|
function hasLoadMetaFromDb(service) {
|
|
7743
7915
|
return typeof service === "object" && service !== null && typeof service["loadMetaFromDb"] === "function";
|
|
7744
7916
|
}
|
|
@@ -7775,6 +7947,21 @@ var ObjectQLPlugin = class {
|
|
|
7775
7947
|
ctx.logger.info("ObjectQL engine registered", {
|
|
7776
7948
|
services: ["objectql", "data", "manifest"]
|
|
7777
7949
|
});
|
|
7950
|
+
if (this.environmentId === void 0) {
|
|
7951
|
+
this.ql.registerApp({
|
|
7952
|
+
id: "com.objectstack.metadata-objects",
|
|
7953
|
+
name: "Metadata Platform Objects",
|
|
7954
|
+
version: "1.0.0",
|
|
7955
|
+
type: "plugin",
|
|
7956
|
+
scope: "system",
|
|
7957
|
+
objects: [
|
|
7958
|
+
SysMetadataObject,
|
|
7959
|
+
SysMetadataHistoryObject,
|
|
7960
|
+
SysMetadataAuditObject,
|
|
7961
|
+
SysViewDefinitionObject
|
|
7962
|
+
]
|
|
7963
|
+
});
|
|
7964
|
+
}
|
|
7778
7965
|
const protocolShim = new ObjectStackProtocolImplementation(
|
|
7779
7966
|
this.ql,
|
|
7780
7967
|
() => ctx.getServices ? ctx.getServices() : /* @__PURE__ */ new Map(),
|