@objectstack/objectql 1.0.10 → 1.0.12

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.mjs CHANGED
@@ -1,30 +1,277 @@
1
1
  // src/registry.ts
2
2
  import { ObjectSchema } from "@objectstack/spec/data";
3
- import { ManifestSchema } from "@objectstack/spec/kernel";
3
+ import { ManifestSchema, InstalledPackageSchema } from "@objectstack/spec/kernel";
4
4
  import { AppSchema } from "@objectstack/spec/ui";
5
+ var RESERVED_NAMESPACES = /* @__PURE__ */ new Set(["base", "system"]);
6
+ var DEFAULT_OWNER_PRIORITY = 100;
7
+ var DEFAULT_EXTENDER_PRIORITY = 200;
8
+ function computeFQN(namespace, shortName) {
9
+ if (!namespace || RESERVED_NAMESPACES.has(namespace)) {
10
+ return shortName;
11
+ }
12
+ return `${namespace}__${shortName}`;
13
+ }
14
+ function parseFQN(fqn) {
15
+ const idx = fqn.indexOf("__");
16
+ if (idx === -1) {
17
+ return { namespace: void 0, shortName: fqn };
18
+ }
19
+ return {
20
+ namespace: fqn.slice(0, idx),
21
+ shortName: fqn.slice(idx + 2)
22
+ };
23
+ }
24
+ function mergeObjectDefinitions(base, extension) {
25
+ const merged = { ...base };
26
+ if (extension.fields) {
27
+ merged.fields = { ...base.fields, ...extension.fields };
28
+ }
29
+ if (extension.validations) {
30
+ merged.validations = [...base.validations || [], ...extension.validations];
31
+ }
32
+ if (extension.indexes) {
33
+ merged.indexes = [...base.indexes || [], ...extension.indexes];
34
+ }
35
+ if (extension.label !== void 0) merged.label = extension.label;
36
+ if (extension.pluralLabel !== void 0) merged.pluralLabel = extension.pluralLabel;
37
+ if (extension.description !== void 0) merged.description = extension.description;
38
+ return merged;
39
+ }
5
40
  var SchemaRegistry = class {
41
+ static get logLevel() {
42
+ return this._logLevel;
43
+ }
44
+ static set logLevel(level) {
45
+ this._logLevel = level;
46
+ }
47
+ static log(msg) {
48
+ if (this._logLevel === "silent" || this._logLevel === "error" || this._logLevel === "warn") return;
49
+ console.log(msg);
50
+ }
51
+ // ==========================================
52
+ // Namespace Management
53
+ // ==========================================
54
+ /**
55
+ * Register a namespace for a package.
56
+ * Enforces namespace uniqueness within the instance.
57
+ *
58
+ * @throws Error if namespace is already registered to a different package
59
+ */
60
+ static registerNamespace(namespace, packageId) {
61
+ if (!namespace) return;
62
+ const existing = this.namespaceRegistry.get(namespace);
63
+ if (existing && existing !== packageId) {
64
+ throw new Error(
65
+ `Namespace "${namespace}" is already registered to package "${existing}". Package "${packageId}" cannot use the same namespace.`
66
+ );
67
+ }
68
+ this.namespaceRegistry.set(namespace, packageId);
69
+ this.log(`[Registry] Registered namespace: ${namespace} \u2192 ${packageId}`);
70
+ }
71
+ /**
72
+ * Unregister a namespace when a package is uninstalled.
73
+ */
74
+ static unregisterNamespace(namespace, packageId) {
75
+ const existing = this.namespaceRegistry.get(namespace);
76
+ if (existing === packageId) {
77
+ this.namespaceRegistry.delete(namespace);
78
+ this.log(`[Registry] Unregistered namespace: ${namespace}`);
79
+ }
80
+ }
81
+ /**
82
+ * Get the package that owns a namespace.
83
+ */
84
+ static getNamespaceOwner(namespace) {
85
+ return this.namespaceRegistry.get(namespace);
86
+ }
87
+ // ==========================================
88
+ // Object Registration (Ownership Model)
89
+ // ==========================================
90
+ /**
91
+ * Register an object with ownership semantics.
92
+ *
93
+ * @param schema - The object definition
94
+ * @param packageId - The owning package ID
95
+ * @param namespace - The package namespace (for FQN computation)
96
+ * @param ownership - 'own' (single owner) or 'extend' (additive merge)
97
+ * @param priority - Merge priority (lower applied first, higher wins on conflict)
98
+ *
99
+ * @throws Error if trying to 'own' an object that already has an owner
100
+ */
101
+ static registerObject(schema, packageId, namespace, ownership = "own", priority = ownership === "own" ? DEFAULT_OWNER_PRIORITY : DEFAULT_EXTENDER_PRIORITY) {
102
+ const shortName = schema.name;
103
+ const fqn = computeFQN(namespace, shortName);
104
+ if (namespace) {
105
+ this.registerNamespace(namespace, packageId);
106
+ }
107
+ let contributors = this.objectContributors.get(fqn);
108
+ if (!contributors) {
109
+ contributors = [];
110
+ this.objectContributors.set(fqn, contributors);
111
+ }
112
+ if (ownership === "own") {
113
+ const existingOwner = contributors.find((c) => c.ownership === "own");
114
+ if (existingOwner && existingOwner.packageId !== packageId) {
115
+ throw new Error(
116
+ `Object "${fqn}" is already owned by package "${existingOwner.packageId}". Package "${packageId}" cannot claim ownership. Use 'extend' to add fields.`
117
+ );
118
+ }
119
+ const idx = contributors.findIndex((c) => c.packageId === packageId && c.ownership === "own");
120
+ if (idx !== -1) {
121
+ contributors.splice(idx, 1);
122
+ console.warn(`[Registry] Re-registering owned object: ${fqn} from ${packageId}`);
123
+ }
124
+ } else {
125
+ const idx = contributors.findIndex((c) => c.packageId === packageId && c.ownership === "extend");
126
+ if (idx !== -1) {
127
+ contributors.splice(idx, 1);
128
+ }
129
+ }
130
+ const contributor = {
131
+ packageId,
132
+ namespace: namespace || "",
133
+ ownership,
134
+ priority,
135
+ definition: { ...schema, name: fqn }
136
+ // Store with FQN as name
137
+ };
138
+ contributors.push(contributor);
139
+ contributors.sort((a, b) => a.priority - b.priority);
140
+ this.mergedObjectCache.delete(fqn);
141
+ this.log(`[Registry] Registered object: ${fqn} (${ownership}, priority=${priority}) from ${packageId}`);
142
+ return fqn;
143
+ }
6
144
  /**
7
- * Universal Register Method
8
- * @param type The category of metadata (e.g., 'object', 'app', 'plugin')
9
- * @param item The metadata item itself
10
- * @param keyField The property to use as the unique key (default: 'name')
145
+ * Resolve an object by FQN, merging all contributions.
146
+ * Returns the merged object or undefined if not found.
11
147
  */
12
- static registerItem(type, item, keyField = "name") {
148
+ static resolveObject(fqn) {
149
+ const cached = this.mergedObjectCache.get(fqn);
150
+ if (cached) return cached;
151
+ const contributors = this.objectContributors.get(fqn);
152
+ if (!contributors || contributors.length === 0) {
153
+ return void 0;
154
+ }
155
+ const ownerContrib = contributors.find((c) => c.ownership === "own");
156
+ if (!ownerContrib) {
157
+ console.warn(`[Registry] Object "${fqn}" has extenders but no owner. Skipping.`);
158
+ return void 0;
159
+ }
160
+ let merged = { ...ownerContrib.definition };
161
+ for (const contrib of contributors) {
162
+ if (contrib.ownership === "extend") {
163
+ merged = mergeObjectDefinitions(merged, contrib.definition);
164
+ }
165
+ }
166
+ this.mergedObjectCache.set(fqn, merged);
167
+ return merged;
168
+ }
169
+ /**
170
+ * Get object by name (FQN or short name with fallback scan).
171
+ * For compatibility, tries exact match first, then scans for suffix match.
172
+ */
173
+ static getObject(name) {
174
+ const direct = this.resolveObject(name);
175
+ if (direct) return direct;
176
+ for (const fqn of this.objectContributors.keys()) {
177
+ const { shortName } = parseFQN(fqn);
178
+ if (shortName === name) {
179
+ return this.resolveObject(fqn);
180
+ }
181
+ }
182
+ return void 0;
183
+ }
184
+ /**
185
+ * Get all registered objects (merged).
186
+ *
187
+ * @param packageId - Optional filter: only objects contributed by this package
188
+ */
189
+ static getAllObjects(packageId) {
190
+ const results = [];
191
+ for (const fqn of this.objectContributors.keys()) {
192
+ if (packageId) {
193
+ const contributors = this.objectContributors.get(fqn);
194
+ const hasContribution = contributors?.some((c) => c.packageId === packageId);
195
+ if (!hasContribution) continue;
196
+ }
197
+ const merged = this.resolveObject(fqn);
198
+ if (merged) {
199
+ merged._packageId = this.getObjectOwner(fqn)?.packageId;
200
+ results.push(merged);
201
+ }
202
+ }
203
+ return results;
204
+ }
205
+ /**
206
+ * Get all contributors for an object.
207
+ */
208
+ static getObjectContributors(fqn) {
209
+ return this.objectContributors.get(fqn) || [];
210
+ }
211
+ /**
212
+ * Get the owner contributor for an object.
213
+ */
214
+ static getObjectOwner(fqn) {
215
+ const contributors = this.objectContributors.get(fqn);
216
+ return contributors?.find((c) => c.ownership === "own");
217
+ }
218
+ /**
219
+ * Unregister all objects contributed by a package.
220
+ *
221
+ * @throws Error if trying to uninstall an owner that has extenders
222
+ */
223
+ static unregisterObjectsByPackage(packageId, force = false) {
224
+ for (const [fqn, contributors] of this.objectContributors.entries()) {
225
+ const packageContribs = contributors.filter((c) => c.packageId === packageId);
226
+ for (const contrib of packageContribs) {
227
+ if (contrib.ownership === "own" && !force) {
228
+ const otherExtenders = contributors.filter(
229
+ (c) => c.packageId !== packageId && c.ownership === "extend"
230
+ );
231
+ if (otherExtenders.length > 0) {
232
+ throw new Error(
233
+ `Cannot uninstall package "${packageId}": object "${fqn}" is extended by ${otherExtenders.map((c) => c.packageId).join(", ")}. Uninstall extenders first.`
234
+ );
235
+ }
236
+ }
237
+ const idx = contributors.indexOf(contrib);
238
+ if (idx !== -1) {
239
+ contributors.splice(idx, 1);
240
+ this.log(`[Registry] Removed ${contrib.ownership} contribution to ${fqn} from ${packageId}`);
241
+ }
242
+ }
243
+ if (contributors.length === 0) {
244
+ this.objectContributors.delete(fqn);
245
+ }
246
+ this.mergedObjectCache.delete(fqn);
247
+ }
248
+ }
249
+ // ==========================================
250
+ // Generic Metadata (Non-Object Types)
251
+ // ==========================================
252
+ /**
253
+ * Universal Register Method for non-object metadata.
254
+ */
255
+ static registerItem(type, item, keyField = "name", packageId) {
13
256
  if (!this.metadata.has(type)) {
14
257
  this.metadata.set(type, /* @__PURE__ */ new Map());
15
258
  }
16
259
  const collection = this.metadata.get(type);
17
- const key = String(item[keyField]);
260
+ const baseName = String(item[keyField]);
261
+ if (packageId) {
262
+ item._packageId = packageId;
263
+ }
18
264
  try {
19
265
  this.validate(type, item);
20
266
  } catch (e) {
21
- console.error(`[Registry] Validation failed for ${type} ${key}: ${e.message}`);
267
+ console.error(`[Registry] Validation failed for ${type} ${baseName}: ${e.message}`);
22
268
  }
23
- if (collection.has(key)) {
24
- console.warn(`[Registry] Overwriting ${type}: ${key}`);
269
+ const storageKey = packageId ? `${packageId}:${baseName}` : baseName;
270
+ if (collection.has(storageKey)) {
271
+ console.warn(`[Registry] Overwriting ${type}: ${storageKey}`);
25
272
  }
26
- collection.set(key, item);
27
- console.log(`[Registry] Registered ${type}: ${key}`);
273
+ collection.set(storageKey, item);
274
+ this.log(`[Registry] Registered ${type}: ${storageKey}`);
28
275
  }
29
276
  /**
30
277
  * Validate Metadata against Spec Zod Schemas
@@ -33,9 +280,12 @@ var SchemaRegistry = class {
33
280
  if (type === "object") {
34
281
  return ObjectSchema.parse(item);
35
282
  }
36
- if (type === "app") {
283
+ if (type === "apps") {
37
284
  return AppSchema.parse(item);
38
285
  }
286
+ if (type === "package") {
287
+ return InstalledPackageSchema.parse(item);
288
+ }
39
289
  if (type === "plugin") {
40
290
  return ManifestSchema.parse(item);
41
291
  }
@@ -46,66 +296,198 @@ var SchemaRegistry = class {
46
296
  */
47
297
  static unregisterItem(type, name) {
48
298
  const collection = this.metadata.get(type);
49
- if (collection && collection.has(name)) {
50
- collection.delete(name);
51
- console.log(`[Registry] Unregistered ${type}: ${name}`);
52
- } else {
299
+ if (!collection) {
53
300
  console.warn(`[Registry] Attempted to unregister non-existent ${type}: ${name}`);
301
+ return;
302
+ }
303
+ if (collection.has(name)) {
304
+ collection.delete(name);
305
+ this.log(`[Registry] Unregistered ${type}: ${name}`);
306
+ return;
54
307
  }
308
+ for (const key of collection.keys()) {
309
+ if (key.endsWith(`:${name}`)) {
310
+ collection.delete(key);
311
+ this.log(`[Registry] Unregistered ${type}: ${key}`);
312
+ return;
313
+ }
314
+ }
315
+ console.warn(`[Registry] Attempted to unregister non-existent ${type}: ${name}`);
55
316
  }
56
317
  /**
57
318
  * Universal Get Method
58
319
  */
59
320
  static getItem(type, name) {
60
- return this.metadata.get(type)?.get(name);
321
+ if (type === "object" || type === "objects") {
322
+ return this.getObject(name);
323
+ }
324
+ const collection = this.metadata.get(type);
325
+ if (!collection) return void 0;
326
+ const direct = collection.get(name);
327
+ if (direct) return direct;
328
+ for (const [key, item] of collection) {
329
+ if (key.endsWith(`:${name}`)) return item;
330
+ }
331
+ return void 0;
61
332
  }
62
333
  /**
63
334
  * Universal List Method
64
335
  */
65
- static listItems(type) {
66
- return Array.from(this.metadata.get(type)?.values() || []);
336
+ static listItems(type, packageId) {
337
+ if (type === "object" || type === "objects") {
338
+ return this.getAllObjects(packageId);
339
+ }
340
+ const items = Array.from(this.metadata.get(type)?.values() || []);
341
+ if (packageId) {
342
+ return items.filter((item) => item._packageId === packageId);
343
+ }
344
+ return items;
67
345
  }
68
346
  /**
69
347
  * Get all registered metadata types (Kinds)
70
348
  */
71
349
  static getRegisteredTypes() {
72
- return Array.from(this.metadata.keys());
350
+ const types = Array.from(this.metadata.keys());
351
+ if (!types.includes("object") && this.objectContributors.size > 0) {
352
+ types.push("object");
353
+ }
354
+ return types;
73
355
  }
74
356
  // ==========================================
75
- // Typed Helper Methods (Shortcuts)
357
+ // Package Management
76
358
  // ==========================================
77
- /**
78
- * Object Helpers
79
- */
80
- static registerObject(schema) {
81
- this.registerItem("object", schema, "name");
359
+ static installPackage(manifest, settings) {
360
+ const now = (/* @__PURE__ */ new Date()).toISOString();
361
+ const pkg = {
362
+ manifest,
363
+ status: "installed",
364
+ enabled: true,
365
+ installedAt: now,
366
+ updatedAt: now,
367
+ settings
368
+ };
369
+ if (manifest.namespace) {
370
+ this.registerNamespace(manifest.namespace, manifest.id);
371
+ }
372
+ if (!this.metadata.has("package")) {
373
+ this.metadata.set("package", /* @__PURE__ */ new Map());
374
+ }
375
+ const collection = this.metadata.get("package");
376
+ if (collection.has(manifest.id)) {
377
+ console.warn(`[Registry] Overwriting package: ${manifest.id}`);
378
+ }
379
+ collection.set(manifest.id, pkg);
380
+ this.log(`[Registry] Installed package: ${manifest.id} (${manifest.name})`);
381
+ return pkg;
82
382
  }
83
- static getObject(name) {
84
- return this.getItem("object", name);
383
+ static uninstallPackage(id) {
384
+ const pkg = this.getPackage(id);
385
+ if (!pkg) {
386
+ console.warn(`[Registry] Package not found for uninstall: ${id}`);
387
+ return false;
388
+ }
389
+ if (pkg.manifest.namespace) {
390
+ this.unregisterNamespace(pkg.manifest.namespace, id);
391
+ }
392
+ this.unregisterObjectsByPackage(id);
393
+ const collection = this.metadata.get("package");
394
+ if (collection) {
395
+ collection.delete(id);
396
+ this.log(`[Registry] Uninstalled package: ${id}`);
397
+ return true;
398
+ }
399
+ return false;
85
400
  }
86
- static getAllObjects() {
87
- return this.listItems("object");
401
+ static getPackage(id) {
402
+ return this.metadata.get("package")?.get(id);
88
403
  }
89
- /**
90
- * Plugin Helpers
91
- */
404
+ static getAllPackages() {
405
+ return this.listItems("package");
406
+ }
407
+ static enablePackage(id) {
408
+ const pkg = this.getPackage(id);
409
+ if (pkg) {
410
+ pkg.enabled = true;
411
+ pkg.status = "installed";
412
+ pkg.statusChangedAt = (/* @__PURE__ */ new Date()).toISOString();
413
+ pkg.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
414
+ this.log(`[Registry] Enabled package: ${id}`);
415
+ }
416
+ return pkg;
417
+ }
418
+ static disablePackage(id) {
419
+ const pkg = this.getPackage(id);
420
+ if (pkg) {
421
+ pkg.enabled = false;
422
+ pkg.status = "disabled";
423
+ pkg.statusChangedAt = (/* @__PURE__ */ new Date()).toISOString();
424
+ pkg.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
425
+ this.log(`[Registry] Disabled package: ${id}`);
426
+ }
427
+ return pkg;
428
+ }
429
+ // ==========================================
430
+ // App Helpers
431
+ // ==========================================
432
+ static registerApp(app, packageId) {
433
+ this.registerItem("apps", app, "name", packageId);
434
+ }
435
+ static getApp(name) {
436
+ return this.getItem("apps", name);
437
+ }
438
+ static getAllApps() {
439
+ return this.listItems("apps");
440
+ }
441
+ // ==========================================
442
+ // Plugin Helpers
443
+ // ==========================================
92
444
  static registerPlugin(manifest) {
93
445
  this.registerItem("plugin", manifest, "id");
94
446
  }
95
447
  static getAllPlugins() {
96
448
  return this.listItems("plugin");
97
449
  }
98
- /**
99
- * Kind (Metadata Type) Helpers
100
- */
450
+ // ==========================================
451
+ // Kind Helpers
452
+ // ==========================================
101
453
  static registerKind(kind) {
102
454
  this.registerItem("kind", kind, "id");
103
455
  }
104
456
  static getAllKinds() {
105
457
  return this.listItems("kind");
106
458
  }
459
+ // ==========================================
460
+ // Reset (for testing)
461
+ // ==========================================
462
+ /**
463
+ * Clear all registry state. Use only for testing.
464
+ */
465
+ static reset() {
466
+ this.objectContributors.clear();
467
+ this.mergedObjectCache.clear();
468
+ this.namespaceRegistry.clear();
469
+ this.metadata.clear();
470
+ this.log("[Registry] Reset complete");
471
+ }
107
472
  };
108
- // Nested Map: Type -> Name/ID -> MetadataItem
473
+ // ==========================================
474
+ // Logging control
475
+ // ==========================================
476
+ /** Controls verbosity of registry console messages. Default: 'info'. */
477
+ SchemaRegistry._logLevel = "info";
478
+ // ==========================================
479
+ // Object-specific storage (Ownership Model)
480
+ // ==========================================
481
+ /** FQN → Contributor[] (all packages that own/extend this object) */
482
+ SchemaRegistry.objectContributors = /* @__PURE__ */ new Map();
483
+ /** FQN → Merged ServiceObject (cached, invalidated on changes) */
484
+ SchemaRegistry.mergedObjectCache = /* @__PURE__ */ new Map();
485
+ /** Namespace → PackageId (ensures namespace uniqueness) */
486
+ SchemaRegistry.namespaceRegistry = /* @__PURE__ */ new Map();
487
+ // ==========================================
488
+ // Generic metadata storage (non-object types)
489
+ // ==========================================
490
+ /** Type → Name/ID → MetadataItem */
109
491
  SchemaRegistry.metadata = /* @__PURE__ */ new Map();
110
492
 
111
493
  // src/protocol.ts
@@ -149,7 +531,7 @@ var ObjectStackProtocolImplementation = class {
149
531
  async getMetaItems(request) {
150
532
  return {
151
533
  type: request.type,
152
- items: SchemaRegistry.listItems(request.type)
534
+ items: SchemaRegistry.listItems(request.type, request.packageId)
153
535
  };
154
536
  }
155
537
  async getMetaItem(request) {
@@ -162,31 +544,57 @@ var ObjectStackProtocolImplementation = class {
162
544
  async getUiView(request) {
163
545
  const schema = SchemaRegistry.getObject(request.object);
164
546
  if (!schema) throw new Error(`Object ${request.object} not found`);
165
- let view;
547
+ const fields = schema.fields || {};
548
+ const fieldKeys = Object.keys(fields);
166
549
  if (request.type === "list") {
167
- view = {
168
- type: "list",
169
- object: request.object,
170
- columns: Object.keys(schema.fields || {}).slice(0, 5).map((f) => ({
171
- field: f,
172
- label: schema.fields[f].label || f
173
- }))
550
+ const priorityFields = ["name", "title", "label", "subject", "email", "status", "type", "category", "created_at"];
551
+ let columns = fieldKeys.filter((k) => priorityFields.includes(k));
552
+ if (columns.length < 5) {
553
+ const remaining = fieldKeys.filter((k) => !columns.includes(k) && k !== "id" && !fields[k].hidden);
554
+ columns = [...columns, ...remaining.slice(0, 5 - columns.length)];
555
+ }
556
+ return {
557
+ list: {
558
+ type: "grid",
559
+ object: request.object,
560
+ label: schema.label || schema.name,
561
+ columns: columns.map((f) => ({
562
+ field: f,
563
+ label: fields[f]?.label || f,
564
+ sortable: true
565
+ })),
566
+ sort: fields["created_at"] ? [{ field: "created_at", order: "desc" }] : void 0,
567
+ searchableFields: columns.slice(0, 3)
568
+ // Make first few textual columns searchable
569
+ }
174
570
  };
175
571
  } else {
176
- view = {
177
- type: "form",
178
- object: request.object,
179
- sections: [
180
- {
181
- label: "General",
182
- fields: Object.keys(schema.fields || {}).map((f) => ({
183
- field: f
184
- }))
185
- }
186
- ]
572
+ const formFields = fieldKeys.filter((k) => k !== "id" && k !== "created_at" && k !== "modified_at" && !fields[k].hidden).map((f) => ({
573
+ field: f,
574
+ label: fields[f]?.label,
575
+ required: fields[f]?.required,
576
+ readonly: fields[f]?.readonly,
577
+ type: fields[f]?.type,
578
+ // Default to 2 columns for most, 1 for textareas
579
+ colSpan: fields[f]?.type === "textarea" || fields[f]?.type === "html" ? 2 : 1
580
+ }));
581
+ return {
582
+ form: {
583
+ type: "simple",
584
+ object: request.object,
585
+ label: `Edit ${schema.label || schema.name}`,
586
+ sections: [
587
+ {
588
+ label: "General Information",
589
+ columns: 2,
590
+ collapsible: false,
591
+ collapsed: false,
592
+ fields: formFields
593
+ }
594
+ ]
595
+ }
187
596
  };
188
597
  }
189
- return view;
190
598
  }
191
599
  async findData(request) {
192
600
  const options = { ...request.query };
@@ -426,23 +834,98 @@ var ObjectQL = class {
426
834
  }
427
835
  /**
428
836
  * Register contribution (Manifest)
837
+ *
838
+ * Installs the manifest as a Package (the unit of installation),
839
+ * then decomposes it into individual metadata items (objects, apps, actions, etc.)
840
+ * and registers each into the SchemaRegistry.
841
+ *
842
+ * Key: Package ≠ App. The manifest is the package. The apps[] array inside
843
+ * the manifest contains UI navigation definitions (AppSchema).
429
844
  */
430
845
  registerApp(manifest) {
431
- const id = manifest.id;
432
- this.logger.debug("Registering app manifest", { id });
846
+ const id = manifest.id || manifest.name;
847
+ const namespace = manifest.namespace;
848
+ this.logger.debug("Registering package manifest", { id, namespace });
849
+ SchemaRegistry.installPackage(manifest);
850
+ this.logger.debug("Installed Package", { id: manifest.id, name: manifest.name, namespace });
433
851
  if (manifest.objects) {
434
852
  if (Array.isArray(manifest.objects)) {
435
853
  this.logger.debug("Registering objects from manifest (Array)", { id, objectCount: manifest.objects.length });
436
854
  for (const objDef of manifest.objects) {
437
- SchemaRegistry.registerObject(objDef);
438
- this.logger.debug("Registered Object", { object: objDef.name, from: id });
855
+ const fqn = SchemaRegistry.registerObject(objDef, id, namespace, "own");
856
+ this.logger.debug("Registered Object", { fqn, from: id });
439
857
  }
440
858
  } else {
441
859
  this.logger.debug("Registering objects from manifest (Map)", { id, objectCount: Object.keys(manifest.objects).length });
442
860
  for (const [name, objDef] of Object.entries(manifest.objects)) {
443
861
  objDef.name = name;
444
- SchemaRegistry.registerObject(objDef);
445
- this.logger.debug("Registered Object", { object: name, from: id });
862
+ const fqn = SchemaRegistry.registerObject(objDef, id, namespace, "own");
863
+ this.logger.debug("Registered Object", { fqn, from: id });
864
+ }
865
+ }
866
+ }
867
+ if (Array.isArray(manifest.objectExtensions) && manifest.objectExtensions.length > 0) {
868
+ this.logger.debug("Registering object extensions", { id, count: manifest.objectExtensions.length });
869
+ for (const ext of manifest.objectExtensions) {
870
+ const targetFqn = ext.extend;
871
+ const priority = ext.priority ?? 200;
872
+ const extDef = {
873
+ name: targetFqn,
874
+ // Use the target FQN as name
875
+ fields: ext.fields,
876
+ label: ext.label,
877
+ pluralLabel: ext.pluralLabel,
878
+ description: ext.description,
879
+ validations: ext.validations,
880
+ indexes: ext.indexes
881
+ };
882
+ SchemaRegistry.registerObject(extDef, id, void 0, "extend", priority);
883
+ this.logger.debug("Registered Object Extension", { target: targetFqn, priority, from: id });
884
+ }
885
+ }
886
+ if (Array.isArray(manifest.apps) && manifest.apps.length > 0) {
887
+ this.logger.debug("Registering apps from manifest", { id, count: manifest.apps.length });
888
+ for (const app of manifest.apps) {
889
+ const appName = app.name || app.id;
890
+ if (appName) {
891
+ SchemaRegistry.registerApp(app, id);
892
+ this.logger.debug("Registered App", { app: appName, from: id });
893
+ }
894
+ }
895
+ }
896
+ if (manifest.name && manifest.navigation && !manifest.apps?.length) {
897
+ SchemaRegistry.registerApp(manifest, id);
898
+ this.logger.debug("Registered manifest-as-app", { app: manifest.name, from: id });
899
+ }
900
+ const metadataArrayKeys = [
901
+ "actions",
902
+ "dashboards",
903
+ "reports",
904
+ "flows",
905
+ "agents",
906
+ "apis",
907
+ "ragPipelines",
908
+ "profiles",
909
+ "sharingRules"
910
+ ];
911
+ for (const key of metadataArrayKeys) {
912
+ const items = manifest[key];
913
+ if (Array.isArray(items) && items.length > 0) {
914
+ this.logger.debug(`Registering ${key} from manifest`, { id, count: items.length });
915
+ for (const item of items) {
916
+ const itemName = item.name || item.id;
917
+ if (itemName) {
918
+ SchemaRegistry.registerItem(key, item, "name", id);
919
+ }
920
+ }
921
+ }
922
+ }
923
+ const seedData = manifest.data;
924
+ if (Array.isArray(seedData) && seedData.length > 0) {
925
+ this.logger.debug("Registering seed data datasets", { id, count: seedData.length });
926
+ for (const dataset of seedData) {
927
+ if (dataset.object) {
928
+ SchemaRegistry.registerItem("data", dataset, "object", id);
446
929
  }
447
930
  }
448
931
  }
@@ -478,6 +961,23 @@ var ObjectQL = class {
478
961
  getSchema(objectName) {
479
962
  return SchemaRegistry.getObject(objectName);
480
963
  }
964
+ /**
965
+ * Resolve an object name to its Fully Qualified Name (FQN).
966
+ *
967
+ * Short names like 'task' are resolved to FQN like 'todo__task'
968
+ * via SchemaRegistry lookup. If no match is found, the name is
969
+ * returned as-is (for ad-hoc / unregistered objects).
970
+ *
971
+ * This ensures that all driver operations use a consistent key
972
+ * regardless of whether the caller uses the short name or FQN.
973
+ */
974
+ resolveObjectName(name) {
975
+ const schema = SchemaRegistry.getObject(name);
976
+ if (schema) {
977
+ return schema.name;
978
+ }
979
+ return name;
980
+ }
481
981
  /**
482
982
  * Helper to get the target driver
483
983
  */
@@ -561,6 +1061,7 @@ var ObjectQL = class {
561
1061
  // Data Access Methods (IDataEngine Interface)
562
1062
  // ============================================
563
1063
  async find(object, query) {
1064
+ object = this.resolveObjectName(object);
564
1065
  this.logger.debug("Find operation starting", { object, query });
565
1066
  const driver = this.getDriver(object);
566
1067
  const ast = this.toQueryAST(object, query);
@@ -584,6 +1085,7 @@ var ObjectQL = class {
584
1085
  }
585
1086
  }
586
1087
  async findOne(objectName, query) {
1088
+ objectName = this.resolveObjectName(objectName);
587
1089
  this.logger.debug("FindOne operation", { objectName });
588
1090
  const driver = this.getDriver(objectName);
589
1091
  const ast = this.toQueryAST(objectName, query);
@@ -591,6 +1093,7 @@ var ObjectQL = class {
591
1093
  return driver.findOne(objectName, ast);
592
1094
  }
593
1095
  async insert(object, data, options) {
1096
+ object = this.resolveObjectName(object);
594
1097
  this.logger.debug("Insert operation starting", { object, isBatch: Array.isArray(data) });
595
1098
  const driver = this.getDriver(object);
596
1099
  const hookContext = {
@@ -621,6 +1124,7 @@ var ObjectQL = class {
621
1124
  }
622
1125
  }
623
1126
  async update(object, data, options) {
1127
+ object = this.resolveObjectName(object);
624
1128
  this.logger.debug("Update operation starting", { object });
625
1129
  const driver = this.getDriver(object);
626
1130
  let id = data.id || data._id;
@@ -656,6 +1160,7 @@ var ObjectQL = class {
656
1160
  }
657
1161
  }
658
1162
  async delete(object, options) {
1163
+ object = this.resolveObjectName(object);
659
1164
  this.logger.debug("Delete operation starting", { object });
660
1165
  const driver = this.getDriver(object);
661
1166
  let id = void 0;
@@ -691,6 +1196,7 @@ var ObjectQL = class {
691
1196
  }
692
1197
  }
693
1198
  async count(object, query) {
1199
+ object = this.resolveObjectName(object);
694
1200
  const driver = this.getDriver(object);
695
1201
  if (driver.count) {
696
1202
  const ast = this.toQueryAST(object, { filter: query?.filter });
@@ -700,6 +1206,7 @@ var ObjectQL = class {
700
1206
  return res.length;
701
1207
  }
702
1208
  async aggregate(object, query) {
1209
+ object = this.resolveObjectName(object);
703
1210
  const driver = this.getDriver(object);
704
1211
  this.logger.debug(`Aggregate on ${object} using ${driver.name}`, query);
705
1212
  throw new Error("Aggregate not yet fully implemented in ObjectQL->Driver mapping");
@@ -723,7 +1230,8 @@ var ObjectQLPlugin = class {
723
1230
  this.version = "1.0.0";
724
1231
  this.init = async (ctx) => {
725
1232
  if (!this.ql) {
726
- this.ql = new ObjectQL(this.hostContext);
1233
+ const hostCtx = { ...this.hostContext, logger: ctx.logger };
1234
+ this.ql = new ObjectQL(hostCtx);
727
1235
  }
728
1236
  ctx.registerService("objectql", this.ql);
729
1237
  let hasMetadata = false;
@@ -775,9 +1283,14 @@ var ObjectQLPlugin = class {
775
1283
  }
776
1284
  };
777
1285
  export {
1286
+ DEFAULT_EXTENDER_PRIORITY,
1287
+ DEFAULT_OWNER_PRIORITY,
778
1288
  ObjectQL,
779
1289
  ObjectQLPlugin,
780
1290
  ObjectStackProtocolImplementation,
781
- SchemaRegistry
1291
+ RESERVED_NAMESPACES,
1292
+ SchemaRegistry,
1293
+ computeFQN,
1294
+ parseFQN
782
1295
  };
783
1296
  //# sourceMappingURL=index.mjs.map