Skip to content

Releases: objectstack-ai/framework

@objectstack/observability@5.0.0

22 May 15:30
378e5ef

Choose a tag to compare

Patch Changes

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

@objectstack/objectql@5.1.0

22 May 16:02
3ec28b4

Choose a tag to compare

Patch Changes

  • 75f4ee6: feat(metadata): introduce executionPinned capability for runtime version pinning (ADR-0009)

    Adds a new capability flag on the metadata type registry so that types whose runtime
    transaction rows reference a specific historical version (flow, workflow, approval)
    get unified pinning behavior — instead of every business table re-implementing its
    own snapshot column.

    • MetadataTypeRegistryEntrySchema gains executionPinned: boolean, enforced
      invariant executionPinned ⇒ supportsVersioning.
    • flow, workflow, approval flipped to executionPinned: true. approval
      also corrected to supportsVersioning: true (it was wrongly false).
    • MetadataRepository.getByHash(ref, hash) added to the interface. Production
      implementation in SysMetadataRepository resolves historical bodies through
      sys_metadata_history keyed by (organization_id, type, name, checksum).
      In-memory and FS repositories serve HEAD-only matches.
    • sys_metadata_history gains an index on (organization_id, type, name, checksum)
      to keep hash lookups O(log n).
    • HistoryCleanupManager skips pinned types entirely (both age-based and
      count-based retention) — pinned-type history must never be GC'd.

    See docs/adr/0009-execution-pinned-metadata.md for full rationale and the
    list of rejected alternatives (no shared snapshot table, no inlined snapshot column).

  • 823d559: Remove sys_metadata_history.metadata_id column.

    The column was originally a Field.lookup FK into sys_metadata.id,
    then downgraded to plain text during the M1 history-writes work so
    that DELETE tombstones could keep an orphaned ref. After M1 we
    concluded the column carries no business value:

    • Audit-time joins use (organization_id, type, name, version),
      which is already a UNIQUE composite key.

    • The physical row id is a database-internal detail with no logical
      identity — it cannot follow an item through delete + recreate.

    • No code reader was ever added.

      This release removes the column outright:

    • Dropped metadata_id from SysMetadataHistoryObject
      (@objectstack/platform-objects).

    • Dropped metadataId from MetadataHistoryRecordSchema
      (@objectstack/spec).

    • SysMetadataRepository.put/delete no longer write the column.

    • Legacy DatabaseLoader.createHistoryRecord no longer writes it;
      getHistoryRecord/queryHistory filter by (type, name) directly
      (no parent-row lookup needed).

    • MetadataHistoryCleanup maxVersions policy groups by
      (type, name) instead of metadata_id.

      Migration: Drop the column from existing sys_metadata_history
      tables in a follow-up SQL migration. Existing history rows remain
      queryable since (organization_id, type, name, version) is already
      the canonical lookup key. No consumer code should be reading
      metadata_id — if you are, switch to (organization_id, type, name, version).

      See ADR-0008 §14 for the full rationale.

  • Updated dependencies [75f4ee6]

  • Updated dependencies [823d559]

    • @objectstack/spec@5.1.0
    • @objectstack/metadata-core@5.1.0
    • @objectstack/core@5.1.0
    • @objectstack/formula@5.1.0
    • @objectstack/types@5.1.0

@objectstack/objectql@5.0.0

22 May 15:30
378e5ef

Choose a tag to compare

