@objectstack/objectql 7.7.0 → 7.9.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 +207 -3
- package/dist/index.d.ts +207 -3
- package/dist/index.js +378 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -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;
|
|
@@ -2355,6 +2386,44 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
2355
2386
|
}
|
|
2356
2387
|
} catch {
|
|
2357
2388
|
}
|
|
2389
|
+
if (request.previewDrafts) {
|
|
2390
|
+
try {
|
|
2391
|
+
const orgId = request.organizationId;
|
|
2392
|
+
const queryDrafts = async (oid) => {
|
|
2393
|
+
const whereClause = { type: request.type, state: "draft", organization_id: oid };
|
|
2394
|
+
if (packageId) whereClause.package_id = packageId;
|
|
2395
|
+
let rs = await this.engine.find("sys_metadata", { where: whereClause });
|
|
2396
|
+
if (!rs || rs.length === 0) {
|
|
2397
|
+
const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
|
|
2398
|
+
if (alt) {
|
|
2399
|
+
const altWhere = { type: alt, state: "draft", organization_id: oid };
|
|
2400
|
+
if (packageId) altWhere.package_id = packageId;
|
|
2401
|
+
rs = await this.engine.find("sys_metadata", { where: altWhere });
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
return rs ?? [];
|
|
2405
|
+
};
|
|
2406
|
+
const draftRecords = [...await queryDrafts(null), ...orgId ? await queryDrafts(orgId) : []];
|
|
2407
|
+
if (draftRecords.length > 0) {
|
|
2408
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2409
|
+
for (const existing of items) {
|
|
2410
|
+
const entry = existing;
|
|
2411
|
+
if (entry && typeof entry === "object" && "name" in entry) byName.set(entry.name, entry);
|
|
2412
|
+
}
|
|
2413
|
+
for (const record of draftRecords) {
|
|
2414
|
+
const data = typeof record.metadata === "string" ? JSON.parse(record.metadata) : record.metadata;
|
|
2415
|
+
if (data && typeof data === "object" && "name" in data) {
|
|
2416
|
+
const recPkg = record.package_id ?? void 0;
|
|
2417
|
+
if (recPkg && data._packageId === void 0) data._packageId = recPkg;
|
|
2418
|
+
data._draft = true;
|
|
2419
|
+
byName.set(data.name, data);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
items = Array.from(byName.values());
|
|
2423
|
+
}
|
|
2424
|
+
} catch {
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2358
2427
|
try {
|
|
2359
2428
|
const services = this.getServicesRegistry?.();
|
|
2360
2429
|
const metadataService = services?.get("metadata");
|
|
@@ -2413,6 +2482,34 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
2413
2482
|
let item;
|
|
2414
2483
|
const orgId = request.organizationId;
|
|
2415
2484
|
const readState = request.state === "draft" ? "draft" : "active";
|
|
2485
|
+
if (request.previewDrafts && readState !== "draft") {
|
|
2486
|
+
try {
|
|
2487
|
+
const findDraft = async (oid) => {
|
|
2488
|
+
const rec = await this.engine.findOne("sys_metadata", {
|
|
2489
|
+
where: { type: request.type, name: request.name, state: "draft", organization_id: oid }
|
|
2490
|
+
});
|
|
2491
|
+
if (rec) return rec;
|
|
2492
|
+
const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
|
|
2493
|
+
if (alt) {
|
|
2494
|
+
return await this.engine.findOne("sys_metadata", {
|
|
2495
|
+
where: { type: alt, name: request.name, state: "draft", organization_id: oid }
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
return void 0;
|
|
2499
|
+
};
|
|
2500
|
+
const draftRec = (orgId ? await findDraft(orgId) : void 0) ?? await findDraft(null);
|
|
2501
|
+
if (draftRec) {
|
|
2502
|
+
const draftItem = typeof draftRec.metadata === "string" ? JSON.parse(draftRec.metadata) : draftRec.metadata;
|
|
2503
|
+
if (draftItem && typeof draftItem === "object") {
|
|
2504
|
+
const recPkg = draftRec.package_id ?? void 0;
|
|
2505
|
+
if (recPkg && draftItem._packageId === void 0) draftItem._packageId = recPkg;
|
|
2506
|
+
draftItem._draft = true;
|
|
2507
|
+
}
|
|
2508
|
+
return { type: request.type, name: request.name, item: decorateMetadataItem(request.type, draftItem) };
|
|
2509
|
+
}
|
|
2510
|
+
} catch {
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2416
2513
|
try {
|
|
2417
2514
|
const findOverlay = async (oid) => {
|
|
2418
2515
|
const where = {
|
|
@@ -3769,6 +3866,55 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3769
3866
|
);
|
|
3770
3867
|
}
|
|
3771
3868
|
}
|
|
3869
|
+
/**
|
|
3870
|
+
* Ensure a just-PUBLISHED object's physical table exists so it is usable
|
|
3871
|
+
* for data CRUD immediately — without a server restart. Registering the
|
|
3872
|
+
* object (above) only updates the in-memory registry; the table is created
|
|
3873
|
+
* by the driver's schema sync, which otherwise only runs at boot. Without
|
|
3874
|
+
* this, inserting into a freshly-published object fails with "no such
|
|
3875
|
+
* table" (surfaced as `object_not_found`) until the next restart.
|
|
3876
|
+
* Best-effort + non-fatal: drivers without DDL (or read-only datasources)
|
|
3877
|
+
* simply no-op, and a sync failure must not abort the publish.
|
|
3878
|
+
*/
|
|
3879
|
+
async ensureObjectStorage(type, name) {
|
|
3880
|
+
if (type !== "object" && type !== "objects") return;
|
|
3881
|
+
try {
|
|
3882
|
+
await this.engine.syncObjectSchema(name);
|
|
3883
|
+
} catch (err) {
|
|
3884
|
+
console.warn(`[Protocol] table sync failed for object '${name}': ${err?.message ?? err}`);
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
/**
|
|
3888
|
+
* Inverse of {@link ensureObjectStorage}: drop an object's physical table.
|
|
3889
|
+
* DESTRUCTIVE — deletes the table and all its rows. Only invoked when a
|
|
3890
|
+
* delete explicitly opts into storage teardown (see {@link deleteMetaItem}'s
|
|
3891
|
+
* `dropStorage`), so publishing an object solely to preview it can be undone
|
|
3892
|
+
* without leaving an orphan table. Best-effort: a failure is logged, not
|
|
3893
|
+
* thrown — the metadata delete already succeeded, and a stray table is
|
|
3894
|
+
* reclaimed by the next sync/drop rather than blocking the delete.
|
|
3895
|
+
*/
|
|
3896
|
+
async dropObjectStorage(type, name) {
|
|
3897
|
+
if (type !== "object" && type !== "objects") return;
|
|
3898
|
+
try {
|
|
3899
|
+
await this.engine.dropObjectSchema(name);
|
|
3900
|
+
} catch (err) {
|
|
3901
|
+
console.warn(`[Protocol] table drop failed for object '${name}': ${err?.message ?? err}`);
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Guard for storage teardown on delete. Drops a physical table only when
|
|
3906
|
+
* the caller opted in AND it is safe: object types only (others have no
|
|
3907
|
+
* table), active state only (drafts were never materialised), and never a
|
|
3908
|
+
* `sys_`-prefixed platform table.
|
|
3909
|
+
*/
|
|
3910
|
+
shouldDropStorage(type, name, dropStorage, state) {
|
|
3911
|
+
if (!dropStorage) return false;
|
|
3912
|
+
const singular = PLURAL_TO_SINGULAR3[type] ?? type;
|
|
3913
|
+
if (singular !== "object") return false;
|
|
3914
|
+
if (state !== "active") return false;
|
|
3915
|
+
if (name.startsWith("sys_")) return false;
|
|
3916
|
+
return true;
|
|
3917
|
+
}
|
|
3772
3918
|
async saveMetaItem(request) {
|
|
3773
3919
|
if (!request.item) {
|
|
3774
3920
|
throw new Error("Item data is required");
|
|
@@ -3842,6 +3988,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3842
3988
|
throw err;
|
|
3843
3989
|
}
|
|
3844
3990
|
}
|
|
3991
|
+
request.item = normalizeViewMetadata(request.type, request.item, request.name);
|
|
3845
3992
|
{
|
|
3846
3993
|
const schema = resolveOverlaySchema(request.type, request.item);
|
|
3847
3994
|
if (schema) {
|
|
@@ -3896,6 +4043,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
3896
4043
|
});
|
|
3897
4044
|
if (mode === "publish") {
|
|
3898
4045
|
this.applyObjectRegistryMutation(request);
|
|
4046
|
+
await this.ensureObjectStorage(request.type, request.name);
|
|
3899
4047
|
}
|
|
3900
4048
|
await this.recordMetadataAudit({
|
|
3901
4049
|
type: request.type,
|
|
@@ -4066,6 +4214,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4066
4214
|
name: request.name,
|
|
4067
4215
|
item: result.item.body
|
|
4068
4216
|
});
|
|
4217
|
+
await this.ensureObjectStorage(request.type, request.name);
|
|
4069
4218
|
return {
|
|
4070
4219
|
success: true,
|
|
4071
4220
|
version: result.version,
|
|
@@ -4086,6 +4235,160 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4086
4235
|
throw err;
|
|
4087
4236
|
}
|
|
4088
4237
|
}
|
|
4238
|
+
/**
|
|
4239
|
+
* List pending DRAFT metadata (ADR-0033) for the org, optionally narrowed
|
|
4240
|
+
* by `packageId` and/or `type`. The list reads of `getMetaItems` only see
|
|
4241
|
+
* the ACTIVE registry; this exposes what an AI authored but a human hasn't
|
|
4242
|
+
* published yet, so the console can show a "pending changes" surface and a
|
|
4243
|
+
* just-built app package isn't displayed as empty. No body is returned.
|
|
4244
|
+
*/
|
|
4245
|
+
async listDrafts(request) {
|
|
4246
|
+
await this.ensureOverlayIndex();
|
|
4247
|
+
const orgId = request?.organizationId ?? null;
|
|
4248
|
+
const repo = this.getOverlayRepo(orgId);
|
|
4249
|
+
const drafts = await repo.listDrafts({
|
|
4250
|
+
...request?.type ? { type: PLURAL_TO_SINGULAR3[request.type] ?? request.type } : {},
|
|
4251
|
+
...request?.packageId ? { packageId: request.packageId } : {}
|
|
4252
|
+
});
|
|
4253
|
+
return { drafts };
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Publish every pending DRAFT bound to a package in one shot (ADR-0033) —
|
|
4257
|
+
* the "publish whole app" action. Promotes each draft→active by reusing the
|
|
4258
|
+
* per-item {@link publishMetaItem} primitive (which runs the overridable /
|
|
4259
|
+
* lock guards and refreshes the runtime registry), so this needs NO
|
|
4260
|
+
* `metadata` service (unlike `MetadataService.publishPackage`, which reads
|
|
4261
|
+
* the in-memory registry and 503s when that service is absent). Per-item
|
|
4262
|
+
* failures are collected and do NOT abort the rest.
|
|
4263
|
+
*/
|
|
4264
|
+
async publishPackageDrafts(request) {
|
|
4265
|
+
await this.ensureOverlayIndex();
|
|
4266
|
+
const orgId = request.organizationId ?? null;
|
|
4267
|
+
const repo = this.getOverlayRepo(orgId);
|
|
4268
|
+
const drafts = await repo.listDrafts({ packageId: request.packageId });
|
|
4269
|
+
const published = [];
|
|
4270
|
+
const failed = [];
|
|
4271
|
+
for (const d of drafts) {
|
|
4272
|
+
try {
|
|
4273
|
+
const r = await this.publishMetaItem({
|
|
4274
|
+
type: d.type,
|
|
4275
|
+
name: d.name,
|
|
4276
|
+
...request.organizationId ? { organizationId: request.organizationId } : {},
|
|
4277
|
+
...request.actor ? { actor: request.actor } : {},
|
|
4278
|
+
message: `publish app package '${request.packageId}'`
|
|
4279
|
+
});
|
|
4280
|
+
published.push({ type: d.type, name: d.name, version: r.version });
|
|
4281
|
+
} catch (e) {
|
|
4282
|
+
failed.push({
|
|
4283
|
+
type: d.type,
|
|
4284
|
+
name: d.name,
|
|
4285
|
+
error: e?.message ?? "publish failed",
|
|
4286
|
+
...e?.code ? { code: e.code } : {}
|
|
4287
|
+
});
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
return {
|
|
4291
|
+
success: failed.length === 0 && published.length > 0,
|
|
4292
|
+
publishedCount: published.length,
|
|
4293
|
+
failedCount: failed.length,
|
|
4294
|
+
published,
|
|
4295
|
+
failed
|
|
4296
|
+
};
|
|
4297
|
+
}
|
|
4298
|
+
/**
|
|
4299
|
+
* Discard every pending DRAFT bound to a package — the NON-destructive
|
|
4300
|
+
* inverse of {@link publishPackageDrafts}. Drops only `state='draft'` rows
|
|
4301
|
+
* (via the per-item delete primitive), reverting the package to its last
|
|
4302
|
+
* published baseline; active/published metadata and physical tables are
|
|
4303
|
+
* left untouched.
|
|
4304
|
+
*
|
|
4305
|
+
* Use case: "I edited this app for a while and it turned out worse than
|
|
4306
|
+
* before — abandon all my changes." Routes through the sys_metadata path
|
|
4307
|
+
* (no metadata-service dependency, unlike `POST /packages/:id/revert`).
|
|
4308
|
+
*/
|
|
4309
|
+
async discardPackageDrafts(request) {
|
|
4310
|
+
await this.ensureOverlayIndex();
|
|
4311
|
+
const orgId = request.organizationId ?? null;
|
|
4312
|
+
const repo = this.getOverlayRepo(orgId);
|
|
4313
|
+
const drafts = await repo.listDrafts({ packageId: request.packageId });
|
|
4314
|
+
const discarded = [];
|
|
4315
|
+
const failed = [];
|
|
4316
|
+
for (const d of drafts) {
|
|
4317
|
+
try {
|
|
4318
|
+
await this.deleteMetaItem({
|
|
4319
|
+
type: d.type,
|
|
4320
|
+
name: d.name,
|
|
4321
|
+
state: "draft",
|
|
4322
|
+
...request.organizationId ? { organizationId: request.organizationId } : {},
|
|
4323
|
+
...request.actor ? { actor: request.actor } : {}
|
|
4324
|
+
});
|
|
4325
|
+
discarded.push({ type: d.type, name: d.name });
|
|
4326
|
+
} catch (e) {
|
|
4327
|
+
failed.push({
|
|
4328
|
+
type: d.type,
|
|
4329
|
+
name: d.name,
|
|
4330
|
+
error: e?.message ?? "discard failed",
|
|
4331
|
+
...e?.code ? { code: e.code } : {}
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
return {
|
|
4336
|
+
success: failed.length === 0 && discarded.length > 0,
|
|
4337
|
+
discardedCount: discarded.length,
|
|
4338
|
+
failedCount: failed.length,
|
|
4339
|
+
discarded,
|
|
4340
|
+
failed
|
|
4341
|
+
};
|
|
4342
|
+
}
|
|
4343
|
+
/**
|
|
4344
|
+
* Delete an ENTIRE package: every `sys_metadata` row bound to it (active
|
|
4345
|
+
* AND draft) and — by default — the physical table of each object it
|
|
4346
|
+
* defined. DESTRUCTIVE: removes the app and its data. Use case: "I don't
|
|
4347
|
+
* want this package anymore."
|
|
4348
|
+
*
|
|
4349
|
+
* Set `keepData: true` to remove the metadata but preserve object tables.
|
|
4350
|
+
* The `sys_`-table guard in {@link deleteMetaItem} still applies, so
|
|
4351
|
+
* platform storage is never dropped. Drafts are removed before active rows
|
|
4352
|
+
* so each object's table is torn down once. Per-item failures are collected
|
|
4353
|
+
* without aborting the rest.
|
|
4354
|
+
*/
|
|
4355
|
+
async deletePackage(request) {
|
|
4356
|
+
const where = { package_id: request.packageId };
|
|
4357
|
+
if (request.organizationId) where.organization_id = request.organizationId;
|
|
4358
|
+
const rows = await this.engine.find("sys_metadata", { where });
|
|
4359
|
+
const dropStorage = request.keepData !== true;
|
|
4360
|
+
const ordered = [...rows].sort((a, b) => (a.state === "draft" ? 0 : 1) - (b.state === "draft" ? 0 : 1));
|
|
4361
|
+
const deleted = [];
|
|
4362
|
+
const failed = [];
|
|
4363
|
+
for (const row of ordered) {
|
|
4364
|
+
const state = row.state === "draft" ? "draft" : "active";
|
|
4365
|
+
try {
|
|
4366
|
+
await this.deleteMetaItem({
|
|
4367
|
+
type: row.type,
|
|
4368
|
+
name: row.name,
|
|
4369
|
+
state,
|
|
4370
|
+
...row.organization_id ? { organizationId: row.organization_id } : {},
|
|
4371
|
+
...request.actor ? { actor: request.actor } : {},
|
|
4372
|
+
...dropStorage ? { dropStorage: true } : {}
|
|
4373
|
+
});
|
|
4374
|
+
deleted.push({ type: row.type, name: row.name, state });
|
|
4375
|
+
} catch (e) {
|
|
4376
|
+
failed.push({
|
|
4377
|
+
type: row.type,
|
|
4378
|
+
name: row.name,
|
|
4379
|
+
error: e?.message ?? "delete failed",
|
|
4380
|
+
...e?.code ? { code: e.code } : {}
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
return {
|
|
4385
|
+
success: failed.length === 0 && deleted.length > 0,
|
|
4386
|
+
deletedCount: deleted.length,
|
|
4387
|
+
failedCount: failed.length,
|
|
4388
|
+
deleted,
|
|
4389
|
+
failed
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4089
4392
|
/**
|
|
4090
4393
|
* Restore the body recorded at history `toVersion` as the new
|
|
4091
4394
|
* live row. Writes a history event with `op='revert'`. 404
|
|
@@ -4318,6 +4621,9 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4318
4621
|
} catch {
|
|
4319
4622
|
}
|
|
4320
4623
|
}
|
|
4624
|
+
if (this.shouldDropStorage(request.type, request.name, request.dropStorage, targetState)) {
|
|
4625
|
+
await this.dropObjectStorage(singularTypeForRepo, request.name);
|
|
4626
|
+
}
|
|
4321
4627
|
await this.recordMetadataAudit({
|
|
4322
4628
|
type: request.type,
|
|
4323
4629
|
name: request.name,
|
|
@@ -4366,6 +4672,12 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4366
4672
|
};
|
|
4367
4673
|
}
|
|
4368
4674
|
await this.engine.delete("sys_metadata", { where: { id: existing.id } });
|
|
4675
|
+
{
|
|
4676
|
+
const targetState = request.state === "draft" ? "draft" : "active";
|
|
4677
|
+
if (this.shouldDropStorage(request.type, request.name, request.dropStorage, targetState)) {
|
|
4678
|
+
await this.dropObjectStorage(PLURAL_TO_SINGULAR3[request.type] ?? request.type, request.name);
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4369
4681
|
if (this.environmentId === void 0) {
|
|
4370
4682
|
try {
|
|
4371
4683
|
const services = this.getServicesRegistry?.();
|
|
@@ -4623,6 +4935,39 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
|
|
|
4623
4935
|
const unsubscribed = await svc.unsubscribe(request.object, request.recordId, "current_user");
|
|
4624
4936
|
return { success: true, data: { object: request.object, recordId: request.recordId, unsubscribed } };
|
|
4625
4937
|
}
|
|
4938
|
+
/**
|
|
4939
|
+
* Install a package from a manifest — the single canonical write primitive
|
|
4940
|
+
* for the package subsystem (ADR-0033 consolidation).
|
|
4941
|
+
*
|
|
4942
|
+
* It writes BOTH stores that the runtime keeps for packages, so a package
|
|
4943
|
+
* surfaces consistently no matter which read path is used:
|
|
4944
|
+
* 1. the in-memory `SchemaRegistry` (what the dispatcher's
|
|
4945
|
+
* `/api/v1/packages` list/detail and `getMetaItems({type:'package'})`
|
|
4946
|
+
* read — i.e. what Studio's package selector shows), and
|
|
4947
|
+
* 2. the durable `sys_packages` table via the optional `package` service
|
|
4948
|
+
* (so the package survives a restart; that service re-hydrates these
|
|
4949
|
+
* rows back into the registry on boot).
|
|
4950
|
+
*
|
|
4951
|
+
* The DB write is best-effort and non-fatal: when the `package` service is
|
|
4952
|
+
* absent (e.g. the `marketplace` capability is off) the package is still
|
|
4953
|
+
* registered in-memory and visible for the lifetime of the process.
|
|
4954
|
+
*/
|
|
4955
|
+
async installPackage(request) {
|
|
4956
|
+
const manifest = request.manifest;
|
|
4957
|
+
const pkg = this.engine.registry.installPackage(manifest, request.settings);
|
|
4958
|
+
try {
|
|
4959
|
+
const services = this.getServicesRegistry?.();
|
|
4960
|
+
const pkgSvc = services?.get("package");
|
|
4961
|
+
if (pkgSvc?.publish && manifest?.version) {
|
|
4962
|
+
await pkgSvc.publish({ manifest, metadata: {} });
|
|
4963
|
+
}
|
|
4964
|
+
} catch (e) {
|
|
4965
|
+
console.warn(
|
|
4966
|
+
`[protocol.installPackage] sys_packages persist skipped for '${manifest?.id}': ${e?.message}`
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4969
|
+
return { package: pkg, message: `Installed package: ${manifest?.id}` };
|
|
4970
|
+
}
|
|
4626
4971
|
};
|
|
4627
4972
|
/**
|
|
4628
4973
|
* Metadata types that are customer-overridable via {@link saveMetaItem}/
|
|
@@ -7417,6 +7762,39 @@ var _ObjectQL = class _ObjectQL {
|
|
|
7417
7762
|
}
|
|
7418
7763
|
}
|
|
7419
7764
|
}
|
|
7765
|
+
/**
|
|
7766
|
+
* Sync a SINGLE object's physical storage (create/alter its table) on
|
|
7767
|
+
* demand. Boot-time {@link syncSchemas} runs once at startup, so an object
|
|
7768
|
+
* that becomes live at runtime (e.g. publishing a drafted object) has a
|
|
7769
|
+
* registry entry but no table — data CRUD then fails with "no such table"
|
|
7770
|
+
* until the next restart. Calling this right after the object is registered
|
|
7771
|
+
* makes it immediately usable. Idempotent: the SQL driver only creates the
|
|
7772
|
+
* table when absent (and alters to add new columns).
|
|
7773
|
+
*/
|
|
7774
|
+
async syncObjectSchema(objectName) {
|
|
7775
|
+
const obj = this._registry.getObject(objectName);
|
|
7776
|
+
if (!obj) return;
|
|
7777
|
+
const driver = this.getDriverForObject(objectName);
|
|
7778
|
+
if (!driver || typeof driver.syncSchema !== "function") return;
|
|
7779
|
+
const tableName = StorageNameMapping.resolveTableName(obj);
|
|
7780
|
+
await driver.syncSchema(tableName, obj);
|
|
7781
|
+
}
|
|
7782
|
+
/**
|
|
7783
|
+
* Drop the physical storage (table/collection) backing an object — the
|
|
7784
|
+
* inverse of {@link syncObjectSchema}. DESTRUCTIVE: deletes all rows in the
|
|
7785
|
+
* table. Used by the protocol delete path when the caller explicitly opts
|
|
7786
|
+
* into storage teardown (e.g. discarding an object that was published only
|
|
7787
|
+
* to preview it). No-op when the object's driver does not expose `dropTable`.
|
|
7788
|
+
* Resolves the physical table name from the registered definition, falling
|
|
7789
|
+
* back to the bare name if the def was already removed.
|
|
7790
|
+
*/
|
|
7791
|
+
async dropObjectSchema(objectName) {
|
|
7792
|
+
const obj = this._registry.getObject(objectName);
|
|
7793
|
+
const driver = this.getDriverForObject(objectName);
|
|
7794
|
+
if (!driver || typeof driver.dropTable !== "function") return;
|
|
7795
|
+
const tableName = StorageNameMapping.resolveTableName(obj ?? { name: objectName });
|
|
7796
|
+
await driver.dropTable(tableName);
|
|
7797
|
+
}
|
|
7420
7798
|
/**
|
|
7421
7799
|
* Get a registered driver by datasource name.
|
|
7422
7800
|
* Alias matching @objectql/core datasource() API.
|