@objectstack/objectql 9.9.0 → 9.10.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.mjs CHANGED
@@ -141,7 +141,10 @@ var init_seed_loader = __esm({
141
141
  const seedIdentity = config.identity;
142
142
  const baseEvalCtx = {
143
143
  now: seedNow,
144
- user: seedIdentity?.user,
144
+ // `id: null` is a legitimate seed-time state (the owning admin does not
145
+ // exist yet) that the formula EvalContext's `user.id: string` type does
146
+ // not yet model — cast the fallback so `os.user.id` evaluates to null.
147
+ user: seedIdentity?.user ?? { id: null },
145
148
  // Fall back to the per-tenant organizationId so `os.org.id` resolves
146
149
  // during per-org replay even without an explicit identity.org.
147
150
  org: seedIdentity?.org ?? (config.organizationId ? { id: config.organizationId } : void 0),
@@ -161,7 +164,7 @@ var init_seed_loader = __esm({
161
164
  targetField: "(expression)",
162
165
  attemptedValue: dataset.records[i],
163
166
  recordIndex: i,
164
- message: `Cannot resolve dynamic seed values for ${objectName} record #${i}: ${seedResult.error.message}. Records using cel\`os.user.id\` / cel\`os.org.id\` require a seed identity \u2014 ensure a system/admin user exists before seeding (see SeedLoaderConfig.identity).`
167
+ message: `Cannot resolve dynamic seed values for ${objectName} record #${i}: ${seedResult.error.message}. \`os.user.id\` resolves to null at seed time (the owning admin does not exist yet) and owner-style fields are assigned by the first-admin handoff \u2014 so a required, non-owner field must not depend on it. Provide a literal value or make the field optional.`
165
168
  };
166
169
  errors.push(error);
167
170
  allErrors.push(error);
@@ -840,6 +843,8 @@ function applySystemFields(schema, opts) {
840
843
  const tenancyDisabled = schema.tenancy?.enabled === false;
841
844
  const wantTenant = sf?.tenant !== false && !tenancyDisabled;
842
845
  const wantAudit = sf?.audit !== false;
846
+ const ownership = schema.ownership;
847
+ const wantOwner = ownership !== "org" && ownership !== "none" && !schema.managedBy && !schema.name.startsWith("sys_");
843
848
  const additions = {};
844
849
  if (wantTenant && !schema.fields?.organization_id) {
845
850
  additions.organization_id = {
@@ -898,6 +903,17 @@ function applySystemFields(schema, opts) {
898
903
  };
899
904
  }
900
905
  }
906
+ if (wantOwner && !schema.fields?.owner_id) {
907
+ additions.owner_id = {
908
+ type: "lookup",
909
+ reference: "sys_user",
910
+ label: "Owner",
911
+ required: false,
912
+ readonly: false,
913
+ system: true,
914
+ description: "Record owner (auto-stamped to the creating user on insert; reassignable). Drives owner-scoped views, reports and notifications."
915
+ };
916
+ }
901
917
  if (Object.keys(additions).length === 0) return schema;
902
918
  return {
903
919
  ...schema,
@@ -7025,7 +7041,7 @@ function aggregateBucket(rows, aggregations) {
7025
7041
  const alias = agg.alias;
7026
7042
  const fn = agg.function;
7027
7043
  if (fn === "count") {
7028
- if (!agg.field) {
7044
+ if (!agg.field || agg.field === "*") {
7029
7045
  out[alias] = rows.length;
7030
7046
  } else {
7031
7047
  out[alias] = rows.reduce(
@@ -9448,6 +9464,58 @@ var ScopedContext = class _ScopedContext {
9448
9464
  throw error;
9449
9465
  }
9450
9466
  }
9467
+ /**
9468
+ * Resolve the default driver, if it exposes transaction primitives.
9469
+ * Shared by {@link transaction} and the discrete begin/commit/rollback trio.
9470
+ */
9471
+ txDriver() {
9472
+ const engine = this.engine;
9473
+ const driver = engine.defaultDriver ? engine.drivers?.get(engine.defaultDriver) : void 0;
9474
+ return driver?.beginTransaction ? driver : void 0;
9475
+ }
9476
+ /**
9477
+ * Discrete transaction primitives — `begin` / `commit` / `rollback` as three
9478
+ * separate calls, in contrast to {@link transaction}'s single-callback form.
9479
+ *
9480
+ * This trio exists for callers that cannot keep a JS closure on the stack for
9481
+ * the lifetime of the transaction — chiefly the sandbox runner, where the
9482
+ * hook/action body's `ctx.api.transaction(fn)` is driven across many host
9483
+ * event-loop turns via deferred promises. Across those `setImmediate`
9484
+ * boundaries the engine's ambient `txStore` (AsyncLocalStorage) does NOT
9485
+ * survive, so the transaction handle is threaded **explicitly**: `begin`
9486
+ * returns a child ScopedContext carrying `transaction: trx` in its execution
9487
+ * context, and `resolveTx` honors that explicit handle ahead of the ambient
9488
+ * store. Every `object(...)` op on the returned context therefore reuses the
9489
+ * one connection without relying on ALS.
9490
+ *
9491
+ * Returns `null` when the driver has no transaction support — the caller then
9492
+ * runs non-transactionally against `this` (same graceful degrade as
9493
+ * {@link transaction}).
9494
+ */
9495
+ async beginTransaction() {
9496
+ const driver = this.txDriver();
9497
+ if (!driver) return null;
9498
+ const trx = await driver.beginTransaction();
9499
+ const ctx = new _ScopedContext(
9500
+ { ...this.executionContext, transaction: trx },
9501
+ this.engine
9502
+ );
9503
+ return { ctx, handle: trx };
9504
+ }
9505
+ /** Commit a handle obtained from {@link beginTransaction}. */
9506
+ async commitTransaction(handle) {
9507
+ const driver = this.txDriver();
9508
+ if (!driver) return;
9509
+ if (driver.commit) await driver.commit(handle);
9510
+ else if (driver.commitTransaction) await driver.commitTransaction(handle);
9511
+ }
9512
+ /** Roll back a handle obtained from {@link beginTransaction}. */
9513
+ async rollbackTransaction(handle) {
9514
+ const driver = this.txDriver();
9515
+ if (!driver) return;
9516
+ if (driver.rollback) await driver.rollback(handle);
9517
+ else if (driver.rollbackTransaction) await driver.rollbackTransaction(handle);
9518
+ }
9451
9519
  get userId() {
9452
9520
  return this.executionContext.userId;
9453
9521
  }