Minor Changes

  • 5e9dcb4: BREAKING — metadata: remove project and branch from MetaRef

    The metadata layer no longer models project or branch. Customisation is now
    scoped purely to organisation. Project remains exclusively as an artifact
    packaging concept (the objectstack.json bundle envelope); branching is left
    to Git.

    What changed:

    • MetaRef is now { org, type, name, version? } (was
      { org, project, branch, type, name, version? }). refKey() is the two
      segment string ${org}/${type}/${name} (was five segments).
    • MetadataItem.seq is monotonic per org (was per branch).
    • BranchRef, MergeStrategy, MergeResult types and the optional
      fork/merge methods on MetadataRepository are removed.
    • ListFilter / WatchFilter / HistoryOptions no longer accept project
      or branch.
    • FileSystemRepository disk layout simplified to
      <root>/<type>/<name>.json (was <root>/<project>/<branch>/<type>/<name>.json);
      change-log path is now .objectstack/.log/main.jsonl regardless of any
      branch concept. Constructor no longer accepts project / branch.
    • SysMetadataRepository: removed projectLabel / branchLabel options;
      the sys_metadata schema's project_id / branch columns (if present)
      are ignored. A future major release will DROP them.
    • MetadataManager.setRepository(repo, opts) no longer takes an opts object
      with branch.

    Migration:

    -const ref = { org: 'acme', project: 'crm', branch: 'main', type: 'view', name: 'home' };
    +const ref = { org: 'acme', type: 'view', name: 'home' };
    
    -new FileSystemRepository({ root, org: 'acme', project: 'crm', branch: 'main' });
    +new FileSystemRepository({ root, org: 'acme' });

    Existing sys_metadata rows continue to load; the deprecated columns are
    ignored at read time.

  • f139a24: Subscribe ObjectQLPlugin to metadata.subscribe('object', …) so the
    SchemaRegistry merge cache is invalidated and the affected object
    re-registered on every object metadata change (ADR-0008 M0 PR-7).

    Combined with the PR-6 metadata ↔ repository bridge, this closes the
    Studio HMR loop end-to-end: editing an object definition (file, REST
    write, or Studio inline edit) emits a MetadataEvent, which flows
    through MetadataManager.subscribe('object', …) into ObjectQL, which
    drops the cached merged definition and re-fetches the canonical body
    from the metadata service. Subsequent reads see the new schema with
    no server restart.

    Additions:

    • SchemaRegistry.invalidate(fqnOrName) and invalidateAll()
      public hooks for event-driven cache eviction; contributors are
      preserved so resolveObject recomputes against the next call.
    • ObjectQLPlugin.start() wires the subscription when the metadata
      service exposes subscribe(). The handler invalidates, re-fetches
      via metadata.get('object', name), and re-registers with the
      original packageId / namespace. Deletes only invalidate.
    • ObjectQLPlugin.stop() drains the subscription handles so test
      reloads don't leak watchers.
  • 2f7e42a: ADR-0008 M0 PR-10b: introduce SysMetadataRepository — a
    MetadataRepository wrapper over the existing sys_metadata table.
    M0 keeps single-row update semantics (append-only event log is M1
    work). Whitelist enforcement, optimistic locking via content hash,
    and in-process watch fan-out are all live. Not yet wired into any
    production write path — PR-10c will compose it under a
    LayeredRepository.

  • 888a5c1: PR-10d.3 — feature flag for SysMetadataRepository.put write path in saveMetaItem.

    • ObjectStackProtocolImplementation now accepts an options.useRepositoryWritePath flag
      (also honored via OBJECTSTACK_USE_REPOSITORY_WRITE_PATH=1) that routes overlay writes
      through SysMetadataRepository.put, appending to the change-log and emitting HMR seq.
    • saveMetaItem request grew optional parentVersion (If-Match) and actor fields.
      ConflictError is mapped to a 409 metadata_conflict API error.
    • Plural metadata type aliases (views, dashboards, ...) are normalized to singular
      before the repo's overlay-allowlist gate.
    • SysMetadataRepository.put/delete now update/delete by row id (the engine's
      strict .update semantics require an id or multi:true).
    • sys_metadata.checksum column widened from 64 → 71 chars to hold the "sha256:"
      prefix produced by hashSpec().
    • Default behaviour unchanged: legacy raw-engine path remains until PR-10d.4 flips the
      flag and removes it.
  • 09f005a: PR-10d.5 — Flip default of useRepositoryWritePath to true.

    saveMetaItem now routes overlay-allowed metadata types (view, dashboard,
    report, email_template) through SysMetadataRepository.put by default —
    every write appends to the change log and emits a watch event with a
    monotonic seq for HMR / replay.

    Non-overlay-allowed types (object, flow, agent, ...) still take the
    legacy raw-engine path. This preserves control-plane bootstrap behaviour
    (which writes object/flow definitions via saveMetaItem and is
    permitted by the outer protocol gate to write any type when projectId
    is undefined).

    Opt-out remains available during the deprecation window:

    • Constructor: new ObjectStackProtocolImplementation(engine, …, { useRepositoryWritePath: false })
    • Env var: OBJECTSTACK_USE_REPOSITORY_WRITE_PATH=0

    The legacy raw-engine branch for overlay-allowed types is scheduled for
    removal in PR-10d.6 once this default has soaked for one release.

