Skip to content

Latest commit

 

History

History
925 lines (654 loc) · 36.3 KB

File metadata and controls

925 lines (654 loc) · 36.3 KB

@objectstack/driver-sql

10.3.0

Patch Changes

  • 5ba52b0: fix(driver-sql): honor tenancy.enabled:false in driver org-scoping

    The driver auto-detects organization_id as a tenant-isolation column and, when the caller passes DriverOptions.tenantId, scopes reads/updates/deletes to that tenant (and injects the column on inserts). The implicit column-detection fallback ignored an explicit tenancy.enabled === false, so a platform-global object that opts out of tenancy but carries an optional organization_id FK (e.g. sys_license) was still org-scoped — an authenticated caller's active-org tenantId then hid every NULL-org / cross-org row. The opt-out is now honored in a single shared computeTenantField() used by both initObjects and registerExternalObject (which had drifted). Covers TursoDriver (extends SqlDriver). Genuine org-scoped objects are unaffected.

    • @objectstack/spec@10.3.0
    • @objectstack/core@10.3.0

10.2.0

Patch Changes

  • Updated dependencies [b496498]
    • @objectstack/spec@10.2.0
    • @objectstack/core@10.2.0

10.1.0

Minor Changes

  • 517dad9: Schema drift detection + os migrate for non-additive metadata changes (#2186).

    The metadata→DB schema sync was additive-only: it created tables and added columns but never altered/dropped existing ones, so relaxing required, changing a type/length, or dropping a field silently diverged from an existing database. The physical column won at write time, surfacing a misleading organization_id is required 400 even though /meta reported the field optional.

    • driver-sql — the SQL driver now detects managed-schema drift (metadata is the source of truth) and categorises each divergence safe / needs_confirm / destructive. initObjects warns once per divergence with an actionable hint. A new opt-in SqlDriverConfig.autoMigrate: 'safe' auto-applies the loosening subset (relax NOT NULL, widen varchar) so an existing dev DB self-heals on restart — never destructive, force-disabled under NODE_ENV=production. New public methods detectManagedDrift() / applyMigrationEntries(). SQLite reconciles via the official table-rebuild (copy → swap), preserving data; Postgres/MySQL alter in place.
    • cli — new os migrate plan (dry-run, categorised diff) and os migrate apply (--allow-destructive for drops/tightenings, confirm gate, --json). os dev/serve now pass autoMigrate: 'safe' in dev only.
    • rest — a NOT NULL violation that reaches the driver (metadata validation already passed) now carries a drift-aware hint pointing at os migrate, instead of only the misleading "field is required" message. The VALIDATION_FAILED / fields envelope is unchanged for back-compat.

Patch Changes

  • Updated dependencies [49da36e]
  • Updated dependencies [ac79f16]
    • @objectstack/spec@10.1.0
    • @objectstack/core@10.1.0

10.0.0

Patch Changes

  • 92db3e5: feat(driver-sql): honor external.columnMap on federated (external) objects (ADR-0015).

    When a federated object declares external.columnMap ({ remoteColumn -> localField }), the SQL driver now translates queries to the physical remote columns: WHERE and ORDER BY map local fields to remote columns (value coercion stays keyed by the local field), formatOutput renames remote-column keys back to local field names on read, and write payloads are key-remapped. Managed objects and external objects without a columnMap are unchanged (the resolver falls back to the existing per-site behavior).

  • 2a1b16b: fix(ADR-0015): honor external.remoteName / external.remoteSchema on the federation read path.

    The query path previously resolved an external object's physical table from the object name, ignoring its external binding — so a federated object bound to a differently-named remote table failed with no such table, and ADR-0015's own wh_ordermart.fact_orders example was unqueryable. The SQL driver now resolves the remote table (remoteName, plus remoteSchema via .withSchema() on pg/mysql) and registers external objects' read-coercion metadata without DDL (SqlDriver.registerExternalObject, routed from the engine/plugin schema-sync). The managed path is unchanged. See ADR-0015 §18.

  • Updated dependencies [d7ff626]

  • Updated dependencies [2a1b16b]

  • Updated dependencies [e16f2a8]

  • Updated dependencies [e411a82]

  • Updated dependencies [a581385]

  • Updated dependencies [d5f6d29]

  • Updated dependencies [220ce5b]

  • Updated dependencies [3efe334]

  • Updated dependencies [feead7e]

  • Updated dependencies [6ca20b3]

  • Updated dependencies [5f875fe]

  • Updated dependencies [b469950]

    • @objectstack/spec@10.0.0
    • @objectstack/core@10.0.0

9.11.0

Minor Changes

  • 36138c7: feat(autonumber): date, {field} and per-scope counter reset for autonumber formats

    autonumberFormat previously only understood a single {0000} sequence slot — everything else was a fixed literal prefix on one global counter. Real MES/eHR record numbers need three more token classes, so the format is now tokenized by a shared pure renderer in @objectstack/spec (parseAutonumberFormat / renderAutonumber) that the engine fallback and the SQL driver both call, so they emit byte-identical numbers (#1603 parity):

    • Date tokens{YYYY} {YY} {MM} {DD} {YYYYMMDD} resolve the calendar day in the request's business timezone (ExecutionContext.timezone, ADR-0053; UTC fallback), threaded through the new DriverOptions.timezone.
    • {field} interpolation{section}{island_zone}{000} substitutes record field values into the prefix.
    • Per-scope counter reset — the counter's scope is the rendered prefix before the sequence slot, so AD{YYYYMMDD}{0000} resets daily, {section}{island_zone}{000} numbers per group, and {plan_no}{000} numbers per parent — all from one mechanism, no separate reset config.

    Fixed-prefix formats like CASE-{0000} render an empty scope and keep their single global counter, so existing sequences are unchanged. The persistent _objectstack_sequences table is keyed by a key_hash (SHA-256 of object, tenant_id, field, scope) — a single 64-char primary key that keys every dialect uniformly, stays within MySQL's utf8mb4 index-length limit (four raw columns would not), and lets scope be a generous non-indexed column. Deployments with an older table (3-column, or an interim scope column) are migrated in place on first use, carrying existing counters to scope=''.

    Guardrails:

    • Empty interpolated field is a hard error, not a silent mis-number. A {field} token whose value is missing at create time would render to an empty prefix and collapse the record into the wrong counter scope. Both the SQL driver and the engine fallback now refuse to generate and throw a clear error naming the empty field (shared missingFieldValues helper).
    • Build-time lint (@objectstack/cli compile). autonumber formats are checked against the object's fields: a {field} token naming a non-existent field (or the autonumber field itself) fails the build; a token naming an optional field emits an advisory warning to mark it required: true.
    • Migration fails safe. If a legacy table cannot be migrated to the key_hash shape, fixed-prefix sequences keep working via the legacy key and a per-scope write raises an actionable error instead of corrupting counters.
    • Long {field} scopes are supported (e.g. a long {plan_no}): the non-indexed scope column and hashed key remove the old varchar/PK length ceiling.

    Notes on inherent semantics (documented, not bugs):

    • The counter scope IS the rendered prefix. When two records' tokens render to the same prefix string (e.g. {a}{b} for ('AB','C') and ('A','BC')) they also render the same visible number, so they share one counter to stay unique — the remedy for genuinely-distinct groups is an unambiguous format (a delimiter literal between variable tokens).
    • The sequence pad width is a MINIMUM; past it the number grows ({000}1000), it never wraps — matching mainstream autonumber semantics.

Patch Changes

  • Updated dependencies [e7f6539]
  • Updated dependencies [2365d07]
  • Updated dependencies [6595b53]
  • Updated dependencies [fa8964d]
  • Updated dependencies [36138c7]
  • Updated dependencies [a8e4f3b]
  • Updated dependencies [4c213c2]
  • Updated dependencies [2afb612]
    • @objectstack/spec@9.11.0
    • @objectstack/core@9.11.0

9.10.0

Patch Changes

  • db02bd5: Fix dashboard time-series charts / "last N months" KPIs that filter or group by a Field.datetime column silently returning "No rows".

    The analytics NativeSQLStrategy compiles dashboard relative-date tokens ({12_months_ago}, {today}, …) to ISO date strings and binds them directly into raw SQL, bypassing the driver's own filter coercion. Under better-sqlite3 a Field.datetime column is stored as an INTEGER epoch (ms), so assessed_at >= '2025-06-18' became a TEXT-vs-INTEGER affinity compare that is always false — an empty result even though the rows exist. Field.date columns store ISO TEXT and were unaffected.

    The strategy now coerces a temporal comparand to the column's on-disk storage form via a new optional StrategyContext.coerceTemporalFilterValue hook, wired to the driver's public SqlDriver.temporalFilterValue (the single source of truth for the storage convention). Coercion is dialect-correct: SQLite Field.datetime → epoch ms; Field.date text and native-timestamp dialects (Postgres/MySQL) are left unchanged, so Postgres is never handed an epoch integer. Applied to gte/lte/gt/lt/equals, in/notIn, and the dateRange/timeDimension BETWEEN path.

  • d9508d1: fix(driver-sql): make numeric-scalar type fidelity self-heal on legacy SQLite columns

    The #2025 fix mapped rating/slider/progress to numeric columns, but SQLite never alters a column's type in place and the schema reconciler only adds missing columns — so a column created before that fix keeps its TEXT affinity and would still read back '4' instead of 4 forever.

    A read-side numeric coercion (the new numericFields registry, single-sourced from NUMERIC_SCALAR_TYPES) now coerces numeric-looking stored strings back to numbers on read, mirroring how dateFields already repairs legacy timestamp-typed Field.date rows. The fidelity no longer depends on column affinity alone; null and genuinely non-numeric legacy values are left intact rather than turned into 0/NaN.

  • 1d352d3: fix(driver-sql): round-trip rating/slider/toggle/progress with type fidelity

    rating/slider/toggle/progress had no case in the DDL column-type switch, so they fell to default → table.string (TEXT affinity). SQLite then coerced the written value to a string — rating: 4 read back '4', toggle: true read back '1' — so the value persisted but the JS type leaked on read. On a low-code platform where field types are author-driven, a field that silently returns the wrong type is a runtime-fidelity trap the static gates and value-loss tests don't catch.

    • rating/slider/progress now map to a REAL (numeric) column.
    • toggle maps to a boolean column and is registered in the boolean read-coercion path, so stored 1/0 come back as real JS booleans.
    • The object-valued record/video/audio types are folded into the shared JSON_COLUMN_TYPES source, and the DDL default case now derives JSON-vs-string from that set, so the column-type switch and isJsonField (the read-side deserializer) can no longer drift.
  • Updated dependencies [db02bd5]

  • Updated dependencies [641675d]

  • Updated dependencies [94e9040]

  • Updated dependencies [1f88fd9]

  • Updated dependencies [1f88fd9]

    • @objectstack/spec@9.10.0
    • @objectstack/core@9.10.0

9.9.1

Patch Changes

  • @objectstack/spec@9.9.1
  • @objectstack/core@9.9.1

9.9.0

Minor Changes

  • bfa3102: fix: array-valued field types persist, and Field.time accepts time-of-day — two field-type runtime gaps found driving the showcase field-zoo (which had no seed data, so neither was ever exercised).

    Array/object fields broke every write (driver-sql). multiselect / checkboxes / tags / repeater / vector were absent from the SQL driver's JSON-field classification, so their array values reached the better-sqlite3 binder un-serialized and threw "SQLite3 can only bind numbers, strings, bigints, buffers, and null" — a 500 on insert/update for common field types (even task.labels on a normal object). The DDL column-type switch and isJsonField had drifted into two separate lists; they now share one JSON_COLUMN_TYPES source that includes the array/object types, so these columns are created as JSON and round-trip as arrays/objects. A formatInput safety net additionally serializes any stray array/object value so an unclassified field degrades to a stored string instead of crashing.

    Field.time rejected every valid value (objectql). The validator reused the date/datetime branch (Date.parse), which is NaN for any bare time string — so a time field could never accept 14:30 or 09:05:30. time now validates a time-of-day (HH:MM / HH:MM:SS, optional fractional seconds and Z/offset) and still accepts a full ISO datetime; date/datetime are unchanged.

    Verified live on app-showcase: the full field-zoo specimen (all input-able field types) now persists and round-trips. Regression tests added for both.

Patch Changes

  • 796f0d6: fix(driver-sql): Field.date is now stored and returned as a tz-naive YYYY-MM-DD calendar day (ADR-0053 Phase 1)

    A Field.date ("close date", "due date", "birthday") is semantically a timezone-naive calendar day, but the SQL driver was treating it as an instant: formatInput wrote the value verbatim (keeping any time component, so dev.db held close_date = "2026-07-15T17:24:56.533Z"), while the filter layer (coerceFilterValue) already normalized the comparand to date-only YYYY-MM-DD. That write/filter asymmetry meant a date-equality filter — close_date == '2026-07-15', expires_on: { $in: [...] }, or a daysFromNow(n)-style comparand — compared "2026-07-15T17:24Z" against "2026-07-15" and silently matched nothing.

    This patch aligns the write/read boundary with the date-only contract the filter already enforced:

    • Write (formatInput): every Field.date value (a JS Date, a full-ISO string, or an already date-only string) collapses to YYYY-MM-DD before insert/update. A Date collapses to its UTC calendar day, matching coerceFilterValue.
    • Read (formatOutput): Field.date values are returned as YYYY-MM-DD, slicing any stored time component. This transparently repairs legacy rows that were written as a full timestamp, so date-equality works without a data migration. Read normalization now runs on the find path for every dialect (previously only findOne), matching the new behaviour.
    • The truncation logic is shared by the filter, write and read paths via a single toDateOnly helper, so all three agree on what a date is.

    Field.datetime is unchanged — it keeps full-instant (UTC millisecond) semantics.

    Out of scope (ADR-0053 Phase 2): timezone-aware today()/daysFromNow()/daysAgo(), an org/user reference timezone, and datetime render-time TZ. See ADR-0053 and issue #1928.

  • Updated dependencies [84249a4]

  • Updated dependencies [11af299]

  • Updated dependencies [d5774b5]

  • Updated dependencies [134043a]

  • Updated dependencies [90108e0]

  • Updated dependencies [9afeb2d]

  • Updated dependencies [6bec07e]

  • Updated dependencies [601cc11]

  • Updated dependencies [575448d]

    • @objectstack/spec@9.9.0
    • @objectstack/core@9.9.0

9.8.0

Patch Changes

  • Updated dependencies [97c55b3]
  • Updated dependencies [1b1f490]
    • @objectstack/spec@9.8.0
    • @objectstack/core@9.8.0

9.7.0

Patch Changes

  • @objectstack/spec@9.7.0
  • @objectstack/core@9.7.0

9.6.0

Patch Changes

  • Updated dependencies [d1e930a]
  • Updated dependencies [71578f2]
  • Updated dependencies [5e3a301]
  • Updated dependencies [5db2742]
    • @objectstack/spec@9.6.0
    • @objectstack/core@9.6.0

9.5.1

Patch Changes

  • Updated dependencies [ee72aae]
    • @objectstack/spec@9.5.1
    • @objectstack/core@9.5.1

9.5.0

Patch Changes

  • Updated dependencies [d08551c]
  • Updated dependencies [707aeed]
  • Updated dependencies [7a103d4]
  • Updated dependencies [4b01250]
    • @objectstack/spec@9.5.0
    • @objectstack/core@9.5.0

9.4.0

Patch Changes

  • b678d8c: fix(driver-sql): an unknown $select column must not zero the result set

    find() swallowed any "no such column" error into an empty array. A projected $select naming a column the table lacks (e.g. a generic list view auto-requesting status/due_date/image on an object without them) then made the WHOLE query return zero rows — reading to the UI as "no records exist" while the data was actually there: a silent data-loss footgun.

    When the failure comes from the projection, retry once with SELECT * so the real rows still come back (the phantom field is simply absent from each row). Non-projection errors (unknown table, etc.) still surface as before. This driver backstop holds even when the engine's unknown-field filter cannot fire because the object's schema is not populated in the registry (notably the cloud multi-tenant runtime).

  • Updated dependencies [060467a]

  • Updated dependencies [0856476]

  • Updated dependencies [b678d8c]

  • Updated dependencies [b678d8c]

  • Updated dependencies [b678d8c]

    • @objectstack/spec@9.4.0
    • @objectstack/core@9.4.0

9.3.0

Patch Changes

  • Updated dependencies [1ada658]
  • Updated dependencies [3219191]
  • Updated dependencies [290f631]
  • Updated dependencies [50b7b47]
  • Updated dependencies [f15d6f6]
  • Updated dependencies [f8684ea]
  • Updated dependencies [b4765be]
    • @objectstack/spec@9.3.0
    • @objectstack/core@9.3.0

9.2.0

Patch Changes

  • Updated dependencies [2f57b75]
  • Updated dependencies [2f57b75]
    • @objectstack/spec@9.2.0
    • @objectstack/core@9.2.0

9.1.0

Patch Changes

  • Updated dependencies [b9062c9]
    • @objectstack/spec@9.1.0
    • @objectstack/core@9.1.0

9.0.1

Patch Changes

  • Updated dependencies [1817845]
    • @objectstack/spec@9.0.1
    • @objectstack/core@9.0.1

9.0.0

Patch Changes

  • Updated dependencies [4c3f693]
  • Updated dependencies [0bf39f1]
  • Updated dependencies [f533f42]
  • Updated dependencies [1c83ee8]
    • @objectstack/spec@9.0.0
    • @objectstack/core@9.0.0

8.0.1

Patch Changes

  • @objectstack/spec@8.0.1
  • @objectstack/core@8.0.1

8.0.0

Minor Changes

  • b990b89: fix(autonumber): one owner for autonumber generation — the persistent driver sequence (#1603)

    Autonumber values were generated in TWO places: the SQL driver's persistent, atomic _objectstack_sequences table AND a non-persistent in-memory counter in the ObjectQL engine. Because the engine pre-filled the field BEFORE calling the driver, the driver always saw a value already set and skipped — so the persistent sequence was effectively dead code, and a multi-instance / post-restart deployment could mint duplicate numbers from the in-memory counter.

    This makes generation single-owner:

    • @objectstack/specDriverCapabilities gains an optional autonumber flag: "driver natively generates persistent autonumber/sequence values".

    • @objectstack/driver-sql — advertises supports.autonumber = true. bulkCreate() now fills autonumber fields too (previously only create() / upsert() did), so bulk inserts also draw from the persistent sequence. Field parsing now honors either the spec-canonical autonumberFormat key OR the format shorthand (both appear in metadata).

    • @objectstack/objectql — when the driver advertises native autonumber support, the engine NO LONGER pre-fills (it defers entirely to the persistent driver sequence as the single source of truth). For drivers without native support (memory, mongodb) the in-memory fallback is unchanged. The fallback also now reads either autonumberFormat or format. Record-validation exempts autonumber fields from the required check — the value is runtime-owned and assigned after validation, so a required record number is never rejected as "missing".

    No metadata changes required. Existing data is respected: the driver bootstraps each sequence from the current max numeric tail on first use.

Patch Changes

  • 1e8b680: fix(security): close four P0 launch-readiness findings

    • plugin-auth (P0-1): generateSecret() now throws (fails boot) when no OS_AUTH_SECRET is set and NODE_ENV==='production', instead of silently falling back to a predictable dev-secret-<timestamp> (session forgery). The dev/test fallback is unchanged.
    • plugin-security (P0-2): the permission-resolution catch now fails closed — it logs at ERROR and throws PermissionDeniedError rather than return next(). A degraded metadata service can no longer let every authenticated request bypass RBAC/RLS. System operations still bypass as before.
    • driver-sql (P0-3): the contains / $contains operator now escapes LIKE metacharacters (% / _ / \) in the user value and binds an explicit ESCAPE '\', so a value of % matches literally instead of every row (filter bypass). Correct across SQLite/MySQL/Postgres.
    • driver-mongodb (P0-4): the field-operator translator now rejects unknown $-operators instead of passing them through, blocking $where / $function / $expr (server-side JS execution / query-intent bypass). All legitimate ObjectQL operators remain allowlisted.

    +12 regression tests across the four packages.

  • Updated dependencies [a46c017]

  • Updated dependencies [b990b89]

  • Updated dependencies [99111ec]

  • Updated dependencies [d5a8161]

  • Updated dependencies [5cf1f1b]

  • Updated dependencies [9ef89d4]

  • Updated dependencies [3306d2f]

  • Updated dependencies [c262301]

  • Updated dependencies [bc44195]

  • Updated dependencies [9e2e229]

    • @objectstack/spec@8.0.0
    • @objectstack/core@8.0.0

7.9.0

Patch Changes

  • @objectstack/spec@7.9.0
  • @objectstack/core@7.9.0

7.8.0

Patch Changes

  • Updated dependencies [06f2bbb]
  • Updated dependencies [36719db]
  • Updated dependencies [424ab26]
    • @objectstack/spec@7.8.0
    • @objectstack/core@7.8.0

7.7.0

Patch Changes

  • 764c747: fix(metadata): home the metadata-storage objects in metadata-core and register them from ObjectQL

    Standalone "host config" apps boot without @objectstack/metadata's MetadataPlugin, so nobody registered the metadata-storage objects (sys_metadata, _history, _audit, sys_view_definition) into ObjectQL — their tables were never schema-synced and ObjectQL's own protocol (loadMetaFromDb / getMetaItems) failed with no such table: sys_metadata on every read.

    • Move the four storage-object definitions from @objectstack/platform-objects/metadata to @objectstack/metadata-core (the lowest package shared by their real consumers); platform-objects/metadata now re-exports them for back-compat.
    • ObjectQLPlugin registers these objects itself (gated on environmentId === undefined, mirroring restoreMetadataFromDb) so their tables always sync on platform/standalone kernels.
    • Gate the SQL driver's tenant-audit warning on actual multi-tenant mode — organization_id now exists on every table, so column presence alone no longer implies "tenant-scoped"; single-tenant boots no longer spam the warning for system writes.
  • Updated dependencies [b391955]

  • Updated dependencies [f06b64e]

  • Updated dependencies [023bf93]

    • @objectstack/spec@7.7.0
    • @objectstack/core@7.7.0

7.6.0

Patch Changes

  • Updated dependencies [955d4c8]
  • Updated dependencies [c4a4cbd]
  • Updated dependencies [b046ec2]
  • Updated dependencies [2170ad9]
  • Updated dependencies [02d6359]
  • Updated dependencies [7648242]
  • Updated dependencies [8fa1e7f]
  • Updated dependencies [55866f5]
  • Updated dependencies [60f9c45]
    • @objectstack/spec@7.6.0
    • @objectstack/core@7.6.0

7.5.0

Patch Changes

  • @objectstack/spec@7.5.0
  • @objectstack/core@7.5.0

7.4.1

Patch Changes

  • @objectstack/spec@7.4.1
  • @objectstack/core@7.4.1

7.4.0

Minor Changes

  • 24c9013: fix(driver-sql): materialize declared object-level indexes (#1459)

    The SQL driver synced columns and field-level unique, but silently dropped object-level declared indexes (ObjectSchema.indexes: [{ fields, unique }]). As a result several documented multi-column UNIQUE / dedup guarantees were never enforced at the DB level — a fresh dev --fresh sqlite DB showed only primary-key autoindexes.

    initObjects now materializes declared indexes (syncDeclaredIndexes) after the table is created/altered:

    • single- and multi-column indexes, including UNIQUE
    • NULL-distinct semantics (the cross-dialect default), so multiple NULL rows stay insertable while non-NULL duplicates are rejected — matching the convergence-on-conflict pattern the messaging pipeline relies on
    • idempotent: deterministic, length-bounded index names + per-dialect existing-index introspection (sqlite/pg/mysql); "already exists" races are absorbed
    • indexes referencing a non-materialized (virtual formula) column are skipped with a warning instead of failing sync

    The indexes driver capability flag is now true.

  • 2faf9f2: External Datasource Federation (ADR-0015) — Phase 1.

    Adds the spec foundation and the DDL gate for federating mature external databases without ObjectStack ever mutating their schema:

    • Datasource.schemaMode (managed | external | validate-only) and Datasource.external settings, with a cross-field invariant.
    • Object.external binding (remote table/schema, writability, column map).
    • Shared error contract: ExternalSchemaMismatchError, ExternalWriteForbiddenError, ExternalSchemaModeViolationError (stable codes) + structured SchemaDiffEntry rendering.
    • driver-sql DDL gate: schema-mutating DDL (initObjects/syncSchema/ dropTable) is rejected when schemaMode !== 'managed'.

    All changes are additive and backward-compatible (schemaMode defaults to 'managed').

Patch Changes

  • Updated dependencies [23c7107]
  • Updated dependencies [c72daad]
  • Updated dependencies [f115182]
  • Updated dependencies [2faf9f2]
  • Updated dependencies [2faf9f2]
  • Updated dependencies [2faf9f2]
  • Updated dependencies [58b450b]
  • Updated dependencies [82eb6cf]
  • Updated dependencies [13d8653]
  • Updated dependencies [ff3d006]
  • Updated dependencies [5e831de]
    • @objectstack/spec@7.4.0
    • @objectstack/core@7.4.0

7.3.0

Patch Changes

  • Updated dependencies [5e7c554]
    • @objectstack/spec@7.3.0
    • @objectstack/core@7.3.0

7.2.1

Patch Changes

  • @objectstack/spec@7.2.1
  • @objectstack/core@7.2.1

7.2.0

Patch Changes

  • @objectstack/spec@7.2.0
  • @objectstack/core@7.2.0

7.1.0

Patch Changes

  • Updated dependencies [47a92f4]
    • @objectstack/spec@7.1.0
    • @objectstack/core@7.1.0

7.0.0

Patch Changes

  • Updated dependencies [74470ad]
  • Updated dependencies [d29617e]
  • Updated dependencies [dc72172]
    • @objectstack/spec@7.0.0
    • @objectstack/core@7.0.0

6.9.0

Patch Changes

  • @objectstack/spec@6.9.0
  • @objectstack/core@6.9.0

6.8.1

Patch Changes

  • @objectstack/spec@6.8.1
  • @objectstack/core@6.8.1

6.8.0

Patch Changes

  • Updated dependencies [6e88f77]
  • Updated dependencies [c8b9f57]
    • @objectstack/spec@6.8.0
    • @objectstack/core@6.8.0

6.7.1

Patch Changes

  • @objectstack/spec@6.7.1
  • @objectstack/core@6.7.1

6.7.0

Patch Changes

  • 4944f3a: Promote native database client packages so npm consumers can boot without manual installs.

    • better-sqlite3 is now an optionalDependency (prebuilt binaries cover the common case), so npx @objectstack/cli start boots a default SQLite database out-of-the-box.
    • pg, mysql2, sqlite3, and tedious are declared as optional peerDependencies (peerDependenciesMeta.optional = true), removing install warnings while keeping the loader-on-demand pattern.

    Fixes: Knex: Cannot find module 'better-sqlite3' on fresh npm install @objectstack/cli followed by objectstack start.

  • Updated dependencies [430067b]

  • Updated dependencies [4f9e9d4]

    • @objectstack/spec@6.7.0
    • @objectstack/core@6.7.0

6.6.0

Patch Changes

  • Updated dependencies [a49cfc2]
    • @objectstack/spec@6.6.0
    • @objectstack/core@6.6.0

6.5.1

Patch Changes

  • @objectstack/spec@6.5.1
  • @objectstack/core@6.5.1

6.5.0

Patch Changes

  • @objectstack/spec@6.5.0
  • @objectstack/core@6.5.0

6.4.0

Patch Changes

  • Updated dependencies [f8651cc]
  • Updated dependencies [f8651cc]
  • Updated dependencies [0bf6f9a]
    • @objectstack/spec@6.4.0
    • @objectstack/core@6.4.0

6.3.0

Patch Changes

  • @objectstack/spec@6.3.0
  • @objectstack/core@6.3.0

6.2.0

Patch Changes

  • Updated dependencies [b4c74a9]
    • @objectstack/spec@6.2.0
    • @objectstack/core@6.2.0

6.1.1

Patch Changes

  • @objectstack/spec@6.1.1
  • @objectstack/core@6.1.1

6.1.0

Patch Changes

  • Updated dependencies [93c0589]
    • @objectstack/spec@6.1.0
    • @objectstack/core@6.1.0

6.0.0

Patch Changes

  • Updated dependencies [629a716]
  • Updated dependencies [dbc4f7d]
  • Updated dependencies [944f187]
    • @objectstack/spec@6.0.0
    • @objectstack/core@6.0.0

5.2.0

Patch Changes

  • Updated dependencies [bab2b20]
  • Updated dependencies [fa011d8]
  • Updated dependencies [b806f58]
    • @objectstack/spec@5.2.0
    • @objectstack/core@5.2.0

5.1.0

Patch Changes

  • Updated dependencies [75f4ee6]
  • Updated dependencies [823d559]
    • @objectstack/spec@5.1.0
    • @objectstack/core@5.1.0

5.0.0

Patch Changes

  • Updated dependencies [2f9073a]
    • @objectstack/spec@5.0.0
    • @objectstack/core@5.0.0

4.2.0

Patch Changes

  • Updated dependencies [2869891]
    • @objectstack/spec@4.2.0
    • @objectstack/core@4.2.0

4.1.1

Patch Changes

  • @objectstack/spec@4.1.1
  • @objectstack/core@4.1.1

4.1.0

Minor Changes

  • 0cc0374: feat(driver-sql): tenant-isolated auto_number sequences backed by a persistent counter table

    Breaking nothing; new behaviour is opt-in via object schema.

    The SQL driver now generates auto_number / autonumber field values via a dedicated _objectstack_sequences table keyed by (object, tenant_id, field) instead of scanning the data table for the current MAX on every insert.

    Highlights:

    • Tenant isolation. Objects with an organization_id field get a separate counter per organization. Two tenants creating contracts at the same time both legitimately observe CTR-0001, CTR-0002, … in their own namespaces — they no longer interleave or skip numbers.
    • Tenant resolution. Source order: row[organization_id]DriverOptions.tenantId__global__ sentinel for org-less objects (e.g. setup-side singletons share one counter).
    • Bootstrap from existing data. On the first reservation in a new (object, tenant, field) tuple, the driver seeds last_value from the current per-tenant MAX so legacy/seeded records keep their position and downstream inserts pick up monotonically (gaps are tolerated).
    • Atomic increment. Each reservation runs in a transaction with SELECT … FOR UPDATE (where the dialect supports it) and a single UPDATE of last_value. Tested with 25 concurrent inserts in one tenant producing 25 distinct sequence values.
    • Caller overrides honoured. A row that already has an explicit value for the auto_number field is left untouched, and the sequence bootstrap respects that value so future reservations advance past it.
    • Dual spelling. Both type: 'auto_number' (snake) and type: 'autonumber' (the spec factory output) are recognised.

    Migration notes:

    • The first time the driver handles an auto_number insert, it creates the _objectstack_sequences table automatically — no manual DDL.
    • Pre-existing data is not renumbered. Gaps introduced by older cross-tenant logic (where a tenant's number could "jump" because it inherited another tenant's MAX) remain in place; subsequent inserts continue from MAX + 1 in the affected tenant.
  • 5b878d9: Generate auto_number / autonumber field values on insert. The driver parses the field's format template (e.g. CTR-{0000}) to extract the prefix and pad-width, then scans existing rows with the same prefix and emits prefix + padded(maxN + 1) for any row that omits the field.

    Note: per-call MAX+1 — not atomic across concurrent writers. Fine for seed-data and low-write demo loads; production deployments should layer a dedicated sequence table.

  • f0b3972: Driver-level tenant isolation for objects with organization_id.

    SqlDriver now auto-applies a WHERE organization_id = :tenantId predicate on every read/update/delete and auto-injects the column on insert when the caller passes options.tenantId and the object schema declares an organization_id field. bulkCreate, bulkDelete, updateMany, deleteMany, count and aggregate are all scoped.

    ObjectQL's engine now threads ExecutionContext.tenantId into the driver options for every CRUD entry point (including expandRelatedRecords), so a tenant-scoped session can no longer cross tenants — even through lookup expansion or count fallbacks.

    Backward compatible: callers that omit tenantId (system tasks, seed scripts) keep getting unscoped behaviour. Explicit organization_id on an insert row always wins over the contextual tenantId so admin tooling can still target a specific tenant.

    13 new tests in sql-driver-tenant-scope.test.ts verify cross-tenant find/findOne/update/delete/count/bulkCreate/updateMany/deleteMany isolation, the unscoped admin path, and that global objects (no organization_id) are not scoped.

  • 0e63f2f: Declarative tenant scoping + audit warn for missing tenantId.

    SqlDriver now reads obj.tenancy.tenantField first when picking the tenant column for an object, falling back to the implicit organization_id detection so legacy objects keep working without a spec migration. Set tenancy: { enabled: true, strategy: 'shared', tenantField: 'workspace_id' } on any object to use a custom column.

    Writes (create, update, delete, bulkCreate, bulkDelete, updateMany, deleteMany, upsert) that target a tenant-scoped object without options.tenantId now emit one [tenant-audit] warning per {object}:{op} so missing-context bugs surface in CI/logs instead of silently writing globally. The engine auto-silences when ExecutionContext.isSystem === true (boot-time seeds, kernel mirrors). Callers can opt out per-call with options.bypassTenantAudit = true or globally with OS_TENANT_AUDIT=0.

    Driver README now documents the full scope/bypass matrix and the audit warning.

    Three new tests cover the declared-tenant-field path, the audit throttle, and the bypass flag.

Patch Changes

  • 5683206: Document the tenant-isolation bypass on raw execute() (both SqlDriver.execute() and engine.execute()). The behaviour is unchanged — execute() has always passed commands through verbatim — but the JSDoc now spells out the security contract so callers know they must inline WHERE organization_id = ? themselves or restrict raw execution to genuinely global statements (migrations, control-plane tables).
  • Updated dependencies [2108c30]
  • Updated dependencies [23db640]
    • @objectstack/spec@4.1.0
    • @objectstack/core@4.1.0

4.0.5

Patch Changes

  • 15e0df6: chore: unify all package versions to a single patch release
  • Updated dependencies [15e0df6]
    • @objectstack/spec@4.0.5
    • @objectstack/core@4.0.5

4.0.4

Patch Changes

  • Updated dependencies [326b66b]
    • @objectstack/spec@4.0.4
    • @objectstack/core@4.0.4

4.0.3

Patch Changes

  • @objectstack/spec@4.0.3
  • @objectstack/core@4.0.3

4.0.2

Patch Changes

  • 5f659e9: fix ai
  • Updated dependencies [5f659e9]
    • @objectstack/spec@4.0.2
    • @objectstack/core@4.0.2

3.3.2

Patch Changes

  • Updated dependencies [f08ffc3]
  • Updated dependencies [e0b0a78]
    • @objectstack/spec@4.0.0
    • @objectstack/core@4.0.0

3.3.1

Patch Changes

  • @objectstack/spec@3.3.1
  • @objectstack/core@3.3.1

3.3.0

Minor Changes

  • 814a6c4: sql driver

Patch Changes

  • @objectstack/spec@3.3.0
  • @objectstack/core@3.3.0