Patch Changes

  • 4eb9f8c: ADR-0008 M0 PR-10a: pin overlay-whitelist + canonical-hash invariants
    before re-expressing the overlay path as a LayeredRepository. No
    runtime change — adds 28 regression tests that fail loud if a future
    PR weakens the shared-DB tenancy contract or breaks hash stability.

  • 602cce7: test(objectql): integration coverage for LayeredRepository composed of
    SysMetadataRepository (top, writable overlay) over InMemoryRepository
    (bottom, artifact baseline). Verifies read fallthrough, overlay-wins
    precedence, write routing, delete behavior, event source tagging across
    layers, and merged-list semantics. Part of ADR-0008 PR-10c.

  • 1e625b8: feat(objectql): hash-compat dry-run probe for the legacy → repository
    write-path migration (ADR-0008 PR-10d.1). Pure-function runDryRun() plus
    a CLI (scripts/dry-run-hash-compat.ts) that audits a snapshot of
    sys_metadata for invalid JSON, non-object bodies, unstable hashes across
    canonical round-trip, and duplicate overlay keys. Exits non-zero when
    incompatibilities are found. 14 unit tests covering happy paths, error
    classifications (invalid_json, non_object_body, unstable_hash,
    missing_metadata, duplicate_overlay_key), and boundary conditions
    (empty snapshot, deep nesting, unicode).

  • 6ee42b8: fix(objectql): SysMetadataRepository reuses the existing checksum column
    instead of writing a non-existent _hash column (ADR-0008 PR-10d.2). The
    production sys_metadata schema (packages/platform-objects) already
    ships with checksum: text(64) — perfect for sha256 hex — and version: number for the monotonic counter. No DDL migration is required for
    PR-10d.3 cutover; legacy rows with NULL checksum will be lazily
    backfilled on first put().

    Also extends the PR-10d.1 dry-run probe with two new checks
    (checksum_missing warning, checksum_drift error) and three additional
    tests, taking objectql to 325/325 green.

  • 5cfdc85: PR-10d.4 — REST plumbing for the metadata repository write path.

    • PUT /api/v1/meta/:type/:name (and the compound :type/:section/:name variant)
      now forwards the If-Match header to saveMetaItem as parentVersion, and
      X-Actor (or req.user.id) as actor. ETag-style quotes are stripped.
    • A failed optimistic-lock check surfaces as HTTP 409 with body
      { "error": "...", "code": "metadata_conflict" } (no protocol changes —
      sendError already honoured error.status + error.code).
    • Added a real-engine integration test for the repository write path
      (protocol-save-meta-repo-path-real-engine.test.ts) — addresses the
      PR-10d.3 rubber-duck stub-drift concern by exercising
      ObjectStackProtocolImplementation.saveMetaItem through new ObjectQL()
      with an inline in-memory driver. Covers insert→update version bump,
      parentVersion conflict, checksum length, and plural→singular normalization.

    Default behaviour unchanged: the repository write path remains opt-in via
    options.useRepositoryWritePath / OBJECTSTACK_USE_REPOSITORY_WRITE_PATH=1.
    Flag flip and legacy path removal will follow in a separate post-soak PR.

  • 7825394: PR-10d.6 — remove useRepositoryWritePath feature flag.

    Overlay-allowed metadata types (view, dashboard, report,
    email_template) now unconditionally route through
    SysMetadataRepository.put (change-log + HMR seq). The legacy
    raw-engine branch is retained for non-overlay types (object, flow,
    agent, etc.) used during control-plane bootstrap, since the repository
    assertAllowed() whitelist would reject them.

    Removed:

    • ObjectStackProtocolImplementation constructor option
      { useRepositoryWritePath: boolean }.
    • OBJECTSTACK_USE_REPOSITORY_WRITE_PATH environment variable.

    There is no opt-out: behavior is now equivalent to the PR-10d.5 default.

  • 96ad4df: Fix dev-mode HMR data-reload for *.view.ts / *.flow.ts source...

Read more

@objectstack/objectql@4.2.0

22 May 08:47
bcdae40

Choose a tag to compare

Minor Changes

  • 2869891: feat: Optimistic Concurrency Control (OCC) via If-Match

    Update and Delete requests now accept an optional version token. When supplied,
    the protocol compares it against the record's current updated_at (or version
    column when available) and rejects with 409 CONCURRENT_UPDATE on mismatch,
    preventing silent overwrites when two clients edit the same record.

    Wire formats (opt-in, all server- and client-backward-compatible):

    • PATCH /data/{object}/{id} — supports If-Match: "<token>" header
      or expectedVersion: "<token>" body field (body wins when both present).

    • DELETE /data/{object}/{id} — supports If-Match header or
      ?expectedVersion=... query param.

    • Conflict response: 409 { error, code: 'CONCURRENT_UPDATE', currentVersion, currentRecord } so the client can offer Reload / Overwrite / Cancel UX.

      Behaviour

    • Missing/empty version → no check (legacy callers unaffected).

    • Record not found during the version probe → no check; the downstream write
      produces a normal 404.

    • Object has no updated_at column → no check (explicit opt-out for objects
      without timestamps).

    • Quoted RFC-7232 tokens ("…") are accepted and unquoted before comparison.

      Client

      client.data.update(resource, id, data, { ifMatch }) and
      client.data.delete(resource, id, { ifMatch }) now forward the token as an
      If-Match header.

      Application-level CAS (findOne + compare in protocol.ts) is used in this slice
      to avoid touching every storage driver. A small TOCTOU window remains; for the
      B2B record-editing latencies this protects against, it is more than sufficient.
      Drivers may later be upgraded to atomic WHERE id=? AND updated_at=? writes
      for true CAS without changing the public API.

      Tests: 7 new cases in protocol-data.test.ts cover opt-in, match, mismatch,
      quote-stripping, no-timestamps, empty-token, and the delete path.

Patch Changes

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

@objectstack/objectql@4.1.1

22 May 06:30
c3b6713

Choose a tag to compare

Patch Changes

  • @objectstack/spec@4.1.1
  • @objectstack/core@4.1.1
  • @objectstack/types@4.1.1
  • @objectstack/formula@4.1.1

@objectstack/nuxt@5.1.0

22 May 16:01
3ec28b4

Choose a tag to compare

@objectstack/nuxt@5.1.0

@objectstack/nuxt@5.0.0

22 May 15:31
378e5ef

Choose a tag to compare

Patch Changes

  • Updated dependencies [5e9dcb4]
  • Updated dependencies [96ad4df]
  • Updated dependencies [df18ae9]
    • @objectstack/runtime@5.0.0

@objectstack/nuxt@4.2.0

22 May 08:46
bcdae40

Choose a tag to compare

@objectstack/nuxt@4.2.0

@objectstack/nuxt@4.1.1

22 May 06:29
c3b6713

Choose a tag to compare

@objectstack/nuxt@4.1.1

@objectstack/nextjs@5.1.0

22 May 16:01
3ec28b4

Choose a tag to compare

@objectstack/nextjs@5.1.0