From ebae4b6e9ef0bcdeb4621b4c66b397645515e25e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 03:05:12 +0000 Subject: [PATCH 1/3] test(engine-doubles): adopt assertEngineFindOnePredicate across 212 files / 245 findOne doubles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adoption half of #12068. Discovery was NOT re-derived by hand: the population is `check:engine-double-contract`'s own reported (file, line) answers with a findOne slice installed, so the batch cannot be narrower or wider than what the gate scores. `@objectstack/service-datasource` gains `@objectstack/metadata-core` as a devDependency — the one package in the remainder that depended on neither home of the predicate. Acyclic, measured rather than cited: `turbo run build --filter=@objectstack/service-datasource --dry` exits 0 with zero circular-dependency reports and metadata-core present in the graph. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o --- .../core/src/utils/migration-journal.test.ts | 3 ++- packages/mcp/src/__tests__/plugin.test.ts | 3 ++- packages/mcp/src/mcp-stdio-tools.test.ts | 4 ++-- ...te-response-internal-fields.tripwire.test.ts | 4 ++-- .../src/plugin.record-resource-exposure.test.ts | 4 ++-- .../mcp/src/stdio-data-bridge.exposure.test.ts | 3 ++- .../mcp/src/stdio-data-bridge.not-found.test.ts | 3 ++- .../src/migrations/recorded-by-sentinel.test.ts | 3 ++- .../protocol-publish-drafts-advisories.test.ts | 3 ++- .../src/protocol-publish-drafts-closure.test.ts | 3 ++- ...rotocol-publish-drafts-endpoint-gate.test.ts | 3 ++- .../protocol-publish-drafts-org-scope.test.ts | 3 ++- ...rotocol-publish-drafts-package-scope.test.ts | 3 ++- ...otocol.adr0005-org-override-rollback.test.ts | 3 ++- .../src/protocol.audit-field-governance.test.ts | 3 ++- .../src/protocol.batch-atomic.test.ts | 4 ++-- .../src/protocol.batch-not-attempted.test.ts | 4 ++-- .../src/protocol.batch-row-conformance.test.ts | 4 ++-- .../src/protocol.batch-row-driver-text.test.ts | 4 ++-- .../src/protocol.batch-row-http-status.test.ts | 4 ++-- .../src/protocol.batch-verb-driver-code.test.ts | 3 ++- .../src/protocol.batch-verb-driver-text.test.ts | 3 ++- .../src/protocol.bulk-record-not-found.test.ts | 4 ++-- .../src/protocol.capability-write-door.test.ts | 3 ++- .../src/protocol.code-only-types.test.ts | 3 ++- .../protocol.container-issue-descent.test.ts | 5 +++-- ...tocol.dashboard-dataset-publish-gate.test.ts | 3 ++- ...ol.delete-object-registry-unregister.test.ts | 3 ++- .../src/protocol.delete-receipt-wording.test.ts | 3 ++- .../src/protocol.delete-rewrap-envelope.test.ts | 3 ++- ...tocol.destructive-409-face-inventory.test.ts | 2 ++ ...col.destructive-gate-reachable-types.test.ts | 3 ++- .../protocol.diagnostics-store-outage.test.ts | 12 +++++++----- ...ff-canonical-type-and-history-outage.test.ts | 3 ++- .../protocol.diff-credential-redaction.test.ts | 3 ++- .../src/protocol.diff-dead-history-read.test.ts | 3 ++- .../src/protocol.driver-text-disclosure.test.ts | 3 ++- .../src/protocol.dropped-fields.bulk.test.ts | 6 +++--- .../src/protocol.dropped-fields.test.ts | 8 ++++---- .../protocol.flow-org-override-closed.test.ts | 3 ++- .../protocol.graft-folded-form-sections.test.ts | 3 ++- ...tocol.hydrate-overlay-canonical-type.test.ts | 4 +++- .../src/protocol.i18n-bundle-list-merge.test.ts | 5 +++-- .../protocol.injected-system-columns.test.ts | 3 ++- ....invalid-metadata-422-face-inventory.test.ts | 5 +++-- .../src/protocol.item-name-grammar.test.ts | 3 ++- .../src/protocol.legacy-overlay-delete.test.ts | 4 +++- .../src/protocol.lifecycle-audit-rows.test.ts | 3 ++- .../protocol.lock-gate-canonical-type.test.ts | 2 ++ .../src/protocol.lock-gate-fail-closed.test.ts | 3 ++- .../src/protocol.many-data-atomic.test.ts | 4 ++-- ...col.meta-list-runtime-baseline-dedup.test.ts | 4 +++- ...tocol.meta-types-mint-door-agreement.test.ts | 5 +++-- .../src/protocol.metadata-redaction.test.ts | 3 ++- .../src/protocol.metadata-store-outage.test.ts | 6 ++++-- ...col.migrate-stored-noncanonical-type.test.ts | 3 ++- ...ject-registry-write-through-spelling.test.ts | 3 ++- ...scoped-cold-boot-audit-live-registry.test.ts | 5 +++-- .../protocol.org-scoped-cold-boot-audit.test.ts | 5 +++-- .../protocol.org-scoped-write-refused.test.ts | 3 ++- .../src/protocol.package-closure-gate.test.ts | 3 ++- .../protocol.package-publish-audit-rows.test.ts | 3 ++- .../protocol.platform-schedule-org-gate.test.ts | 3 ++- ....publish-commit-capture-read-failure.test.ts | 3 ++- ...rotocol.publish-item-draft-org-scope.test.ts | 3 ++- ...rotocol.publish-item-rebind-announce.test.ts | 3 ++- ....publish-side-effects-canonical-type.test.ts | 3 ++- ...otocol.publish-stored-type-canonical.test.ts | 3 ++- .../src/protocol.read-decorations.test.ts | 3 ++- ...protocol.read-seam-empty-accumulator.test.ts | 13 +++++++------ .../protocol.read-verb-canonical-fold.test.ts | 2 ++ .../src/protocol.record-not-found.test.ts | 4 ++-- ...col.record-package-commit-durability.test.ts | 3 ++- .../src/protocol.reference-derivation.test.ts | 4 +++- ...otocol.reference-target-unanswerable.test.ts | 4 +++- ...rotocol.revert-stored-type-canonical.test.ts | 3 ++- .../src/protocol.runtime-authoring-gate.test.ts | 3 ++- .../protocol.save-flow-canonicalization.test.ts | 3 ++- .../src/protocol.save-meta-missing-item.test.ts | 3 ++- .../src/protocol.save-receipt-wording.test.ts | 3 ++- .../src/protocol.save-union-issues.test.ts | 5 +++-- .../src/protocol.stored-conversions.test.ts | 3 ++- .../src/protocol.stored-migration.test.ts | 3 ++- .../src/protocol.stored-residue-resave.test.ts | 3 ++- .../src/protocol.unrecognised-meta-type.test.ts | 3 ++- .../src/protocol.update-path-id-wins.test.ts | 3 ++- .../src/protocol.upsert-existence.test.ts | 3 ++- ...te-response-internal-fields.tripwire.test.ts | 4 ++-- .../src/search-clone-schema-conformance.test.ts | 3 ++- .../seed-loader-composite-external-id.test.ts | 3 ++- .../src/seed-loader-deferred-dropped.test.ts | 3 ++- .../src/seed-loader-deferred-failure.test.ts | 3 ++- .../src/seed-loader-driver-text.test.ts | 5 +++-- .../seed-loader-engine-schema-fallback.test.ts | 3 ++- .../src/seed-loader-env-scope.test.ts | 4 ++-- ...loader-existing-records-read-failure.test.ts | 3 ++- .../seed-loader-multi-value-reference.test.ts | 3 ++- .../src/seed-loader-name-probe.test.ts | 3 ++- .../src/seed-loader-pointer-pair.test.ts | 3 ++- .../src/seed-loader-replay.test.ts | 3 ++- .../src/seed-loader-retry.test.ts | 3 ++- .../seed-loader-state-machine-exempt.test.ts | 4 ++-- .../src/seed-loader-summary-stale.test.ts | 3 ++- .../src/seed-loader-unresolved-drop.test.ts | 3 ++- .../sys-metadata-repository.contract.test.ts | 3 ++- .../sys-metadata-repository.draft-drain.test.ts | 3 ++- ...ata-repository.draft-package-inherit.test.ts | 3 ++- ...metadata-repository.history-counters.test.ts | 3 ++- ...adata-repository.package-writability.test.ts | 3 ++- .../sys-metadata-repository.recorded-by.test.ts | 3 ++- .../view-container-runtime-expansion.test.ts | 5 +++-- .../migrate-sys-notification-to-event.test.ts | 5 +++-- .../accept-invitation-adopt-membership.test.ts | 3 ++- ...dmin-revoke-user-session-match-guard.test.ts | 3 ++- .../src/audience-posture-setting.test.ts | 3 ++- .../plugin-auth/src/audience-posture.test.ts | 3 ++- .../src/auth-contains-filter.test.ts | 3 ++- .../src/auth-manager.jwt-eddsa-fallback.test.ts | 3 ++- ...th-manager.optional-plugin-isolation.test.ts | 2 ++ .../plugin-auth/src/auth-manager.test.ts | 7 +++++-- .../src/auth-where-operator-coverage.test.ts | 4 ++-- .../src/change-email-delete-user-wiring.test.ts | 3 ++- .../first-session-membership-ordering.test.ts | 6 ++++-- .../src/impersonation-bearer-rotation.test.ts | 3 ++- .../src/membership-policy-setting.test.ts | 3 ++- .../src/org-create-posture-gate.test.ts | 3 ++- .../src/organization-add-member.test.ts | 3 ++- .../src/remove-member-permission-guard.test.ts | 3 ++- .../src/remove-user-atomicity.test.ts | 3 ++- .../src/revoke-session-match-guard.test.ts | 3 ++- .../scim-case-insensitive-identifier.test.ts | 3 ++- .../plugin-auth/src/session-of-record.test.ts | 3 ++- .../plugin-auth/src/session-tombstone.test.ts | 3 ++- .../src/authored-row-write-verdict.test.ts | 3 ++- .../src/check-only-write-scope.test.ts | 3 ++- .../src/controlled-by-parent-chain.test.ts | 2 ++ ...led-by-parent-detail-write-authority.test.ts | 2 ++ .../controlled-by-parent-master-widener.test.ts | 2 ++ .../src/controlled-by-parent-sharing.test.ts | 3 ++- .../plugin-security/src/explain-engine.test.ts | 3 ++- .../src/field-masking-rule.test.ts | 5 +++-- .../src/invitation-placement.test.ts | 3 ++- .../packaged-permission-set-lock-gate.test.ts | 3 ++- .../src/packaged-permission-set-lock.test.ts | 3 ++- .../packaged-permission-set-restore-leg.test.ts | 3 ++- .../src/permission-denied-user-copy.test.ts | 3 ++- .../src/permission-set-active.test.ts | 3 ++- .../src/permission-set-projection.test.ts | 2 ++ .../src/permission-set-resolution-memo.test.ts | 3 ++- ...olve-permission-sets-for-context.pin.test.ts | 3 ++- .../src/row-write-widener-composition.test.ts | 3 ++- .../src/security-denial-user-copy.test.ts | 3 ++- .../plugin-security/src/security-plugin.test.ts | 10 ++++++---- .../src/select-only-write-visibility.test.ts | 3 ++- .../src/store-fault-fail-closed.test.ts | 2 ++ .../src/vama-write-path-convergence.test.ts | 3 ++- .../src/authored-row-write-deferral.test.ts | 3 ++- .../src/share-link-eligibility.test.ts | 4 ++-- .../plugin-webhooks/src/auto-enqueuer.test.ts | 2 ++ .../src/bootstrap-declared-webhooks.test.ts | 2 ++ .../src/webhook-drop-durable-record.test.ts | 3 ++- .../src/webhook-signing-secret.test.ts | 5 +++-- .../src/meta-compound-save-force-parity.test.ts | 3 ++- .../src/meta-compound-save-mode-parity.test.ts | 3 ++- .../src/meta-object-extension-agreement.test.ts | 4 ++-- ...ta-object-extension-property-classes.test.ts | 4 ++-- ...eta-object-materialization-agreement.test.ts | 4 ++-- .../meta-object-overlay-extension-fold.test.ts | 4 ++-- ...ta-object-search-companion-agreement.test.ts | 4 ++-- .../rest/src/meta-published-overlay.test.ts | 3 ++- .../rest/src/public-form-lookup-picker.test.ts | 5 +++-- .../src/public-form-routes.stored-row.test.ts | 5 +++-- .../src/rest-server-meta-read-org-scope.test.ts | 3 ++- ...ion-execution-calldata-batch-retired.test.ts | 3 ++- .../action-execution-calldata-not-found.test.ts | 4 ++-- .../meta-published-runtime-publish.test.ts | 3 ++- .../meta-save-destructive-remedy.test.ts | 3 ++- .../src/meta-compound-arity-mint-door.test.ts | 3 ++- .../runtime/src/meta-field-overlay-lock.test.ts | 3 ++- .../src/meta-overlay-read-your-writes.test.ts | 4 ++-- .../runtime/src/meta-write-org-scope.test.ts | 3 ++- packages/runtime/src/seed-loader.test.ts | 3 ++- .../src/builtin/crud-bulk-intent.test.ts | 4 ++-- .../src/builtin/crud-config-aliases.test.ts | 4 +++- .../src/builtin/crud-filter-guard.test.ts | 4 +++- .../src/builtin/crud-output-var.test.ts | 4 +++- .../src/builtin/crud-runas.test.ts | 4 +++- ...otify-organization-stamp.integration.test.ts | 4 +++- .../record-lookup-expand.integration.test.ts | 2 ++ .../service-automation/src/run-summary.test.ts | 17 +++++++++++------ .../runas-grant-resolution.integration.test.ts | 4 +++- .../services/service-datasource/package.json | 1 + .../__tests__/datasource-admin-plugin.test.ts | 2 ++ ...delivery-headers-at-rest.integration.test.ts | 4 ++-- .../service-messaging/src/email-channel.test.ts | 2 ++ .../service-messaging/src/inbox-channel.test.ts | 2 ++ .../src/messaging-service-plugin.test.ts | 7 +++++-- .../src/messaging-service.test.ts | 15 +++++++++++---- .../src/notification-schema-conformance.test.ts | 3 ++- .../src/preference-resolver.test.ts | 4 +++- .../src/read-receipt-organization.test.ts | 2 ++ .../src/recipient-resolver.test.ts | 2 ++ .../service-messaging/src/sms-channel.test.ts | 2 ++ .../src/sql-outbox-audit-columns.test.ts | 2 ++ .../src/template-renderer.test.ts | 2 ++ .../src/attachment-access-hooks.test.ts | 6 +++--- .../src/attachment-lifecycle.test.ts | 3 ++- .../src/file-reference-lifecycle.test.ts | 2 ++ .../src/lax-deviation-reclamation-gate.test.ts | 3 ++- .../service-storage/src/metadata-store.test.ts | 3 ++- .../src/storage-routes.metadata-outage.test.ts | 3 ++- .../tombstone-download-live-reference.test.ts | 3 ++- pnpm-lock.yaml | 3 +++ 213 files changed, 498 insertions(+), 260 deletions(-) diff --git a/packages/core/src/utils/migration-journal.test.ts b/packages/core/src/utils/migration-journal.test.ts index bfc0839703..6165b045a7 100644 --- a/packages/core/src/utils/migration-journal.test.ts +++ b/packages/core/src/utils/migration-journal.test.ts @@ -27,7 +27,7 @@ import { assertEngineUpdateDispatch, type EngineDeleteDispatchInput, type EngineUpdateDispatchData, - type EngineUpdateDispatchInput, + type EngineUpdateDispatchInput, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { runMigrationJournal, @@ -85,6 +85,7 @@ class FakeEngine { } async findOne(objectName: string, query?: { where?: Record }): Promise { + assertEngineFindOnePredicate(objectName, query); return (await this.find(objectName, query))[0] ?? null; } diff --git a/packages/mcp/src/__tests__/plugin.test.ts b/packages/mcp/src/__tests__/plugin.test.ts index f5f97c070c..03c78778ca 100644 --- a/packages/mcp/src/__tests__/plugin.test.ts +++ b/packages/mcp/src/__tests__/plugin.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { MCPServerPlugin } from '../plugin.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // Mock PluginContext @@ -69,7 +70,7 @@ function createMockMetadataService() { function createMockDataEngine() { return { find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(), update: vi.fn(), delete: vi.fn(), diff --git a/packages/mcp/src/mcp-stdio-tools.test.ts b/packages/mcp/src/mcp-stdio-tools.test.ts index 6639c12581..60ae4ac3a2 100644 --- a/packages/mcp/src/mcp-stdio-tools.test.ts +++ b/packages/mcp/src/mcp-stdio-tools.test.ts @@ -41,7 +41,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { MCPServerRuntime } from './mcp-server-runtime.js'; import { MCPServerPlugin } from './plugin.js'; import type { McpDataBridge } from './mcp-http-tools.js'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // A real stdio client: newline-delimited JSON-RPC over the transport's pipes @@ -503,7 +503,7 @@ describe('#8034 plugin composition: os serve stdio wiring', () => { }), aggregate: vi.fn(async () => []), count: vi.fn(async () => 0), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; } diff --git a/packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts b/packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts index ce4469b385..87a4e773e2 100644 --- a/packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts +++ b/packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts @@ -46,7 +46,7 @@ import type { ExecutionContext } from '@objectstack/spec/kernel'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; import { omitInternalFieldsFromWriteResponse } from '@objectstack/core'; import { createStdioDataBridge } from './stdio-data-bridge.js'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** The value that must NEVER appear in any MCP response. */ const SENTINEL = 'INTERNAL-SENTINEL-8497-NEVER-SERIALIZED'; @@ -81,7 +81,7 @@ function makeSentinelEngine(): IDataEngine { }); return { find: vi.fn(async () => [storedRow()]), - findOne: vi.fn(async () => storedRow()), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return storedRow(); }), insert: vi.fn(async (_o: string, data: Record) => writtenRow((data?.id as string) ?? 'new-1', data)), update: vi.fn(async (_o: string, data: Record, options?: any) => { diff --git a/packages/mcp/src/plugin.record-resource-exposure.test.ts b/packages/mcp/src/plugin.record-resource-exposure.test.ts index deffd4c394..0220fe5f9d 100644 --- a/packages/mcp/src/plugin.record-resource-exposure.test.ts +++ b/packages/mcp/src/plugin.record-resource-exposure.test.ts @@ -50,7 +50,7 @@ import type { IMetadataService } from '@objectstack/spec/contracts'; import type { MCPServerRuntime } from './mcp-server-runtime.js'; import { MCPServerPlugin } from './plugin.js'; import { enforceApiExposure, GATED_ACTIONS, type McpExposureError } from './stdio-data-bridge.js'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // A real stdio client: newline-delimited JSON-RPC over the transport's pipes @@ -172,7 +172,7 @@ function fakeObjectQL() { }), aggregate: vi.fn(async () => []), count: vi.fn(async () => 0), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; } diff --git a/packages/mcp/src/stdio-data-bridge.exposure.test.ts b/packages/mcp/src/stdio-data-bridge.exposure.test.ts index 8651196d95..75f2fcc02a 100644 --- a/packages/mcp/src/stdio-data-bridge.exposure.test.ts +++ b/packages/mcp/src/stdio-data-bridge.exposure.test.ts @@ -39,6 +39,7 @@ import { DATA_ACTION_TO_API_OPERATION } from '@objectstack/spec/data'; import type { ExecutionContext } from '@objectstack/spec/kernel'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; import { createStdioDataBridge, GATED_ACTIONS, type McpExposureError } from './stdio-data-bridge.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // Doubles @@ -54,7 +55,7 @@ import { createStdioDataBridge, GATED_ACTIONS, type McpExposureError } from './s function makeEngine(rows: Array> = [{ id: 'r1', title: 'row' }]) { return { find: vi.fn(async () => rows), - findOne: vi.fn(async () => rows[0] ?? null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return rows[0] ?? null; }), insert: vi.fn(), update: vi.fn(), delete: vi.fn(), diff --git a/packages/mcp/src/stdio-data-bridge.not-found.test.ts b/packages/mcp/src/stdio-data-bridge.not-found.test.ts index 53b81aa8bc..e533773eb5 100644 --- a/packages/mcp/src/stdio-data-bridge.not-found.test.ts +++ b/packages/mcp/src/stdio-data-bridge.not-found.test.ts @@ -23,12 +23,13 @@ import { describe, it, expect, vi } from 'vitest'; import type { ExecutionContext } from '@objectstack/spec/kernel'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; import { createStdioDataBridge } from './stdio-data-bridge.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** An engine that resolves NO row for any id — every by-id write is a miss. */ function makeEmptyEngine() { return { find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(), update: vi.fn(), delete: vi.fn(), diff --git a/packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts b/packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts index cf042f02db..f08d972ff0 100644 --- a/packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts +++ b/packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts @@ -29,7 +29,7 @@ import { runMigrationJournal, readRunJournal } from '@objectstack/core'; // package both sides already depend on, which is what makes this line legal. import { assertEngineDeleteDispatch, - assertEngineUpdateDispatch, + assertEngineUpdateDispatch, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { createRecordedBySentinelPlan, @@ -77,6 +77,7 @@ class FakeEngine { } async findOne(objectName: string, query?: { where?: Record }): Promise { + assertEngineFindOnePredicate(objectName, query); return (await this.find(objectName, query))[0] ?? null; } diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts index cadcabecc1..0bbadbaaef 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts @@ -35,7 +35,7 @@ */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -117,6 +117,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts index ceebef10d7..b2f81eab11 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts @@ -56,7 +56,7 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so the fake engine below // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, // never from `@objectstack/objectql` — that import would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -155,6 +155,7 @@ function makeStubEngine(options?: { liveObjects?: unknown[]; livePermissions?: u const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts index 97d856d55d..1450cb457c 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts @@ -8,7 +8,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { identityFreeEndpointGateFailure, ApiEndpointSchema } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -149,6 +149,7 @@ function makeStubEngine(namespace?: string) { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts index 7d34f46e00..67d22a6583 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts @@ -8,7 +8,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; /** @@ -126,6 +126,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts b/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts index f00660d061..368a5a4f76 100644 --- a/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts +++ b/packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts @@ -6,7 +6,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; // refuses. Imported from `@objectstack/metadata-core` and not from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; /** @@ -135,6 +135,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts b/packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts index bce30b73bd..bf63d093f3 100644 --- a/packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts +++ b/packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts @@ -98,7 +98,7 @@ * cannot see it. */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -119,6 +119,7 @@ function makeStubEngine(artifacts: Array<{ type: string; name: string }> = []) { `${w.type}|${w.name}|${w.organization_id ?? '__env__'}|${w.state ?? 'active'}`; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.audit-field-governance.test.ts b/packages/metadata-protocol/src/protocol.audit-field-governance.test.ts index 5a9d760eea..17e335d669 100644 --- a/packages/metadata-protocol/src/protocol.audit-field-governance.test.ts +++ b/packages/metadata-protocol/src/protocol.audit-field-governance.test.ts @@ -45,7 +45,7 @@ import { describe, expect, it } from 'vitest'; // below cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core` // and not `@objectstack/objectql`: objectql DEPENDS ON this package, so that // import would close a cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { AUDIT_PROVENANCE_FIELDS } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation } from './index.js'; @@ -82,6 +82,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.batch-atomic.test.ts b/packages/metadata-protocol/src/protocol.batch-atomic.test.ts index 7518d0000b..c0ade64b05 100644 --- a/packages/metadata-protocol/src/protocol.batch-atomic.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-atomic.test.ts @@ -14,7 +14,7 @@ // is already in the database. import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { @@ -47,7 +47,7 @@ function makeTransactionalEngine(opts: { driverCanTransact?: boolean } = {}) { if (data?.title === POISON) throw new Error('update exploded'); return { id: options?.where?.id, ...data }; }); - const findOne = vi.fn(async (_object: string, options?: any) => ({ id: options?.where?.id })); + const findOne = vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return ({ id: options?.where?.id }); }); const del = vi.fn(async (_object: string, options?: any) => { assertEngineDeleteDispatch(options); return { deleted: 1 }; diff --git a/packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts b/packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts index 6752d02e38..adb3e62442 100644 --- a/packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts @@ -40,7 +40,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { @@ -101,7 +101,7 @@ function makeStoreEngine() { rows.delete(id); return { deleted: 1 }; }); - const findOne = vi.fn(async (_object: string, options?: any) => rows.get(options?.where?.id) ?? null); + const findOne = vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return rows.get(options?.where?.id) ?? null; }); const engine: any = { registry: { getObject: (n: string) => (n === 'showcase_private_note' ? SCHEMA : undefined) }, diff --git a/packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts b/packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts index ae3ffd9885..9ac2db4297 100644 --- a/packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts @@ -27,7 +27,7 @@ import { describe, it, expect, vi } from 'vitest'; import { BatchOperationResultSchema, BatchUpdateResponseSchema } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; -import { assertEngineUpdateDispatch, assertEngineDeleteDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const SCHEMA = { name: 'invoice', fields: { title: { name: 'title', type: 'text' } } }; @@ -74,7 +74,7 @@ function makeStoreEngine() { rows.delete(id); return { deleted: 1 }; }), - findOne: vi.fn(async (_object: string, options?: any) => rows.get(options?.where?.id)), + findOne: vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return rows.get(options?.where?.id); }), getDefaultDriverName: () => 'default', getDriverByName: () => ({ beginTransaction: async () => handle }), transaction: vi.fn(async (callback: (ctx: any) => Promise, baseContext?: any) => { diff --git a/packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts b/packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts index d95933ae7d..7de21344d3 100644 --- a/packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts @@ -66,7 +66,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { resolveThrownHttpError, validationFailureDetails } from '@objectstack/types'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -161,7 +161,7 @@ function makeEngine(throwOn: (verb: string, id: unknown) => unknown | undefined) const engine: any = { registry: { getObject: (n: string) => (n === 'leave_request' ? SCHEMA : undefined) }, - findOne: vi.fn(async (_o: string, opts?: any) => rows.get(opts?.where?.id) ?? null), + findOne: vi.fn(async (_o: string, opts?: any) => { assertEngineFindOnePredicate(_o, opts); return rows.get(opts?.where?.id) ?? null; }), insert: vi.fn(async (_o: string, data: any) => { const boom = throwOn('insert', data?.id); if (boom) throw boom; diff --git a/packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts b/packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts index 2cca99bde0..37f5cd79f3 100644 --- a/packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts @@ -57,7 +57,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { resolveThrownHttpError } from '@objectstack/types'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -163,7 +163,7 @@ function makeEngine(throwOn: (verb: string, id: unknown) => unknown | undefined) const engine: any = { registry: { getObject: (n: string) => (n === 'leave_request' ? SCHEMA : undefined) }, - findOne: vi.fn(async (_o: string, opts?: any) => rows.get(opts?.where?.id) ?? null), + findOne: vi.fn(async (_o: string, opts?: any) => { assertEngineFindOnePredicate(_o, opts); return rows.get(opts?.where?.id) ?? null; }), insert: vi.fn(async (_o: string, data: any) => { const boom = throwOn('insert', data?.id); if (boom) throw boom; diff --git a/packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts b/packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts index 3dca35b01c..0fb2299dea 100644 --- a/packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts @@ -96,7 +96,7 @@ import { describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update), from `@objectstack/metadata-core` — never `@objectstack/objectql`, // which depends on THIS package and would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // The closed union itself. Asserting membership against the SCHEMA rather than // against a literal is what makes section 1 a contract test: whatever lands on // the wire has to parse, whoever changes the catalog next. @@ -201,6 +201,7 @@ function makeKernel(opts: { return Array.from(rows.values()).filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); boom('findOne', table); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; diff --git a/packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts b/packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts index 75f69cd8e5..fea7800e1f 100644 --- a/packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts +++ b/packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts @@ -94,7 +94,7 @@ import { describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update). From `@objectstack/metadata-core`, never `@objectstack/objectql` // — objectql depends on THIS package, so that import would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; // --------------------------------------------------------------------------- @@ -188,6 +188,7 @@ function makeKernel(opts: { return Array.from(rows.values()).filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); boom('findOne', table); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; diff --git a/packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts b/packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts index 777e348503..163750fd71 100644 --- a/packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts +++ b/packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts @@ -32,7 +32,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { @@ -86,7 +86,7 @@ function makeStoreEngine() { rows.delete(id); return { deleted: 1 }; }); - const findOne = vi.fn(async (_object: string, options?: any) => rows.get(options?.where?.id) ?? null); + const findOne = vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return rows.get(options?.where?.id) ?? null; }); const engine: any = { registry: { getObject: (n: string) => (n === 'showcase_task' ? SCHEMA : undefined) }, diff --git a/packages/metadata-protocol/src/protocol.capability-write-door.test.ts b/packages/metadata-protocol/src/protocol.capability-write-door.test.ts index 0f53e2c22a..c540b6af36 100644 --- a/packages/metadata-protocol/src/protocol.capability-write-door.test.ts +++ b/packages/metadata-protocol/src/protocol.capability-write-door.test.ts @@ -92,7 +92,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -112,6 +112,7 @@ function makeStubEngine(registeredTypes: string[] = []) { `${w.type}|${w.name}|${w.organization_id ?? '__env__'}|${w.state ?? 'active'}`; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.code-only-types.test.ts b/packages/metadata-protocol/src/protocol.code-only-types.test.ts index 3f4e1add61..34b6816293 100644 --- a/packages/metadata-protocol/src/protocol.code-only-types.test.ts +++ b/packages/metadata-protocol/src/protocol.code-only-types.test.ts @@ -36,7 +36,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { resetEnvWritableMetadataTypes } from './sys-metadata-repository.js'; @@ -168,6 +168,7 @@ function makeStubEngine(artifacts: Array<{ type: string; name: string }> = []) { const writes: Array<{ op: 'insert' | 'update' | 'delete'; table: string }> = []; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.container-issue-descent.test.ts b/packages/metadata-protocol/src/protocol.container-issue-descent.test.ts index 048ba5fdaf..f20f4b6563 100644 --- a/packages/metadata-protocol/src/protocol.container-issue-descent.test.ts +++ b/packages/metadata-protocol/src/protocol.container-issue-descent.test.ts @@ -55,7 +55,7 @@ import { z } from 'zod'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // cannot accept a call ObjectQL would refuse. From `@objectstack/metadata-core` // and not `@objectstack/objectql`, which depends on this package. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { getMetadataTypeSchema } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation, zodIssuesToMetadataIssues } from './protocol.js'; @@ -76,7 +76,8 @@ function makeProtocol() { const rows = new Map(); let nextId = 0; const engine: any = { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return []; }, async insert(table: string, data: Record) { if (table === 'sys_metadata_audit') return { id: 'audit_skip' }; diff --git a/packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts b/packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts index 506a00e9b6..0fbe201a80 100644 --- a/packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts +++ b/packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts @@ -44,7 +44,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // Imported from `@objectstack/metadata-core` and not from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright (#5619). -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; // ───────────────────────────────────────────────────────────────────────────── @@ -129,6 +129,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts b/packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts index 3301e632c6..301a34de62 100644 --- a/packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts +++ b/packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts @@ -36,7 +36,7 @@ import { describe, expect, it, vi } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -129,6 +129,7 @@ function makeHarness(opts: { const engine: any = { async findOne(table: string, query: { where?: Record } = {}) { + assertEngineFindOnePredicate(table, query); if (table !== 'sys_metadata') return null; return rows.find((r) => matches(r, query.where)) ?? null; }, diff --git a/packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts b/packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts index ce95380deb..4c922f0aed 100644 --- a/packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts +++ b/packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts @@ -46,7 +46,7 @@ import { afterEach, describe, expect, it } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { resetEnvWritableMetadataTypes } from './sys-metadata-repository.js'; @@ -88,6 +88,7 @@ function makeStubEngine(opts: { const engine: any = { async findOne(table: string, query: { where?: Record } = {}) { + assertEngineFindOnePredicate(table, query); if (table !== 'sys_metadata') return null; return rows.find((r) => matches(r, query.where)) ?? null; }, diff --git a/packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts b/packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts index ee8ea711ff..b514cd9b17 100644 --- a/packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts +++ b/packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts @@ -75,7 +75,7 @@ * cannot be tested against a harness that mocks `deleteMetaItem`. */ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ErrorCode } from '@objectstack/spec/api'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -173,6 +173,7 @@ function makeSession(opts: { const engine: any = { async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesWhere(h, o.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts b/packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts index 847fea0464..ba0aecb1d2 100644 --- a/packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts +++ b/packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts @@ -145,6 +145,7 @@ import { describe, expect, it } from 'vitest'; // rows 1-3 of the inventory one measurement instead of three guesses. import { resolveThrownHttpError } from '@objectstack/types'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // Harness — the `sys_metadata`-backed kernel the #8333 batch-verb suite uses, @@ -207,6 +208,7 @@ function makeKernel(opts: { seed?: Row[] } = {}) { return Array.from(rows.values()).filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; return null; diff --git a/packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts b/packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts index beb61e67e1..9564f14dc3 100644 --- a/packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts +++ b/packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts @@ -79,7 +79,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; // Imported from `@objectstack/metadata-core`, ⛔ never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // Reason 1 is a claim about the AUTHORABLE SHAPE of a `field` body, so it is // asserted against the schema itself rather than restated in prose here. import { FieldSchema } from '@objectstack/spec/data'; @@ -164,6 +164,7 @@ function makeKernel(seed: Row[] = [], artifacts: Record = {}) { return Array.from(rows.values()).filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; return null; diff --git a/packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.ts b/packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.ts index 5539c9f9c9..3a32fd9298 100644 --- a/packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.ts +++ b/packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.ts @@ -87,6 +87,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ErrorCode } from '@objectstack/spec/api'; import { DEFAULT_METADATA_TYPE_REGISTRY, getMetadataTypeSchema } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * The set the sweep walks — derived from the same two spec symbols the @@ -112,7 +113,8 @@ function emptyRegistry(overrides: Record = {}) { /** An engine whose every read REJECTS — a metadata store the protocol cannot reach. */ function engineThatCannotBeRead(error: () => unknown) { - const reject = vi.fn(async () => { throw error(); }); + const reject = vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { + assertEngineFindOnePredicate(object, query); throw error(); }); return { registry: emptyRegistry(), find: reject, findOne: reject } as any; } @@ -231,7 +233,7 @@ describe('[#8855] the discrimination is selective, not a blanket refusal', () => }, }), find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), } as any; const p = new ObjectStackProtocolImplementation(engine); @@ -257,7 +259,7 @@ describe('[#8855] the discrimination is selective, not a blanket refusal', () => }] : [] )), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), } as any; const p = new ObjectStackProtocolImplementation(engine); @@ -275,7 +277,7 @@ describe('[#8924] a caller error the producer classified is refused loudly, neve return { registry: emptyRegistry(), find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), } as any; } @@ -339,7 +341,7 @@ describe('[#8924] a caller error the producer classified is refused loudly, neve }, }), find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), } as any; const p = new ObjectStackProtocolImplementation(engine); diff --git a/packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts b/packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts index 97b4ec4da9..f59dd0674b 100644 --- a/packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts +++ b/packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts @@ -38,7 +38,7 @@ * the signal to re-pick the fixture, not to delete the assertion. */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { PLURAL_TO_SINGULAR } from '@objectstack/spec/shared'; import { ObjectStackProtocolImplementation } from './index.js'; @@ -109,6 +109,7 @@ function makeStubEngine(opts: { historyError?: () => Error } = {}) { return (tables[table] ?? []).filter((r) => matches(r, o.where)); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); return (tables[table] ?? []).find((r) => matches(r, o.where)) ?? null; }, async insert() { return { id: 'stub' }; }, diff --git a/packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts b/packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts index 031ec986fd..2fe850c28b 100644 --- a/packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts +++ b/packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts @@ -39,7 +39,7 @@ * cannot distinguish (a diff that emitted no values at all would pass it). */ import { afterEach, describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { getMetadataTypeRedactor, registerMetadataTypeRedactor } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './index.js'; @@ -120,6 +120,7 @@ function makeStubEngine() { return (tables[table] ?? []).filter((r) => matches(r, opts.where)); }, async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); return (tables[table] ?? []).find((r) => matches(r, opts.where)) ?? null; }, async insert() { return { id: 'stub' }; }, diff --git a/packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts b/packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts index f980fd55b5..1d82ef6dec 100644 --- a/packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts +++ b/packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts @@ -27,7 +27,7 @@ * re-pick the fixture from the registry, not to delete the assertion. */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './index.js'; /** Takes `historyMetaItem`'s early return — neither flag set in the registry. */ @@ -82,6 +82,7 @@ function makeStubEngine(opts: { throwOnHistory?: boolean } = {}) { return (tables[table] ?? []).filter((r) => matches(r, o.where)); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); return (tables[table] ?? []).find((r) => matches(r, o.where)) ?? null; }, async insert() { return { id: 'stub' }; }, diff --git a/packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts b/packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts index 0c823ee32b..c9611ded54 100644 --- a/packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts +++ b/packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts @@ -94,7 +94,7 @@ import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update). From `@objectstack/metadata-core`, never `@objectstack/objectql` // — objectql depends on THIS package, so that import would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { looksLikeInternalErrorLeak } from '@objectstack/types'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -217,6 +217,7 @@ function makeKernel(opts: { return Array.from(rows.values()); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); boom('findOne'); if (table !== 'sys_metadata') return null; for (const row of rows.values()) { diff --git a/packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts b/packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts index d5b260c1d6..38cab925c8 100644 --- a/packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts +++ b/packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts @@ -12,7 +12,7 @@ // response has no per-row slot; the insert strip is schema-uniform). import { describe, it, expect, vi } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { @@ -38,7 +38,7 @@ describe('updateManyData — per-row droppedFields + context threading (#3455)', // fixture pretends to update — existence must mean one thing in a harness. // (It answered a flat `null` while nothing called it; a row that does not // exist is now correctly refused before `engine.update`.) - const findOne = vi.fn(async (_object: string, options?: any) => ({ id: options?.where?.id, title: 'stored' })); + const findOne = vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return ({ id: options?.where?.id, title: 'stored' }); }); const engine = { registry: { getObject: () => SCHEMA }, update, findOne }; const p = new ObjectStackProtocolImplementation(engine as any); @@ -187,7 +187,7 @@ describe('batchData — per-row droppedFields + context threading (#3455)', () = }); // [#5088] See the updateManyData fixture above: `findOne` is now the // existence probe in front of the write, so it must answer for `rec-1`. - const findOne = vi.fn(async (_object: string, options?: any) => ({ id: options?.where?.id })); + const findOne = vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return ({ id: options?.where?.id }); }); const engine = { registry: { getObject: () => SCHEMA }, update, insert: vi.fn(), findOne }; const p = new ObjectStackProtocolImplementation(engine as any); diff --git a/packages/metadata-protocol/src/protocol.dropped-fields.test.ts b/packages/metadata-protocol/src/protocol.dropped-fields.test.ts index 1c8422a01e..0e451917c8 100644 --- a/packages/metadata-protocol/src/protocol.dropped-fields.test.ts +++ b/packages/metadata-protocol/src/protocol.dropped-fields.test.ts @@ -14,7 +14,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ObjectStackProtocolImplementation } from './protocol.js'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; const SCHEMA = { name: 'approval_case', @@ -38,7 +38,7 @@ describe('updateData — forwards engine write strips as droppedFields (#3431)', // PATCH of an id that names no row is now a 404 rather than a 200 with a // null record. These fixtures are about the strip channel, not about // missing records, so they describe an engine that has the row. - findOne: vi.fn(async () => ({ id: 'rec-1' })), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return ({ id: 'rec-1' }); }), }; const p = new ObjectStackProtocolImplementation(engine as any); const res: any = await p.updateData({ @@ -63,7 +63,7 @@ describe('updateData — forwards engine write strips as droppedFields (#3431)', options?.onFieldsDropped?.({ object, fields: ['approval_status'], reason: 'readonly' }); return { id: 'rec-1' }; }), - findOne: vi.fn(async () => ({ id: 'rec-1' })), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return ({ id: 'rec-1' }); }), }; const p = new ObjectStackProtocolImplementation(engine as any); const res: any = await p.updateData({ object: 'approval_case', id: 'rec-1', data: {} }); @@ -80,7 +80,7 @@ describe('updateData — forwards engine write strips as droppedFields (#3431)', assertEngineUpdateDispatch(data, options); return { id: 'rec-1', ...data }; }), - findOne: vi.fn(async () => ({ id: 'rec-1' })), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return ({ id: 'rec-1' }); }), }; const p = new ObjectStackProtocolImplementation(engine as any); const res: any = await p.updateData({ object: 'approval_case', id: 'rec-1', data: { title: 'B' } }); diff --git a/packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts b/packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts index c4221c14bd..c499d35dad 100644 --- a/packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts +++ b/packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts @@ -83,7 +83,7 @@ import { describe, expect, it } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -104,6 +104,7 @@ function makeStubEngine(artifacts: Array<{ type: string; name: string }> = []) { `${w.type}|${w.name}|${w.organization_id ?? '__env__'}|${w.state ?? 'active'}`; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts b/packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts index 23e1a4b037..2aa35014e0 100644 --- a/packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts +++ b/packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts @@ -32,7 +32,7 @@ import { describe, expect, it } from 'vitest'; import { ViewMetadataSchema } from '@objectstack/spec/ui'; import { assertEngineDeleteDispatch, - assertEngineUpdateDispatch, + assertEngineUpdateDispatch, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation, graftFoldedFormSections } from './protocol.js'; @@ -72,6 +72,7 @@ function makeProtocol() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts b/packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts index 237349cd4d..3b5cb2b1d8 100644 --- a/packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts +++ b/packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts @@ -116,6 +116,7 @@ import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { PLURAL_TO_SINGULAR, canonicalMetaUrlType } from '@objectstack/spec/shared'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; interface StoredRow { id: string; @@ -142,7 +143,8 @@ function makeUnscopedProtocol(rows: StoredRow[]) { return (r as unknown as Record)[k] === v; })); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, // ⛔ No `update` / `delete` / `insert` on this double, deliberately. // Both paths under test are READ-then-register — `loadMetaFromDb` // issues `find` only, and the direct helper calls touch nothing but diff --git a/packages/metadata-protocol/src/protocol.i18n-bundle-list-merge.test.ts b/packages/metadata-protocol/src/protocol.i18n-bundle-list-merge.test.ts index 62614bca8f..8ff5830e78 100644 --- a/packages/metadata-protocol/src/protocol.i18n-bundle-list-merge.test.ts +++ b/packages/metadata-protocol/src/protocol.i18n-bundle-list-merge.test.ts @@ -50,7 +50,7 @@ import { describe, expect, it } from 'vitest'; // [#7774] The identity table's home is `@objectstack/metadata-core`, not the // `SchemaRegistry` that first needed it: `@objectstack/objectql` depends on // THIS package, so importing the registry's copy would close a cycle. -import { ITEM_KEY_DISCRIMINATORS } from '@objectstack/metadata-core'; +import { ITEM_KEY_DISCRIMINATORS, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const PKG = 'com.acme.crm'; @@ -124,7 +124,8 @@ function makeEngine(opts: { items?: unknown[]; rows?: Row[] } = {}) { return true; }); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, } as any, }; } diff --git a/packages/metadata-protocol/src/protocol.injected-system-columns.test.ts b/packages/metadata-protocol/src/protocol.injected-system-columns.test.ts index 0247ddfbef..d3196fffc9 100644 --- a/packages/metadata-protocol/src/protocol.injected-system-columns.test.ts +++ b/packages/metadata-protocol/src/protocol.injected-system-columns.test.ts @@ -45,7 +45,7 @@ import { describe, expect, it } from 'vitest'; // below cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core` // and not `@objectstack/objectql`: objectql DEPENDS ON this package, so that // import would close a cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { AUDIT_PROVENANCE_FIELDS } from '@objectstack/spec/data'; import { ObjectStackProtocolImplementation } from './index.js'; @@ -82,6 +82,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts b/packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts index 93e4845aaf..db63dec3aa 100644 --- a/packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts +++ b/packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts @@ -82,7 +82,7 @@ * needs no rebuild, and its RED result rules out a stale-artifact false green. */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -101,7 +101,8 @@ function makeProtocol() { const rows = new Map(); let nextId = 0; const engine: any = { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return []; }, async insert(table: string, data: Record) { if (table === 'sys_metadata_audit') return { id: 'audit_skip' }; diff --git a/packages/metadata-protocol/src/protocol.item-name-grammar.test.ts b/packages/metadata-protocol/src/protocol.item-name-grammar.test.ts index ddeaff9be6..accddc9480 100644 --- a/packages/metadata-protocol/src/protocol.item-name-grammar.test.ts +++ b/packages/metadata-protocol/src/protocol.item-name-grammar.test.ts @@ -41,7 +41,7 @@ import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // below cannot accept a call ObjectQL itself refuses. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -76,6 +76,7 @@ function makeProtocol(seedRows: Array> = []) { const deletes: Array | undefined> = []; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts b/packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts index 45ec7cd641..8a0447579b 100644 --- a/packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts +++ b/packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts @@ -82,7 +82,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; // #5480 update), so the fake engine below cannot accept a call ObjectQL // refuses. Imported from `@objectstack/metadata-core` and not from // `@objectstack/objectql`, which depends on this package. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { SysMetadataRepository, resetEnvWritableMetadataTypes } from './sys-metadata-repository.js'; @@ -174,6 +174,7 @@ function makeSession(opts: { const engine: any = { async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesWhere(h, o.where)) ?? null; } @@ -590,6 +591,7 @@ describe('#6960 — SysMetadataRepository: the delete verb diverges, the put ver const historyRows: Array> = []; const engine: any = { async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesWhere(h, o.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts b/packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts index 51dc9eb93a..fdcdcbf551 100644 --- a/packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts +++ b/packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts @@ -92,7 +92,7 @@ import { describe, expect, it, vi } from 'vitest'; // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -186,6 +186,7 @@ function makeStubEngine(opts: { failAudit?: boolean } = {}): Harness { const engine: any = { async findOne(table: string, opts2: { where: Record }) { + assertEngineFindOnePredicate(table, opts2); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts2.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts b/packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts index 8e058b4cde..365deb2483 100644 --- a/packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts @@ -76,6 +76,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * An overlay row exactly as `SysMetadataRepository` stores it: keyed by the @@ -127,6 +128,7 @@ function harness(opts: { getObjectOwner: () => undefined, }, findOne: vi.fn(async (object: string, query: any) => { + assertEngineFindOnePredicate(object, query); if (object !== 'sys_metadata') return null; const where = (query?.where ?? {}) as Record; overlayQueries.push(where); diff --git a/packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts b/packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts index 10b41bc810..3becb9c4db 100644 --- a/packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts +++ b/packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts @@ -88,7 +88,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ErrorCode } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** A registry holding only what the test explicitly puts in it. */ function registry(items: Record = {}) { @@ -161,6 +161,7 @@ function engineWithTransientLockReadFault(opts: { const engine: any = { registry: registry(opts.registryItems ?? {}), findOne: vi.fn(async (object: string, query: any) => { + assertEngineFindOnePredicate(object, query); if (object !== 'sys_metadata') { calls.push(`findOne:${object}`); return null; diff --git a/packages/metadata-protocol/src/protocol.many-data-atomic.test.ts b/packages/metadata-protocol/src/protocol.many-data-atomic.test.ts index 489eecbe9c..55a83103bf 100644 --- a/packages/metadata-protocol/src/protocol.many-data-atomic.test.ts +++ b/packages/metadata-protocol/src/protocol.many-data-atomic.test.ts @@ -21,7 +21,7 @@ // test reads the store back afterwards. import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { name: 'invoice', fields: { title: { name: 'title', type: 'text' } } }; @@ -70,7 +70,7 @@ function makeStoreEngine(opts: { driverCanTransact?: boolean; hasTransaction?: b registry: { getObject: (n: string) => (n === 'invoice' ? SCHEMA : undefined) }, update, delete: del, - findOne: vi.fn(async (_object: string, options?: any) => rows.get(options?.where?.id)), + findOne: vi.fn(async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return rows.get(options?.where?.id); }), getDefaultDriverName: () => 'default', getDriverByName: () => (driverCanTransact ? { beginTransaction: async () => handle } : {}), }; diff --git a/packages/metadata-protocol/src/protocol.meta-list-runtime-baseline-dedup.test.ts b/packages/metadata-protocol/src/protocol.meta-list-runtime-baseline-dedup.test.ts index 4c6a33927e..ddd6e53dd9 100644 --- a/packages/metadata-protocol/src/protocol.meta-list-runtime-baseline-dedup.test.ts +++ b/packages/metadata-protocol/src/protocol.meta-list-runtime-baseline-dedup.test.ts @@ -60,6 +60,7 @@ */ import { describe, expect, it } from 'vitest'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; const PKG = 'com.acme.showcase'; const OTHER_PKG = 'com.acme.partner'; @@ -118,7 +119,8 @@ function makeEngine(opts: { items?: any[]; rows?: Row[] } = {}) { return true; }); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, } as any; } diff --git a/packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts b/packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts index a087ed74ad..2b47d203ae 100644 --- a/packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts +++ b/packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts @@ -57,7 +57,7 @@ import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // below cannot accept a call ObjectQL itself refuses. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; /** @@ -76,7 +76,8 @@ function makeProtocol() { const rows = new Map>(); let nextId = 0; const engine: any = { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return []; }, async insert(table: string, data: Record) { if (table !== 'sys_metadata') return { id: 'side_effect_skip' }; diff --git a/packages/metadata-protocol/src/protocol.metadata-redaction.test.ts b/packages/metadata-protocol/src/protocol.metadata-redaction.test.ts index 37be24c50e..8ae065a04c 100644 --- a/packages/metadata-protocol/src/protocol.metadata-redaction.test.ts +++ b/packages/metadata-protocol/src/protocol.metadata-redaction.test.ts @@ -37,7 +37,7 @@ * destroy the #8081 item-3 operator inventory. */ import { afterEach, describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, hashSpec, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { getMetadataTypeRedactor, getMetadataTypeSchema, @@ -84,6 +84,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts b/packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts index 31c2a3520f..b8f59dcebf 100644 --- a/packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts +++ b/packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts @@ -66,6 +66,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ErrorCode } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** A registry with nothing in it — the overlay read is the only source. */ function emptyRegistry(items: Record = {}) { @@ -84,7 +85,8 @@ function emptyRegistry(items: Record = {}) { * store the protocol cannot reach. */ function engineThatCannotBeRead(error: () => unknown, registryItems: Record = {}) { - const reject = vi.fn(async () => { throw error(); }); + const reject = vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { + assertEngineFindOnePredicate(object, query); throw error(); }); return { registry: emptyRegistry(registryItems), find: reject, @@ -97,7 +99,7 @@ function engineWithRows(rows: any[] = [], registryItems: Record = { return { registry: emptyRegistry(registryItems), find: vi.fn(async () => rows), - findOne: vi.fn(async () => rows[0] ?? null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return rows[0] ?? null; }), } as any; } diff --git a/packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts b/packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts index d57610d1e8..811116eb43 100644 --- a/packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts +++ b/packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts @@ -53,7 +53,7 @@ import { describe, expect, it } from 'vitest'; // cannot accept a call the real ObjectQL engine refuses (#4550 delete / #5480 // update). Imported from `@objectstack/metadata-core` rather than // `@objectstack/objectql`, which depends on this package. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { formatStoredMigrationReport, storedMigrationClean } from './stored-migration.js'; @@ -122,6 +122,7 @@ function makeStubEngine(seedRows: SeedRow[]) { return rowsOf(t).filter((r) => matches(r, opts?.where ?? {})); }, async findOne(t: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(t, opts); return rowsOf(t).find((r) => matches(r, opts?.where ?? {})) ?? null; }, async insert(t: string, row: Record) { diff --git a/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts b/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts index d1b295249e..eaaaab3991 100644 --- a/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts +++ b/packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts @@ -134,7 +134,7 @@ import { PLURAL_TO_SINGULAR, canonicalMetaUrlType } from '@objectstack/spec/shar // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -229,6 +229,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_commit') return commit; if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; diff --git a/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts b/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts index 2187045ebf..1ea4a00883 100644 --- a/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts +++ b/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts @@ -67,7 +67,7 @@ import { describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update). From `@objectstack/metadata-core`, never `@objectstack/objectql` // — objectql depends on THIS package, so that import would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -117,7 +117,8 @@ function makeEngine( async find(_table: string, q: { where: Record }) { return rows.filter((r) => matchesWhere(r, q.where)); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert() { return { id: 'x' }; }, async update(_t: string, data: Record, o?: Record) { assertEngineUpdateDispatch(data, o); diff --git a/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts b/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts index 418667eb56..8855a901ff 100644 --- a/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts +++ b/packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts @@ -60,7 +60,7 @@ import { describe, expect, it, vi } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions (#4550 delete / // #5480 update). From `@objectstack/metadata-core`, never `@objectstack/objectql` // — objectql depends on THIS package, so that import would close a cycle. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -109,7 +109,8 @@ function makeEngine(rows: Row[], opts: { dropPredicates?: boolean } = {}) { if (opts.dropPredicates) return rows.filter((r) => r.state === q.where.state); return rows.filter((r) => matchesWhere(r, q.where)); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert() { return { id: 'x' }; }, async update(_t: string, data: Record, o?: Record) { assertEngineUpdateDispatch(data, o); diff --git a/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts b/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts index c8c9b33fe5..1c9f1d16e0 100644 --- a/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts +++ b/packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts @@ -73,7 +73,7 @@ import { afterEach, describe, expect, it } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -141,6 +141,7 @@ function makeStubEngine(artifacts: Array<{ type: string; name: string }> = []) { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => { const w = opts.where; diff --git a/packages/metadata-protocol/src/protocol.package-closure-gate.test.ts b/packages/metadata-protocol/src/protocol.package-closure-gate.test.ts index c219ce123b..0ef81d25c6 100644 --- a/packages/metadata-protocol/src/protocol.package-closure-gate.test.ts +++ b/packages/metadata-protocol/src/protocol.package-closure-gate.test.ts @@ -32,7 +32,7 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so the fake engine cannot // accept a call ObjectQL refuses. From `@objectstack/metadata-core`, never from // `@objectstack/objectql` — that import would close a cycle turbo rejects. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const OWN = 'com.acme.crm'; @@ -105,6 +105,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts b/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts index 810938c1ed..24aa5020dd 100644 --- a/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts +++ b/packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts @@ -98,7 +98,7 @@ import { describe, expect, it, vi } from 'vitest'; // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -219,6 +219,7 @@ function makeStubEngine(opts: { failAudit?: boolean } = {}): Harness { const engine: any = { async findOne(table: string, opts2: { where: Record }) { + assertEngineFindOnePredicate(table, opts2); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts2.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts b/packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts index 2d001a2cac..4d58b887f6 100644 --- a/packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts +++ b/packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts @@ -53,7 +53,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // Imported from `@objectstack/metadata-core` and not from `@objectstack/objectql`: // objectql DEPENDS ON this package, so that import would close a dependency // cycle turbo rejects outright (#5619). -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { PLATFORM_SCHEDULE_CREATE_RECORD_ORG_MISSING, @@ -344,6 +344,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts b/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts index bb2cad88ad..d9f2f2650d 100644 --- a/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts @@ -59,7 +59,7 @@ import { describe, it, expect } from 'vitest'; // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, not // `@objectstack/objectql` — objectql depends on THIS package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -141,6 +141,7 @@ function makeStubEngine() { const engine = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata' && opts?.where?.state === 'active') { activeReads.push(opts.where); if (captureFailure !== null) { diff --git a/packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts b/packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts index 3a2c4d1025..53633b5341 100644 --- a/packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts @@ -28,7 +28,7 @@ */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import type { MetaItemPublishedEvent } from './protocol.js'; @@ -112,6 +112,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts b/packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts index 75d13c5250..de8005098f 100644 --- a/packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts @@ -27,7 +27,7 @@ */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import type { MetaItemPublishedEvent } from './protocol.js'; @@ -111,6 +111,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts b/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts index 84eb40257e..67723c395b 100644 --- a/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts @@ -60,7 +60,7 @@ import { describe, expect, it, vi } from 'vitest'; // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -131,6 +131,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => { const w = opts.where; diff --git a/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts b/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts index b6f1f3f806..f4cade4dc2 100644 --- a/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts +++ b/packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts @@ -80,7 +80,7 @@ import { describe, expect, it } from 'vitest'; // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -174,6 +174,7 @@ function makeStubEngine(): Harness { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.read-decorations.test.ts b/packages/metadata-protocol/src/protocol.read-decorations.test.ts index 3a3c97d6c9..7fac82a6f4 100644 --- a/packages/metadata-protocol/src/protocol.read-decorations.test.ts +++ b/packages/metadata-protocol/src/protocol.read-decorations.test.ts @@ -29,7 +29,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { FlowSchema } from '@objectstack/spec/automation'; import { METADATA_READ_DECORATIONS, getMetadataTypeSchema } from '@objectstack/spec/kernel'; import { @@ -71,6 +71,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts b/packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts index 7198ab2753..76a1d800e9 100644 --- a/packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts +++ b/packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts @@ -41,6 +41,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ErrorCode } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; interface FixtureObject { name: string; @@ -113,7 +114,7 @@ describe('[#8896] searchAll — an object that could not be READ is not an objec if (object === 'lead') throw error; return [{ id: 'a1', name: 'Acme' }]; }), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; return { engine, readCalls }; } @@ -124,7 +125,7 @@ describe('[#8896] searchAll — an object that could not be READ is not an objec find: vi.fn(async (object: string) => ( object === 'acct' ? [{ id: 'a1', name: 'Acme' }] : [{ id: 'l1', name: 'Acme Lead' }] )), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; const protocol = new ObjectStackProtocolImplementation(engine as never); @@ -235,7 +236,7 @@ describe('[#11754] searchAll — a registry that cannot ENUMERATE is not a regis listItems: () => [], }, find, - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; const protocol = new ObjectStackProtocolImplementation(engine as never); @@ -271,7 +272,7 @@ describe('[#11754] searchAll — a registry that cannot ENUMERATE is not a regis const engine = { registry: { ...fixtureRegistry([]), getAllObjects: vi.fn(() => []) }, find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; const protocol = new ObjectStackProtocolImplementation(engine as never); @@ -293,7 +294,7 @@ describe('[#11754] searchAll — a registry that cannot ENUMERATE is not a regis listItems: () => [], }, find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; const protocol = new ObjectStackProtocolImplementation(engine as never); @@ -337,7 +338,7 @@ describe('[#8896] findReferencesToMeta — a source type that could not be READ if (failingType !== null && type === failingType) throw error; return []; }), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; return { engine, typeReads }; } diff --git a/packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts b/packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts index ecb230a52b..c4c81832df 100644 --- a/packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts +++ b/packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts @@ -62,6 +62,7 @@ import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { META_URL_TO_SINGULAR, PLURAL_TO_SINGULAR, metaUrlSpellingRefusal } from '@objectstack/spec/shared'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** Manifest-ABSENT and `allowOrgOverride: true` — history reads REAL rows for it. */ const OVERLAY_ABSENT_TYPE = 'translation'; @@ -109,6 +110,7 @@ function makeStubEngine() { return (tables[table] ?? []).filter((r) => matches(r, where)); }, async findOne(table: string, o: { where?: Record } = {}) { + assertEngineFindOnePredicate(table, o); reads.push({ table, where: o.where ?? {} }); return (tables[table] ?? []).find((r) => matches(r, o.where ?? {})) ?? null; }, diff --git a/packages/metadata-protocol/src/protocol.record-not-found.test.ts b/packages/metadata-protocol/src/protocol.record-not-found.test.ts index efd4096c6f..9acdebb5c4 100644 --- a/packages/metadata-protocol/src/protocol.record-not-found.test.ts +++ b/packages/metadata-protocol/src/protocol.record-not-found.test.ts @@ -21,7 +21,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { name: 'task', fields: { title: { name: 'title', type: 'text' } } }; @@ -33,7 +33,7 @@ const SCHEMA = { name: 'task', fields: { title: { name: 'title', type: 'text' } */ function makeProtocol(rows: Record = {}) { const store = new Map(Object.entries(rows)); - const findOne = vi.fn(async (_object: string, opts: any) => store.get(String(opts?.where?.id)) ?? null); + const findOne = vi.fn(async (_object: string, opts: any) => { assertEngineFindOnePredicate(_object, opts); return store.get(String(opts?.where?.id)) ?? null; }); const update = vi.fn(async (_object: string, data: any, opts: any) => { assertEngineUpdateDispatch(data, opts); const id = String(opts?.where?.id); diff --git a/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts b/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts index c1fc275bd8..0de0b947d7 100644 --- a/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts +++ b/packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts @@ -52,7 +52,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; // cannot accept a call ObjectQL refuses. From `@objectstack/metadata-core`, not // `@objectstack/objectql` — objectql depends on THIS package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -126,6 +126,7 @@ function makeStubEngine() { const engine = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table !== 'sys_metadata') { return (sideTables[table] ?? []).find((r) => matchesWhere(r, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/protocol.reference-derivation.test.ts b/packages/metadata-protocol/src/protocol.reference-derivation.test.ts index f818c112ad..22eb8b0b71 100644 --- a/packages/metadata-protocol/src/protocol.reference-derivation.test.ts +++ b/packages/metadata-protocol/src/protocol.reference-derivation.test.ts @@ -16,6 +16,7 @@ import { describe, expect, it } from 'vitest'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * Registry-backed stub. Reference scanning reads through `getMetaItems`, which @@ -25,7 +26,8 @@ import { ObjectStackProtocolImplementation } from './protocol.js'; function protocolWith(items: Record>>) { const engine: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async count() { return 0; }, registry: { listItems: (type: string) => items[type] ?? [], diff --git a/packages/metadata-protocol/src/protocol.reference-target-unanswerable.test.ts b/packages/metadata-protocol/src/protocol.reference-target-unanswerable.test.ts index 8e4f633a6e..f85b646a8f 100644 --- a/packages/metadata-protocol/src/protocol.reference-target-unanswerable.test.ts +++ b/packages/metadata-protocol/src/protocol.reference-target-unanswerable.test.ts @@ -39,12 +39,14 @@ import { describe, expect, it } from 'vitest'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { REFERENCE_SITES } from './reference-sites.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** Same registry-backed stub the sibling derivation suite uses. */ function protocolWith(items: Record>>) { const engine: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async count() { return 0; }, registry: { listItems: (type: string) => items[type] ?? [], diff --git a/packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts b/packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts index 3dc3bac68b..0457d14984 100644 --- a/packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts +++ b/packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts @@ -109,7 +109,7 @@ import { PLURAL_TO_SINGULAR, canonicalMetaUrlType } from '@objectstack/spec/shar // `@objectstack/metadata-core` and NOT from `@objectstack/objectql`: objectql // depends on this package, so that import would close a dependency cycle turbo // rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -202,6 +202,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_commit') return commit; if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; diff --git a/packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts b/packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts index 83256b758c..2ec5ee7b8c 100644 --- a/packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts +++ b/packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts @@ -30,7 +30,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // [#4716] The advisory-tier rule the Q2 fence test proves its body WOULD trip // — imported from the full barrel deliberately: this is a TEST, not the gate // (the gate itself may only reach the registry through `@objectstack/lint/runtime`, @@ -99,6 +99,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts b/packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts index d291aba810..8dd0454287 100644 --- a/packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts +++ b/packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts @@ -28,7 +28,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { hashSpec, assertEngineDeleteDispatch, - assertEngineUpdateDispatch, + assertEngineUpdateDispatch, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -100,6 +100,7 @@ function makeStubEngine() { }; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts b/packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts index cab1e13243..fb0c4bf543 100644 --- a/packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts +++ b/packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts @@ -51,6 +51,7 @@ import { describe, it, expect, vi } from 'vitest'; import { ObjectStackProtocolImplementation, clientFacingFailureText } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * A protocol over an engine whose every verb is a tripwire: this guard is the @@ -58,7 +59,7 @@ import { ObjectStackProtocolImplementation, clientFacingFailureText } from './pr * all would mean the check had moved behind something with side effects. */ function makeProtocol() { - const findOne = vi.fn(async () => null); + const findOne = vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }); const find = vi.fn(async () => [] as unknown[]); const engine = { registry: { getObject: () => undefined }, diff --git a/packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts b/packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts index fbbeadfe6b..752b7fce95 100644 --- a/packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts +++ b/packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts @@ -46,7 +46,7 @@ import { afterEach, describe, expect, it } from 'vitest'; // #5480 update). Imported from `@objectstack/metadata-core`, never from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { resetEnvWritableMetadataTypes } from './sys-metadata-repository.js'; @@ -68,6 +68,7 @@ function makeStubEngine(artifacts: Array<{ type: string; name: string }> = []) { `${w.type}|${w.name}|${w.organization_id ?? '__env__'}|${w.state ?? 'active'}`; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.save-union-issues.test.ts b/packages/metadata-protocol/src/protocol.save-union-issues.test.ts index ef62a454fc..ea2d86a544 100644 --- a/packages/metadata-protocol/src/protocol.save-union-issues.test.ts +++ b/packages/metadata-protocol/src/protocol.save-union-issues.test.ts @@ -30,7 +30,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { getMetadataTypeSchema } from '@objectstack/spec/kernel'; import { ObjectStackProtocolImplementation, zodIssuesToMetadataIssues } from './protocol.js'; @@ -51,7 +51,8 @@ function makeProtocol() { const rows = new Map(); let nextId = 0; const engine: any = { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return []; }, async insert(table: string, data: Record) { if (table === 'sys_metadata_audit') return { id: 'audit_skip' }; diff --git a/packages/metadata-protocol/src/protocol.stored-conversions.test.ts b/packages/metadata-protocol/src/protocol.stored-conversions.test.ts index 76977599e4..5140a4eb92 100644 --- a/packages/metadata-protocol/src/protocol.stored-conversions.test.ts +++ b/packages/metadata-protocol/src/protocol.stored-conversions.test.ts @@ -23,7 +23,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -58,6 +58,7 @@ function makeStubEngine(seedRows: Array & { type: string; name: str const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return rows.find((r) => matches(r, opts.where)) ?? null; }, async find(_t: string, opts: { where: Record }) { diff --git a/packages/metadata-protocol/src/protocol.stored-migration.test.ts b/packages/metadata-protocol/src/protocol.stored-migration.test.ts index 7dd9d2a2a7..616ea5dbc6 100644 --- a/packages/metadata-protocol/src/protocol.stored-migration.test.ts +++ b/packages/metadata-protocol/src/protocol.stored-migration.test.ts @@ -25,7 +25,7 @@ import { describe, expect, it } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { formatStoredMigrationReport, storedMigrationClean } from './stored-migration.js'; @@ -84,6 +84,7 @@ function makeStubEngine( return rowsOf(t).filter((r) => matches(r, opts?.where ?? {})); }, async findOne(t: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(t, opts); return rowsOf(t).find((r) => matches(r, opts?.where ?? {})) ?? null; }, async insert(t: string, row: Record) { diff --git a/packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts b/packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts index 1e9d8672ed..17638f5445 100644 --- a/packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts +++ b/packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts @@ -42,7 +42,7 @@ import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // below cannot accept a call ObjectQL itself refuses. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; function matches(r: Record, where: Record): boolean { @@ -90,6 +90,7 @@ function makeStubEngine(seedRows: Array>) { return rowsOf(t).filter((r) => matches(r, opts?.where ?? {})); }, async findOne(t: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(t, opts); return rowsOf(t).find((r) => matches(r, opts?.where ?? {})) ?? null; }, async insert(t: string, row: Record) { diff --git a/packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts b/packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts index 3702dfeea4..4bc49204f1 100644 --- a/packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts +++ b/packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts @@ -54,7 +54,7 @@ import { describe, expect, it } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so the fake engine // below cannot accept a call ObjectQL itself refuses. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; interface Row { @@ -93,6 +93,7 @@ function makeProtocol(seedRows: Array> = []) { const deletes: Array | undefined> = []; const engine: any = { async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); for (const row of rows.values()) { if (opts.where.type !== undefined && row.type !== opts.where.type) continue; if (opts.where.name !== undefined && row.name !== opts.where.name) continue; diff --git a/packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts b/packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts index e4b208059d..b5195cea53 100644 --- a/packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts +++ b/packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts @@ -58,7 +58,7 @@ import { describe, it, expect, vi } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, - resolveEngineUpdateDispatch, + resolveEngineUpdateDispatch, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; @@ -92,6 +92,7 @@ type Row = Record & { id: string }; function makeProtocol(rows: Row[]) { const store = new Map(rows.map((r) => [r.id, { ...r }])); const findOne = vi.fn(async (_object: string, opts: any) => { + assertEngineFindOnePredicate(_object, opts); const row = store.get(String(opts?.where?.id)); return row ? { ...row } : null; }); diff --git a/packages/metadata-protocol/src/protocol.upsert-existence.test.ts b/packages/metadata-protocol/src/protocol.upsert-existence.test.ts index bd0209a2b8..6dd37b77ad 100644 --- a/packages/metadata-protocol/src/protocol.upsert-existence.test.ts +++ b/packages/metadata-protocol/src/protocol.upsert-existence.test.ts @@ -26,7 +26,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; const SCHEMA = { @@ -55,6 +55,7 @@ function makeRlsEngine() { context?.isSystem === true || row.owner_id === context?.userId; const findOne = vi.fn(async (_object: string, options?: any) => { + assertEngineFindOnePredicate(_object, options); const row = rows.get(options?.where?.id) ?? null; if (!row) return null; return visible(row, options?.context) ? row : null; diff --git a/packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts b/packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts index 66b3f7d6fc..000b129eb6 100644 --- a/packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts +++ b/packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts @@ -45,7 +45,7 @@ // enumeration is shown to pick it up, and the scan is shown to catch its leak. import { describe, it, expect } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { collectInternalWriteResponseFields, @@ -106,7 +106,7 @@ function makeSentinelEngine() { assertEngineDeleteDispatch(options); return { deleted: 1 }; }, - findOne: async (_object: string, options?: any) => storedRow(options?.where?.id ?? 'row-1'), + findOne: async (_object: string, options?: any) => { assertEngineFindOnePredicate(_object, options); return storedRow(options?.where?.id ?? 'row-1'); }, find: async (_object: string, _options?: any) => [storedRow('row-1')], count: async () => 1, validate: async () => ({ valid: true, issues: [] }), diff --git a/packages/metadata-protocol/src/search-clone-schema-conformance.test.ts b/packages/metadata-protocol/src/search-clone-schema-conformance.test.ts index 0d3262496d..0833c66ac0 100644 --- a/packages/metadata-protocol/src/search-clone-schema-conformance.test.ts +++ b/packages/metadata-protocol/src/search-clone-schema-conformance.test.ts @@ -30,6 +30,7 @@ import { describe, it, expect, vi } from 'vitest'; import { CloneDataResponseSchema, SearchAllHitSchema, SearchAllResponseSchema } from '@objectstack/spec/api'; import { ObjectStackProtocolImplementation } from './protocol.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** What a caller actually holds after the REST relay: the JSON body. */ function overTheWire(value: T): T { @@ -144,7 +145,7 @@ function makeCloneProtocol() { const inserts: Array<{ object: string; data: Record }> = []; const engine = { registry: { getObject: (n: string) => (n === 'customer' ? customer : undefined) }, - findOne: vi.fn(async () => ({ ...source })), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return ({ ...source }); }), insert: vi.fn(async (object: string, data: Record) => { inserts.push({ object, data }); return { id: 'cus_2', ...data, created_at: '2026-08-25T00:00:00Z' }; diff --git a/packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts b/packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts index 0731bd00f7..c4bece344d 100644 --- a/packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts +++ b/packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Composite externalId (framework#3434). @@ -43,6 +43,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts b/packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts index a0abb67c92..5acf8f8bce 100644 --- a/packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts +++ b/packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * #5127 / #11674 — pass 2 RESOLVES the target; does it have a record to write @@ -52,6 +52,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts b/packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts index 3bf39259e0..24406d1d43 100644 --- a/packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts +++ b/packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * framework#2805: a pass-2 (deferred) reference back-fill that FAILS must be @@ -37,6 +37,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-driver-text.test.ts b/packages/metadata-protocol/src/seed-loader-driver-text.test.ts index 79a9201c89..a2dae2ad39 100644 --- a/packages/metadata-protocol/src/seed-loader-driver-text.test.ts +++ b/packages/metadata-protocol/src/seed-loader-driver-text.test.ts @@ -79,7 +79,7 @@ import { describe, expect, it, vi } from 'vitest'; import { validationFailureDetails } from '@objectstack/types'; import { SeedLoaderService } from './seed-loader.js'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // The physical conditions @@ -145,7 +145,7 @@ function createMetadata(): IMetadataService { function failingEngine(thrown: () => unknown): IDataEngine { return { find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(async () => { throw thrown(); }), update: vi.fn(async (_o: string, data: any, options?: any) => { assertEngineUpdateDispatch(data, options); @@ -221,6 +221,7 @@ describe('[#8442] raw driver text is withheld from the seed `errors[].message`', : rows; }), findOne: vi.fn(async (o: string, q?: any) => { + assertEngineFindOnePredicate(o, q); const rows = await (engine.find as any)(o, q); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts b/packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts index 5b9d7fa951..616bf8111f 100644 --- a/packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts +++ b/packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Reference-graph fallback to the ENGINE schema registry. @@ -49,6 +49,7 @@ function createFaithfulEngine(schemas: Record) { return records; }), findOne: vi.fn(async (objectName: string, query?: any) => { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-env-scope.test.ts b/packages/metadata-protocol/src/seed-loader-env-scope.test.ts index dc9ad27fac..d2f879a5d6 100644 --- a/packages/metadata-protocol/src/seed-loader-env-scope.test.ts +++ b/packages/metadata-protocol/src/seed-loader-env-scope.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { SeedLoaderService } from './seed-loader.js'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * `Seed.env` is ENFORCED, not merely authorable (framework#4704). @@ -50,7 +50,7 @@ function createEngine() { if (typeof query?.limit === 'number') records = records.slice(0, query.limit); return records; }), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(async (objectName: string, data: any) => { if (!store[objectName]) store[objectName] = []; if (Array.isArray(data)) { diff --git a/packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts b/packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts index f051d08589..9ba072e876 100644 --- a/packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts +++ b/packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts @@ -42,7 +42,7 @@ import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts' // refuses. From `@objectstack/metadata-core`, not `@objectstack/objectql` — // objectql depends on THIS package, so that import would close a dependency // cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // `.js` extension deliberately, unlike the older sibling seed-loader tests: // `moduleResolution: nodenext` requires it, and an extensionless specifier is // exactly the TS2835 that makes up part of this package's frozen TEST_DEBT @@ -92,6 +92,7 @@ function createEngine() { return records; }), findOne: vi.fn(async (objectName: string, query?: Record) => { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as unknown as (o: string, q: unknown) => Promise)( objectName, { ...query, limit: 1 }, ); diff --git a/packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts b/packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts index 997fec59fe..41454d69f0 100644 --- a/packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts +++ b/packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Multi-value reference resolution (`Field.lookup(..., { multiple: true })`). @@ -44,6 +44,7 @@ function createEngine(schemas: Record) { return records.map((r) => ({ ...r })); }), findOne: vi.fn(async (objectName: string, query?: any) => { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-name-probe.test.ts b/packages/metadata-protocol/src/seed-loader-name-probe.test.ts index 2fbf33c9cd..f716f97ecd 100644 --- a/packages/metadata-protocol/src/seed-loader-name-probe.test.ts +++ b/packages/metadata-protocol/src/seed-loader-name-probe.test.ts @@ -7,7 +7,7 @@ import { describe, it, expect, vi } from 'vitest'; // rule and sit in this package's frozen debt; a NEW file must not add to it. import { SeedLoaderService } from './seed-loader.js'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * [#9071] The seed loader's external-key lookup chain must ASK the metadata @@ -105,6 +105,7 @@ function createColumnStrictEngine(columns: Record) { return records; }), findOne: vi.fn(async (objectName: string, query?: any) => { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts b/packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts index 23224800e4..76db8fa389 100644 --- a/packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts +++ b/packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader.js'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Declared pointer-pair resolution — #11339 (ADR-0052 §5, the ActivityPointer @@ -55,6 +55,7 @@ function createFaithfulEngine(schemas: Record) { return records; }), findOne: vi.fn(async (objectName: string, query?: any) => { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-replay.test.ts b/packages/metadata-protocol/src/seed-loader-replay.test.ts index 8e84b7caca..b8e1f1746c 100644 --- a/packages/metadata-protocol/src/seed-loader-replay.test.ts +++ b/packages/metadata-protocol/src/seed-loader-replay.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Replay regression: seeds with lookup natural keys must survive a dev-server @@ -40,6 +40,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-retry.test.ts b/packages/metadata-protocol/src/seed-loader-retry.test.ts index f58ab5f23d..6ddfeb473f 100644 --- a/packages/metadata-protocol/src/seed-loader-retry.test.ts +++ b/packages/metadata-protocol/src/seed-loader-retry.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * framework#3150: the self-referencing seed path (`hasSelfRef`) writes records @@ -33,6 +33,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts b/packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts index 4d6a136d2b..b1e2974d8f 100644 --- a/packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts +++ b/packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * #3433 — a curated seed is a snapshot of ESTABLISHED facts (a project already @@ -74,7 +74,7 @@ function createEnforcingEngine(): { engine: IDataEngine; store: Record null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), // Per-row partial-success path the seed loader prefers (framework#3172): // one verdict per row, so a rejected row is culled, not thrown as a batch. insertMany: vi.fn(async (objectName: string, rows: any[], opts: any) => diff --git a/packages/metadata-protocol/src/seed-loader-summary-stale.test.ts b/packages/metadata-protocol/src/seed-loader-summary-stale.test.ts index efdabc5376..5e339bad6f 100644 --- a/packages/metadata-protocol/src/seed-loader-summary-stale.test.ts +++ b/packages/metadata-protocol/src/seed-loader-summary-stale.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SeedLoadResultSchema, SeedLoaderResultSchema } from '@objectstack/spec/data'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * framework#4998: a roll-up summary recompute that exhausts its retries must be @@ -45,6 +45,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts b/packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts index 5cadee528a..b2bcd99e2a 100644 --- a/packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts +++ b/packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts @@ -8,7 +8,7 @@ import { describe, it, expect, vi } from 'vitest'; // tests; this one is the one tsc can actually read. import { SeedLoaderService } from './seed-loader.js'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * framework#4997: a record DROPPED because its reference cannot be resolved — @@ -56,6 +56,7 @@ function createFaithfulEngine(): { engine: IDataEngine; store: Record { + assertEngineFindOnePredicate(objectName, query); const rows = await (engine.find as any)(objectName, { ...query, limit: 1 }); return rows[0] ?? null; }), diff --git a/packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts index 99659fa1b6..9ff51addda 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts @@ -55,7 +55,7 @@ import { describe, it, expect, afterEach } from 'vitest'; import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, - hashSpec, + hashSpec, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { runRepositoryContractTests } from '@objectstack/metadata-core/testing'; import type { MetadataEvent, WatchFilter } from '@objectstack/metadata-core'; @@ -109,6 +109,7 @@ function makeFakeEngine() { return typeof opts.limit === 'number' ? hits.slice(0, opts.limit) : hits; }, async findOne(table: string, opts: { where: Record }): Promise { + assertEngineFindOnePredicate(table, opts); return tableOf(table).find((r) => matches(r, opts.where)) ?? null; }, async insert(table: string, data: Record): Promise<{ id: string }> { diff --git a/packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts index 2218f4c9fe..cc3a01e03d 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts @@ -34,7 +34,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { readFileSync } from 'node:fs'; import { SysMetadataRepository } from './sys-metadata-repository.js'; @@ -143,6 +143,7 @@ function makeFakeEngine() { }); }, async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; } diff --git a/packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts index 8ab02ee23b..e1aec72b96 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts @@ -29,7 +29,7 @@ import { describe, it, expect } from 'vitest'; // The engine-double contract gate: a fake looser than ObjectQL's own verb // dispatch is how #4434 shipped a dead route with its suite green. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SysMetadataRepository } from './sys-metadata-repository.js'; interface Row { @@ -65,6 +65,7 @@ function makeFakeEngine(seed: Row[] = []) { rows, history, async findOne(table: string, q: { where: Record }) { + assertEngineFindOnePredicate(table, q); return tableOf(table).find((r) => matches(r, q.where)) ?? null; }, async find(table: string, q: { where: Record }) { diff --git a/packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts index 17e69696f1..1e454fea99 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts @@ -31,7 +31,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { readFileSync } from 'node:fs'; import { SysMetadataRepository } from './sys-metadata-repository.js'; import { isMissingTableError } from '@objectstack/metadata/errors'; @@ -129,6 +129,7 @@ function makeFakeEngine() { }); }, async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') { if (historyReadFailure) throw historyReadFailure(); return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; diff --git a/packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts index c5da412979..742aab3b94 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts @@ -58,7 +58,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; // refuses. Imported from `@objectstack/metadata-core` and not from // `@objectstack/objectql`: objectql DEPENDS ON this package, so that import // would close a dependency cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import type { MetaRef } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './protocol.js'; import { SysMetadataRepository, resetEnvWritableMetadataTypes } from './sys-metadata-repository.js'; @@ -125,6 +125,7 @@ function makeFakeEngine() { return Array.from(rows.values()); }, async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; return findRow(opts.where)?.row ?? null; }, diff --git a/packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts b/packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts index 7207b528b4..10baba6bef 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts @@ -24,7 +24,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; // would close a dependency cycle turbo rejects outright — which is why all 26 // of this package's (file, verb) pairs sat in the gate's DEBT ledger until // #5619 sank the two predicates into a package both sides already depend on. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SysMetadataRepository } from './sys-metadata-repository.js'; interface Row { [k: string]: unknown } @@ -70,6 +70,7 @@ function makeFakeEngine() { }); }, async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null; return findRow(opts.where)?.row ?? null; }, diff --git a/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts b/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts index 49316e3932..a370e557f4 100644 --- a/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts +++ b/packages/metadata-protocol/src/view-container-runtime-expansion.test.ts @@ -28,7 +28,7 @@ * choke point all three runtime hydration callers share. */ import { describe, expect, it } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from './index.js'; interface Row { @@ -62,7 +62,8 @@ function makeStubEngine() { return null; }; const engine: any = { - async findOne(_t: string, opts: { where: Record }) { return findRow(opts.where)?.row ?? null; }, + async findOne(_t: string, opts: { where: Record }) { + assertEngineFindOnePredicate(_t, opts); return findRow(opts.where)?.row ?? null; }, async find(_t: string, opts: { where: Record }) { return Array.from(rows.values()).filter((r) => matches(r, opts.where)); }, diff --git a/packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts b/packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts index 6923727168..ba50a34428 100644 --- a/packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts +++ b/packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts @@ -9,7 +9,7 @@ import { describe, it, expect } from 'vitest'; // would close a dependency cycle turbo rejects, and is why both of this file's // (file, verb) pairs sat in the gate's DEBT ledger until #5619 sank the two // predicates into a package that depends on neither side. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { migrateSysNotificationToEvent } from './migrate-sys-notification-to-event.js'; /** Columns the legacy (pre-ADR-0030) sys_notification table physically has. */ @@ -57,7 +57,8 @@ function fakeEngine() { return data; }, async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async delete(_object?: string, options?: Record) { assertEngineDeleteDispatch(options); return {}; diff --git a/packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts b/packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts index 35c66b3561..51bad787eb 100644 --- a/packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts +++ b/packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts @@ -21,7 +21,7 @@ // (revert the fix → these tests go red) mean anything. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; @@ -93,6 +93,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const found = rows(name).find((r) => matches(r, q.where)); return found ? { ...found } : null; }, diff --git a/packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts b/packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts index 8cb07bbaff..5aab4a5221 100644 --- a/packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts +++ b/packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts @@ -18,7 +18,7 @@ // would prove nothing — the whole question is what answer leaves the library. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { defaultRoles } from 'better-auth/plugins/admin/access'; import { AuthManager } from './auth-manager'; import { @@ -78,6 +78,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/audience-posture-setting.test.ts b/packages/plugins/plugin-auth/src/audience-posture-setting.test.ts index 6435236c46..7d7cc71f36 100644 --- a/packages/plugins/plugin-auth/src/audience-posture-setting.test.ts +++ b/packages/plugins/plugin-auth/src/audience-posture-setting.test.ts @@ -35,6 +35,7 @@ import type { PluginContext } from '@objectstack/core'; import { AUDIENCE_POSTURES } from '@objectstack/spec/system'; import { AuthPlugin } from './auth-plugin.js'; import { AuthManager } from './auth-manager.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; const SECRET = 'test-secret-at-least-32-chars-long'; @@ -45,7 +46,7 @@ function makeEngine() { return { insert: vi.fn(async (_object: string, row: any) => row), find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; } diff --git a/packages/plugins/plugin-auth/src/audience-posture.test.ts b/packages/plugins/plugin-auth/src/audience-posture.test.ts index 03c6e4a01a..cb6c315632 100644 --- a/packages/plugins/plugin-auth/src/audience-posture.test.ts +++ b/packages/plugins/plugin-auth/src/audience-posture.test.ts @@ -20,7 +20,7 @@ // the ADR-0078 defect this card closes. import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ERROR_CODE_LEDGER } from '@objectstack/spec/api'; import { AuthManager } from './auth-manager'; import { @@ -77,6 +77,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/auth-contains-filter.test.ts b/packages/plugins/plugin-auth/src/auth-contains-filter.test.ts index 6756ebc4ac..2629de70f7 100644 --- a/packages/plugins/plugin-auth/src/auth-contains-filter.test.ts +++ b/packages/plugins/plugin-auth/src/auth-contains-filter.test.ts @@ -91,6 +91,7 @@ import { FILTER_OPERATORS } from '@objectstack/spec/data'; import type { QueryAST } from '@objectstack/spec/data'; import type { IDataEngine } from '@objectstack/core'; import { createObjectQLAdapterFactory } from './objectql-adapter'; +import { assertEngineFindOnePredicate } from '@objectstack/objectql'; /** * The columns face 2 reads, declared — a real table has to be told. @@ -126,7 +127,7 @@ function sqlReadEngine(driver: SqlDriver): IDataEngine { // `find(obj, … as any)` too, and nothing here needs to be off-contract. return { find: (object: string, query: QueryAST) => driver.find(object, query), - findOne: (object: string, query: QueryAST) => driver.findOne(object, query), + findOne: (object: string, query: QueryAST) => { assertEngineFindOnePredicate(object, query); return driver.findOne(object, query); }, count: (object: string, query?: QueryAST) => driver.count(object, query), } as unknown as IDataEngine; } diff --git a/packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts b/packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts index 69a631d6fd..00ebcd4960 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts @@ -12,7 +12,7 @@ // if the fallback did not reach jose, these tests would 500 like the bug report. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; const createMemoryEngine = () => { @@ -49,6 +49,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); return rows(name).find((r) => matches(r, q.where)) ?? null; }, async find(name: string, q: any = {}) { diff --git a/packages/plugins/plugin-auth/src/auth-manager.optional-plugin-isolation.test.ts b/packages/plugins/plugin-auth/src/auth-manager.optional-plugin-isolation.test.ts index 4485279683..4a9e46da6c 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.optional-plugin-isolation.test.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.optional-plugin-isolation.test.ts @@ -16,6 +16,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; +import { assertEngineFindOnePredicate } from '@objectstack/objectql'; const failures = vi.hoisted(() => ({ oauthProvider: false, bearer: false })); @@ -83,6 +84,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); return rows(name).find((r) => matches(r, q.where)) ?? null; }, async find(name: string, q: any = {}) { diff --git a/packages/plugins/plugin-auth/src/auth-manager.test.ts b/packages/plugins/plugin-auth/src/auth-manager.test.ts index 254e5feb2e..9ff8cbbe6a 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.test.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { AuthManager, ipMatchesRange } from './auth-manager'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; // Mock better-auth so we can control the handler behaviour vi.mock('better-auth', () => ({ @@ -898,7 +898,7 @@ describe('AuthManager', () => { find: vi.fn(async (object: string) => object === 'sys_member' && role !== null ? [{ role }] : [], ), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), count: vi.fn(async () => 0), insert: vi.fn(async () => ({})), update: vi.fn(async (_o: string, data: any, options?: any) => { @@ -3052,6 +3052,7 @@ describe('AuthManager', () => { // in dogfood that a query-agnostic mock had masked. const makeEngine = (user: any) => ({ findOne: vi.fn(async (_obj: string, q: any) => { + assertEngineFindOnePredicate(_obj, q); const w = q?.where ?? {}; if (!user) return null; const matches = Object.entries(w).every(([k, v]) => (user as any)[k] === v); @@ -3665,6 +3666,7 @@ describe('AuthManager', () => { const SECRET = 'test-secret-at-least-32-chars-long'; const makeEngine = (user: any) => ({ findOne: vi.fn(async (_o: string, q: any) => { + assertEngineFindOnePredicate(_o, q); const w = q?.where ?? {}; if (!user) return null; return Object.entries(w).every(([k, v]) => (user as any)[k] === v) ? user : null; @@ -3787,6 +3789,7 @@ describe('AuthManager', () => { const SECRET = 'test-secret-at-least-32-chars-long'; const makeEngine = (user: any, org: any) => ({ findOne: vi.fn(async (obj: string, q: any) => { + assertEngineFindOnePredicate(obj, q); const row = obj === 'sys_organization' ? org : user; if (!row) return null; const w = q?.where ?? {}; diff --git a/packages/plugins/plugin-auth/src/auth-where-operator-coverage.test.ts b/packages/plugins/plugin-auth/src/auth-where-operator-coverage.test.ts index acc0d93059..718a998448 100644 --- a/packages/plugins/plugin-auth/src/auth-where-operator-coverage.test.ts +++ b/packages/plugins/plugin-auth/src/auth-where-operator-coverage.test.ts @@ -74,7 +74,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { SqlDriver } from '@objectstack/driver-sql'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { whereOperators } from '@better-auth/core/db/adapter'; import { FILTER_OPERATORS } from '@objectstack/spec/data'; import type { QueryAST } from '@objectstack/spec/data'; @@ -146,7 +146,7 @@ function sqlEngine(driver: SqlDriver): IDataEngine { // `query-options/no-any-erasure` (#4674/#4918) counts test-side calls too. return { find: (object: string, query: QueryAST) => driver.find(object, query), - findOne: (object: string, query: QueryAST) => driver.findOne(object, query), + findOne: (object: string, query: QueryAST) => { assertEngineFindOnePredicate(object, query); return driver.findOne(object, query); }, count: (object: string, query?: QueryAST) => driver.count(object, query), delete: async (object: string, options: Record) => { const dispatch = assertEngineDeleteDispatch(options); diff --git a/packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts b/packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts index a1e1ac1824..ae69f37730 100644 --- a/packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts +++ b/packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts @@ -32,7 +32,7 @@ */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import type { IEmailService, SendEmailResult, SendTemplateInput } from '@objectstack/spec/contracts'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; @@ -101,6 +101,7 @@ function createMemoryEngine() { return { ...row }; }, async findOne(name: string, query: MemoryQuery = {}): Promise { + assertEngineFindOnePredicate(name, query); const row = rows(name).find((r) => matches(r, query.where)); return row ? project(row, query.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/first-session-membership-ordering.test.ts b/packages/plugins/plugin-auth/src/first-session-membership-ordering.test.ts index 9af52775c0..233ea63da7 100644 --- a/packages/plugins/plugin-auth/src/first-session-membership-ordering.test.ts +++ b/packages/plugins/plugin-auth/src/first-session-membership-ordering.test.ts @@ -38,6 +38,7 @@ import { describe, it, expect, vi } from 'vitest'; import { AuthManager } from './auth-manager.js'; import { loginEventFor } from './auth-session-audit.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; const USER = 'u_first_session'; const DEFAULT_ORG = 'org_default'; @@ -77,7 +78,7 @@ function makeEngine(seed: MemberRow[] = []) { ), // eslint-disable-next-line @typescript-eslint/no-explicit-any findOne: vi.fn(async (model: string, q: any) => - model === 'sys_member' ? (rows.find((r) => match(r, q?.where)) ?? null) : null, + { assertEngineFindOnePredicate(model, q); return model === 'sys_member' ? (rows.find((r) => match(r, q?.where)) ?? null) : null; }, ), }; } @@ -231,7 +232,8 @@ describe("[#8247 rule 2] a user's FIRST session settles the membership before re it('a broken engine never breaks session create', async () => { const engine = { - findOne: vi.fn(async () => { throw new Error('db down'); }), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { + assertEngineFindOnePredicate(object, query); throw new Error('db down'); }), find: vi.fn(async () => { throw new Error('db down'); }), insert: vi.fn(async () => { throw new Error('db down'); }), }; diff --git a/packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts b/packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts index 9484d41b72..0cd3b2f702 100644 --- a/packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts +++ b/packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts @@ -25,7 +25,7 @@ // calls. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; import { @@ -83,6 +83,7 @@ export const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/membership-policy-setting.test.ts b/packages/plugins/plugin-auth/src/membership-policy-setting.test.ts index 4cc6effacf..d9c490b275 100644 --- a/packages/plugins/plugin-auth/src/membership-policy-setting.test.ts +++ b/packages/plugins/plugin-auth/src/membership-policy-setting.test.ts @@ -43,6 +43,7 @@ vi.mock('./reconcile-membership.js', async (importOriginal) => { }); import { reconcileMembership, backfillMemberships, MEMBERSHIP_POLICIES } from './reconcile-membership.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; const reconcileSpy = vi.mocked(reconcileMembership); const backfillSpy = vi.mocked(backfillMemberships); @@ -75,7 +76,7 @@ function makeEngine(seed: { users?: Array<{ id: string }>; members?: Array<{ use } return []; }), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; } diff --git a/packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts b/packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts index 3e7803b19e..e1bef818cd 100644 --- a/packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts +++ b/packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts @@ -55,7 +55,7 @@ // precedent: patch the real thing, never stub our own code). import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { createTenancyService, type TenancyService, type TenancyPosture } from './tenancy-service'; @@ -105,6 +105,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/organization-add-member.test.ts b/packages/plugins/plugin-auth/src/organization-add-member.test.ts index db3e87026f..45451418a0 100644 --- a/packages/plugins/plugin-auth/src/organization-add-member.test.ts +++ b/packages/plugins/plugin-auth/src/organization-add-member.test.ts @@ -31,7 +31,7 @@ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest'; import { Hono } from 'hono'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { AuthPlugin } from './auth-plugin'; import { runOrganizationAddMember } from './organization-add-member.js'; @@ -105,6 +105,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts b/packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts index fa3652eb21..2c02bee094 100644 --- a/packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts +++ b/packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts @@ -47,7 +47,7 @@ // so the mounted route is what has to answer here. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; @@ -123,6 +123,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts b/packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts index dcb373adbc..21683ee61a 100644 --- a/packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts +++ b/packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts @@ -33,7 +33,7 @@ // the double cannot accept a call ObjectQL refuses. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { SysMember } from '@objectstack/platform-objects'; import { AuthManager } from './auth-manager'; import { inviteForAudienceGate } from './audience-gate-test-support'; @@ -143,6 +143,7 @@ const createMemoryEngine = (vetoUserDelete?: () => Error | undefined) => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const found = rows(name).find((r) => matches(r, q.where)); return found ? { ...found } : null; }, diff --git a/packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts b/packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts index aec1f09e0c..e50f28fbc8 100644 --- a/packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts +++ b/packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts @@ -16,7 +16,7 @@ // answer leaves the library. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { REVOKE_SESSION_NOT_FOUND_CODE, @@ -73,6 +73,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/scim-case-insensitive-identifier.test.ts b/packages/plugins/plugin-auth/src/scim-case-insensitive-identifier.test.ts index b78c48d3a4..99f4b6785e 100644 --- a/packages/plugins/plugin-auth/src/scim-case-insensitive-identifier.test.ts +++ b/packages/plugins/plugin-auth/src/scim-case-insensitive-identifier.test.ts @@ -86,6 +86,7 @@ import { createObjectQLAdapterFactory, NORMALISED_IDENTIFIER_FIELDS, } from './objectql-adapter'; +import { assertEngineFindOnePredicate } from '@objectstack/objectql'; /** * The `sys_user` columns face 2 touches, declared — a real table has to be told. @@ -130,7 +131,7 @@ function sqlReadEngine(driver: SqlDriver): IDataEngine { // `query-options/no-any-erasure` (#4674/#4918) counts test-side calls too. return { find: (object: string, query: QueryAST) => driver.find(object, query), - findOne: (object: string, query: QueryAST) => driver.findOne(object, query), + findOne: (object: string, query: QueryAST) => { assertEngineFindOnePredicate(object, query); return driver.findOne(object, query); }, count: (object: string, query?: QueryAST) => driver.count(object, query), } as unknown as IDataEngine; } diff --git a/packages/plugins/plugin-auth/src/session-of-record.test.ts b/packages/plugins/plugin-auth/src/session-of-record.test.ts index 69d14a9cd8..77f583bfce 100644 --- a/packages/plugins/plugin-auth/src/session-of-record.test.ts +++ b/packages/plugins/plugin-auth/src/session-of-record.test.ts @@ -24,7 +24,7 @@ // and the sign-in after-hook really run. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AuthManager } from './auth-manager'; import { cacheSecondaryStorage } from './secondary-storage'; @@ -86,6 +86,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-auth/src/session-tombstone.test.ts b/packages/plugins/plugin-auth/src/session-tombstone.test.ts index a394240aa1..596b0a4b76 100644 --- a/packages/plugins/plugin-auth/src/session-tombstone.test.ts +++ b/packages/plugins/plugin-auth/src/session-tombstone.test.ts @@ -20,7 +20,7 @@ // the library does around our write. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { runWithEndpointContext } from '@better-auth/core/context'; import { AuthManager } from './auth-manager'; import { BETTER_AUTH_MOUNTED_SURFACE } from './auth-route-ledger'; @@ -91,6 +91,7 @@ const createMemoryEngine = () => { return { ...row }; }, async findOne(name: string, q: any = {}) { + assertEngineFindOnePredicate(name, q); const row = rows(name).find((r) => matches(r, q.where)); return row ? project(row, q.fields) : null; }, diff --git a/packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts b/packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts index 686e2d8e29..4f6aa7bd2c 100644 --- a/packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts +++ b/packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts @@ -34,7 +34,7 @@ // shape: an app policy spelling the identical predicate is app-authored and must // keep counting. import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; import { SecurityPlugin } from './security-plugin.js'; @@ -218,6 +218,7 @@ function makeEngine(opts: { findOneThrows?: boolean } = {}) { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); // The fail-closed case drives the probe itself into a throw — the method // must swallow it into `abstain` and never reject outward. if (opts.findOneThrows) throw new Error('engine exploded'); diff --git a/packages/plugins/plugin-security/src/check-only-write-scope.test.ts b/packages/plugins/plugin-security/src/check-only-write-scope.test.ts index 8a946e4c65..f30284f5a9 100644 --- a/packages/plugins/plugin-security/src/check-only-write-scope.test.ts +++ b/packages/plugins/plugin-security/src/check-only-write-scope.test.ts @@ -56,7 +56,7 @@ // must not change); reverting site 2 alone reddens the site-2 holder describe. // Neither revert reddens the other's pins. import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SharingService, buildSharingMiddleware } from '@objectstack/plugin-sharing'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; @@ -279,6 +279,7 @@ function makeEngine() { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); const rows = await this.find(object, { ...options, limit: 1 }); return rows[0] ?? null; }, diff --git a/packages/plugins/plugin-security/src/controlled-by-parent-chain.test.ts b/packages/plugins/plugin-security/src/controlled-by-parent-chain.test.ts index f05e0d854d..b3e3465010 100644 --- a/packages/plugins/plugin-security/src/controlled-by-parent-chain.test.ts +++ b/packages/plugins/plugin-security/src/controlled-by-parent-chain.test.ts @@ -53,6 +53,7 @@ import { SecurityPlugin } from './security-plugin.js'; import { SharingService, type SharingEngine } from '@objectstack/plugin-sharing'; import { matchesFilterCondition } from '@objectstack/formula'; import type { PermissionSet } from '@objectstack/spec/security'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const REP = 'usr_rep'; const OTHER = 'usr_other'; @@ -275,6 +276,7 @@ function makeStore(rows: Record, quoteModel: string = 'controlled return typeof options?.limit === 'number' ? hits.slice(0, options.limit) : hits; }), findOne: vi.fn(async (object: string, options: any = {}) => { + assertEngineFindOnePredicate(object, options); const all = rows[object] ?? []; return all.find((r) => matchesFilterCondition(r, options?.where ?? null)) ?? null; }), diff --git a/packages/plugins/plugin-security/src/controlled-by-parent-detail-write-authority.test.ts b/packages/plugins/plugin-security/src/controlled-by-parent-detail-write-authority.test.ts index a5519a8814..9d40b3e341 100644 --- a/packages/plugins/plugin-security/src/controlled-by-parent-detail-write-authority.test.ts +++ b/packages/plugins/plugin-security/src/controlled-by-parent-detail-write-authority.test.ts @@ -56,6 +56,7 @@ import { matchesFilterCondition } from '@objectstack/formula'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; import { defaultPermissionSets } from './objects/default-permission-sets.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const MKT = 'usr_marketing'; const ADMIN = 'usr_admin'; @@ -371,6 +372,7 @@ function makeStore(rows: Record) { return typeof options?.limit === 'number' ? hits.slice(0, options.limit) : hits; }), findOne: vi.fn(async (object: string, options: any = {}) => { + assertEngineFindOnePredicate(object, options); const all = rows[object] ?? []; return all.find((r) => matchesFilterCondition(r, options?.where ?? options?.filter ?? null)) ?? null; }), diff --git a/packages/plugins/plugin-security/src/controlled-by-parent-master-widener.test.ts b/packages/plugins/plugin-security/src/controlled-by-parent-master-widener.test.ts index 5699f30cd8..d5b1d26218 100644 --- a/packages/plugins/plugin-security/src/controlled-by-parent-master-widener.test.ts +++ b/packages/plugins/plugin-security/src/controlled-by-parent-master-widener.test.ts @@ -59,6 +59,7 @@ import { matchesFilterCondition } from '@objectstack/formula'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; import { defaultPermissionSets } from './objects/default-permission-sets.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const MKT = 'usr_marketing'; const ADMIN = 'usr_admin'; @@ -271,6 +272,7 @@ function makeStore(rows: Record) { return typeof options?.limit === 'number' ? hits.slice(0, options.limit) : hits; }), findOne: vi.fn(async (object: string, options: any = {}) => { + assertEngineFindOnePredicate(object, options); const all = rows[object] ?? []; return all.find((r) => matchesFilterCondition(r, options?.where ?? options?.filter ?? null)) ?? null; }), diff --git a/packages/plugins/plugin-security/src/controlled-by-parent-sharing.test.ts b/packages/plugins/plugin-security/src/controlled-by-parent-sharing.test.ts index 77f6136db3..e86204c24d 100644 --- a/packages/plugins/plugin-security/src/controlled-by-parent-sharing.test.ts +++ b/packages/plugins/plugin-security/src/controlled-by-parent-sharing.test.ts @@ -45,7 +45,7 @@ import { matchesFilterCondition } from '@objectstack/formula'; // the day the validator's own skip list changes. This package's vitest config // aliases `@objectstack/objectql` to `packages/objectql/src`, so this resolves // to the source in the checkout rather than a prebuilt dist. -import { validateRecord } from '@objectstack/objectql'; +import { validateRecord, assertEngineFindOnePredicate } from '@objectstack/objectql'; import type { PermissionSet } from '@objectstack/spec/security'; const REP = 'usr_rep'; @@ -227,6 +227,7 @@ function makeStore(rows: Record, detail: DetailVariant = 'valid', return typeof options?.limit === 'number' ? hits.slice(0, options.limit) : hits; }), findOne: vi.fn(async (object: string, options: any = {}) => { + assertEngineFindOnePredicate(object, options); // [#7505] The one thing this double gains: a store that is DOWN for one // object, so "the row is not there" and "I could not look" stop being the // same observation. diff --git a/packages/plugins/plugin-security/src/explain-engine.test.ts b/packages/plugins/plugin-security/src/explain-engine.test.ts index efa556a150..d3b0781afc 100644 --- a/packages/plugins/plugin-security/src/explain-engine.test.ts +++ b/packages/plugins/plugin-security/src/explain-engine.test.ts @@ -6,6 +6,7 @@ import { resolveUserAuthzGrants } from '@objectstack/core'; import { PermissionSetSchema } from '@objectstack/spec/security'; import { PermissionEvaluator } from './permission-evaluator'; import { explainAccess, buildContextForUser, type ExplainEngineDeps } from './explain-engine'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; const SALES_USER = PermissionSetSchema.parse({ name: 'sales_user', @@ -339,7 +340,7 @@ describe('explainAccess — fls reports partial masking (#9127)', () => { // `delegatorSets` stays null, which is a different report entirely. ql: { getSchema: () => PRIVATE_SCHEMA, - findOne: async () => ({ id: 'u_boss' }), + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return ({ id: 'u_boss' }); }, find: async () => [], }, getPartialMaskRules: async () => ({ phone: 'phone' }), diff --git a/packages/plugins/plugin-security/src/field-masking-rule.test.ts b/packages/plugins/plugin-security/src/field-masking-rule.test.ts index f992206ebc..bbee349b69 100644 --- a/packages/plugins/plugin-security/src/field-masking-rule.test.ts +++ b/packages/plugins/plugin-security/src/field-masking-rule.test.ts @@ -22,6 +22,7 @@ import { SecurityPlugin } from './security-plugin.js'; import { FieldMasker, maskFieldValue, MASK_CHAR } from './field-masker.js'; import type { PermissionSet } from '@objectstack/spec/security'; import type { FieldMaskingRule } from '@objectstack/spec/data'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // maskFieldValue — the preset + keepHead/keepTail transforms @@ -189,7 +190,7 @@ describe('SecurityPlugin — maskingRule middleware enforcement (#8993)', () => const ql: any = { registerMiddleware: (mw: any) => { if (!middleware) middleware = mw; }, getSchema: () => schema, - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async () => [], }; const metadata = { get: async () => schema, list: async () => sets }; @@ -304,7 +305,7 @@ describe('SecurityPlugin — maskingRule middleware enforcement (#8993)', () => const ql: any = { registerMiddleware: (mw: any) => { if (!middleware) middleware = mw; }, getSchema: () => badSchema, - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async () => [], }; const metadata = { get: async () => badSchema, list: async () => [setNoCap] }; diff --git a/packages/plugins/plugin-security/src/invitation-placement.test.ts b/packages/plugins/plugin-security/src/invitation-placement.test.ts index c38c21dc37..5923fd52d0 100644 --- a/packages/plugins/plugin-security/src/invitation-placement.test.ts +++ b/packages/plugins/plugin-security/src/invitation-placement.test.ts @@ -16,6 +16,7 @@ import { createInvitationPlacementService, readPlacementIntent, } from './invitation-placement.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; const INTENT = { businessUnitId: 'bu_plant_a', positions: ['qc_inspector', 'line_lead'] }; @@ -38,7 +39,7 @@ function makeQl(tables: Record = {}) { ), ); }), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(async (_o: string, row: any) => ({ id: 'row', ...row })), }; } diff --git a/packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts b/packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts index c8ab939baf..7aaac1b474 100644 --- a/packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts +++ b/packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts @@ -40,7 +40,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so this fake engine cannot // accept a call ObjectQL refuses (`check:engine-double-contract`; imported // from `@objectstack/metadata-core` exactly as the sibling pins do). -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation, resetEnvWritableMetadataTypes, @@ -116,6 +116,7 @@ function makeFakeEngine(opts?: { listItemsThrows?: boolean }) { return Array.from(rows.values()); }, async findOne(table: string, opts2: { where: Record }) { + assertEngineFindOnePredicate(table, opts2); if (table === 'sys_metadata_history') return null; return findRow(opts2.where)?.row ?? null; }, diff --git a/packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts b/packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts index dfcc6f4a76..3fe6f1bcd0 100644 --- a/packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts +++ b/packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts @@ -75,7 +75,7 @@ import { describe, it, expect } from 'vitest'; import { PermissionSetSchema } from '@objectstack/spec/security'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SysPermissionSet } from './objects/sys-permission-set.object.js'; import { createPermissionSetWriteThrough, @@ -143,6 +143,7 @@ function makeQl(declared: any[] | null = null) { return typeof q?.limit === 'number' ? hit.slice(0, q.limit) : hit; }, async findOne(object: string, q: any) { + assertEngineFindOnePredicate(object, q); const rows = tableFor(object); return rows?.find((r) => matches(r, q?.where)) ?? null; }, diff --git a/packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts b/packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts index dd839139fe..3e463686bd 100644 --- a/packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts +++ b/packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts @@ -68,7 +68,7 @@ */ import { describe, it, expect } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { PermissionSetSchema } from '@objectstack/spec/security'; import { createPermissionSetWriteThrough, @@ -140,6 +140,7 @@ function makeQl(rows: any[], declared: any[]) { ); }, async findOne(object: string, q: any) { + assertEngineFindOnePredicate(object, q); return (await this.find(object, q))[0] ?? null; }, // Opens with the PRODUCER's own dispatch predicate, never a hand-mirrored diff --git a/packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts b/packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts index 6264aea898..591284d275 100644 --- a/packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts +++ b/packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts @@ -38,7 +38,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { FileI18nAdapter } from '@objectstack/service-i18n'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; @@ -125,6 +125,7 @@ function makeEngine() { return (tables[object] ??= []).filter((r) => matches(r, options?.where ?? options?.filter)); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); return (await this.find(object, options))[0] ?? null; }, async insert(object: string, data: any) { (tables[object] ??= []).push({ ...data }); return data; }, diff --git a/packages/plugins/plugin-security/src/permission-set-active.test.ts b/packages/plugins/plugin-security/src/permission-set-active.test.ts index 123edf77fb..673a48df5d 100644 --- a/packages/plugins/plugin-security/src/permission-set-active.test.ts +++ b/packages/plugins/plugin-security/src/permission-set-active.test.ts @@ -26,6 +26,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SecurityPlugin } from './security-plugin.js'; import type { PermissionSet } from '@objectstack/spec/security'; import type { ISecurityService } from '@objectstack/spec/contracts'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** The metadata-declared baseline every authenticated caller resolves additively. */ const MEMBER_DEFAULT: PermissionSet = { @@ -64,7 +65,7 @@ async function locateSecurityService( const ql: any = { registerMiddleware: () => {}, getSchema: (name: string) => (name === 'deal' ? schema : null), - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async (object: string, query: any) => { if (object !== 'sys_permission_set') return []; const wanted: string[] = query?.where?.name?.$in ?? []; diff --git a/packages/plugins/plugin-security/src/permission-set-projection.test.ts b/packages/plugins/plugin-security/src/permission-set-projection.test.ts index 00bd08ba8e..eea212f601 100644 --- a/packages/plugins/plugin-security/src/permission-set-projection.test.ts +++ b/packages/plugins/plugin-security/src/permission-set-projection.test.ts @@ -35,6 +35,7 @@ import { reconcilePermissionSetProjection, type ProjectionLogger, } from './permission-set-projection.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * In-memory ql over sys_permission_set + sys_metadata. @@ -77,6 +78,7 @@ function makeQl() { return rows ? rows.filter((r) => matches(r, q?.where)) : []; }, async findOne(object: string, q: any) { + assertEngineFindOnePredicate(object, q); const rows = tableFor(object); return rows?.find((r) => matches(r, q?.where)) ?? null; }, diff --git a/packages/plugins/plugin-security/src/permission-set-resolution-memo.test.ts b/packages/plugins/plugin-security/src/permission-set-resolution-memo.test.ts index 35c0a977ee..8db79d1731 100644 --- a/packages/plugins/plugin-security/src/permission-set-resolution-memo.test.ts +++ b/packages/plugins/plugin-security/src/permission-set-resolution-memo.test.ts @@ -42,6 +42,7 @@ import { describe, it, expect, vi } from 'vitest'; import type { PermissionSet } from '@objectstack/spec/security'; import { SecurityPlugin } from './security-plugin.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** Resolvable from metadata, so only `custom_role` below reaches the db loader. */ const baselineSet: PermissionSet = { @@ -74,7 +75,7 @@ const makeHarness = () => { }, getSchema: () => baseSchema, find, - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), }; const services: Record = { manifest: { register: vi.fn() }, diff --git a/packages/plugins/plugin-security/src/resolve-permission-sets-for-context.pin.test.ts b/packages/plugins/plugin-security/src/resolve-permission-sets-for-context.pin.test.ts index 8c5705e1ec..d843b19b13 100644 --- a/packages/plugins/plugin-security/src/resolve-permission-sets-for-context.pin.test.ts +++ b/packages/plugins/plugin-security/src/resolve-permission-sets-for-context.pin.test.ts @@ -31,6 +31,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SecurityPlugin } from './security-plugin.js'; import type { PermissionSet } from '@objectstack/spec/security'; import type { ISecurityService } from '@objectstack/spec/contracts'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** A metadata-declared set: the platform baseline every member resolves additively. */ const MEMBER_DEFAULT: PermissionSet = { @@ -61,7 +62,7 @@ function bootPlugin(dbRows: Array> = []) { const ql: any = { registerMiddleware: () => {}, getSchema: (name: string) => (name === 'deal' ? schema : null), - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async (object: string, query: any) => { if (object !== 'sys_permission_set') return []; const wanted: string[] = query?.where?.name?.$in ?? []; diff --git a/packages/plugins/plugin-security/src/row-write-widener-composition.test.ts b/packages/plugins/plugin-security/src/row-write-widener-composition.test.ts index badc535866..9e2ed4210b 100644 --- a/packages/plugins/plugin-security/src/row-write-widener-composition.test.ts +++ b/packages/plugins/plugin-security/src/row-write-widener-composition.test.ts @@ -60,7 +60,7 @@ // Anything read-scope-dependent belongs in the real-stack pin: // `packages/qa/dogfood/test/authored-row-write-scope.dogfood.test.ts`. import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SharingService, buildSharingMiddleware } from '@objectstack/plugin-sharing'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; @@ -244,6 +244,7 @@ function makeEngine() { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); const rows = await this.find(object, { ...options, limit: 1 }); return rows[0] ?? null; }, diff --git a/packages/plugins/plugin-security/src/security-denial-user-copy.test.ts b/packages/plugins/plugin-security/src/security-denial-user-copy.test.ts index e08f8aaa2b..c2ffc414c4 100644 --- a/packages/plugins/plugin-security/src/security-denial-user-copy.test.ts +++ b/packages/plugins/plugin-security/src/security-denial-user-copy.test.ts @@ -37,7 +37,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { matchesFilterCondition } from '@objectstack/formula'; import { FileI18nAdapter } from '@objectstack/service-i18n'; import { PermissionSetSchema } from '@objectstack/spec/security'; @@ -154,6 +154,7 @@ function makeEngine() { return (tables[object] ??= []).filter((r) => matches(r, options?.where ?? options?.filter)); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); return (await this.find(object, options))[0] ?? null; }, async insert(object: string, data: any) { (tables[object] ??= []).push({ ...data }); return data; }, diff --git a/packages/plugins/plugin-security/src/security-plugin.test.ts b/packages/plugins/plugin-security/src/security-plugin.test.ts index 9aaeb70467..d29f21de47 100644 --- a/packages/plugins/plugin-security/src/security-plugin.test.ts +++ b/packages/plugins/plugin-security/src/security-plugin.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { describe, it, expect, vi } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { SecurityPlugin } from './security-plugin.js'; import { PermissionEvaluator, crudBucketForOperation } from './permission-evaluator.js'; import { FieldMasker } from './field-masker.js'; @@ -119,7 +119,7 @@ describe('SecurityPlugin', () => { const ql: any = { registerMiddleware: (mw: any) => middlewares.push(mw), find: vi.fn(async () => []), - findOne: vi.fn(async () => null), + findOne: vi.fn(async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }), insert: vi.fn(async (_o: string, d: any) => ({ id: d?.id ?? 'x' })), // Opens with the PRODUCER's own dispatch predicate, never a hand-mirrored // guard (check:engine-double-contract) — same shape as the makeQl double @@ -3551,7 +3551,7 @@ describe('SecurityPlugin — ADR-0066 D3 field-level requiredPermissions', () => const ql: any = { registerMiddleware: (mw: any) => { if (!middleware) middleware = mw; }, getSchema: () => schema, - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async () => [], }; const metadata = { get: async () => schema, list: async () => sets }; @@ -3655,6 +3655,7 @@ describe('explainAccessForCaller (ADR-0090 D6/D12)', () => { return typeof opts?.limit === 'number' ? rows.slice(0, opts.limit) : rows; }, async findOne(object: string, opts: any) { + assertEngineFindOnePredicate(object, opts); const rows = (tables[object] ?? []).filter((r) => matches(r, opts?.where)); return rows[0] ?? null; }, @@ -3779,6 +3780,7 @@ describe('SecurityPlugin — ADR-0090 D10 agent intersection', () => { return typeof o?.limit === 'number' ? rows.slice(0, o.limit) : rows; }, async findOne(object: string, o: any) { + assertEngineFindOnePredicate(object, o); if (object.startsWith('sys_')) { return (tables[object] ?? []).filter((r) => matches(r, o?.where))[0] ?? null; } @@ -3960,7 +3962,7 @@ describe('managed-object write denies wiring (#3325)', () => { const ql: any = { registerMiddleware: vi.fn(), getSchema: () => undefined, - findOne: async () => null, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return null; }, find: async () => [], count: async () => 0, insert: async (_o: string, d: any) => ({ id: d?.id ?? 'x' }), diff --git a/packages/plugins/plugin-security/src/select-only-write-visibility.test.ts b/packages/plugins/plugin-security/src/select-only-write-visibility.test.ts index ddf606a1a2..a76a8c004b 100644 --- a/packages/plugins/plugin-security/src/select-only-write-visibility.test.ts +++ b/packages/plugins/plugin-security/src/select-only-write-visibility.test.ts @@ -41,7 +41,7 @@ // posture-permitting object) is not newly narrowed — their readable set // is unbounded, so the readability requirement imposes nothing. import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { SharingService, buildSharingMiddleware } from '@objectstack/plugin-sharing'; import { PermissionSetSchema } from '@objectstack/spec/security'; import type { PermissionSet } from '@objectstack/spec/security'; @@ -269,6 +269,7 @@ function makeEngine() { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); const rows = await this.find(object, { ...options, limit: 1 }); return rows[0] ?? null; }, diff --git a/packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts b/packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts index 0cee308a45..000c4590f2 100644 --- a/packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts +++ b/packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts @@ -36,6 +36,7 @@ import { describe, it, expect, vi } from 'vitest'; import { SecurityPlugin } from './security-plugin.js'; import type { PermissionSet } from '@objectstack/spec/security'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const USER = 'usr_admin'; const OTHER = 'usr_other'; @@ -144,6 +145,7 @@ async function boot(options: BootOptions = {}) { getSchema: (object: string) => SCHEMAS[object], find: vi.fn(async (object: string, o: any = {}) => (rows[object] ?? []).filter((r) => matches(r, o?.where))), findOne: vi.fn(async (object: string, o: any = {}) => { + assertEngineFindOnePredicate(object, o); if (options.faultOn && object === options.faultOn) throw datasourceOutage(object); return (rows[object] ?? []).find((r) => matches(r, o?.where)) ?? null; }), diff --git a/packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts b/packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts index c6fb223812..c08d55d5a4 100644 --- a/packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts +++ b/packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts @@ -31,7 +31,7 @@ import { assertEngineUpdateDispatch, resolveEngineDeleteDispatch, ENGINE_DELETE_REJECT_MESSAGE, - ENGINE_UPDATE_REJECT_MESSAGE, + ENGINE_UPDATE_REJECT_MESSAGE, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { SharingService, buildSharingMiddleware } from '@objectstack/plugin-sharing'; import { PermissionSetSchema } from '@objectstack/spec/security'; @@ -128,6 +128,7 @@ function makeEngine() { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); const rows = await this.find(object, { ...options, limit: 1 }); return rows[0] ?? null; }, diff --git a/packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts b/packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts index 26d2c2fb34..123318e92c 100644 --- a/packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts +++ b/packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts @@ -51,7 +51,7 @@ // what THIS file owns is the consumer half: which outcomes widen, which do not, // and that everything that is not a literal `admit` leaves the refusal intact. import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { SharingService, type SharingSecurityProbe } from './sharing-service.js'; import { buildSharingMiddleware } from './sharing-plugin.js'; @@ -204,6 +204,7 @@ function makeEngine() { return rows.filter((r) => matches(r, options.filter ?? options.where)).slice(0, options.limit ?? 1000); }, async findOne(object: string, options: any = {}) { + assertEngineFindOnePredicate(object, options); const rows = await this.find(object, { ...options, limit: 1 }); return rows[0] ?? null; }, diff --git a/packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts b/packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts index bb7d979946..f78ad1fc82 100644 --- a/packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts +++ b/packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts @@ -45,7 +45,7 @@ import type { DriverQuery } from '@objectstack/spec/contracts'; // The producer's OWN dispatch predicate: a double that opens its write verbs // with this cannot accept a call the real engine refuses // (`check:engine-double-contract`). -import { assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ShareLinkService } from './share-link-service.js'; import { SysShareLink } from './objects/sys-share-link.object.js'; @@ -136,7 +136,7 @@ async function boot(article: any = ARTICLE, options: BootOptions = {}) { if (!options.shapeRow || object !== 'article' || !Array.isArray(rows)) return rows; return rows.map(options.shapeRow); }, - findOne: (object: string, query: any) => driver.findOne(object, query), + findOne: (object: string, query: any) => { assertEngineFindOnePredicate(object, query); return driver.findOne(object, query); }, insert: (object: string, data: any) => driver.create(object, data), // Opened with ObjectQL's OWN dispatch predicate rather than a hand-mirrored // id check, so this facade cannot accept an update the real engine refuses diff --git a/packages/plugins/plugin-webhooks/src/auto-enqueuer.test.ts b/packages/plugins/plugin-webhooks/src/auto-enqueuer.test.ts index 2f005fb206..af3361bb5b 100644 --- a/packages/plugins/plugin-webhooks/src/auto-enqueuer.test.ts +++ b/packages/plugins/plugin-webhooks/src/auto-enqueuer.test.ts @@ -27,6 +27,7 @@ import type { } from '@objectstack/spec/contracts'; import type { EnqueueHttpInput } from '@objectstack/service-messaging'; import { AutoEnqueuer, type HttpEnqueueFn } from './auto-enqueuer.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // Fakes @@ -84,6 +85,7 @@ class FakeEngine implements IDataEngine { return all.filter((r) => Object.entries(q.where).every(([k, v]) => { if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`); return r[k] === v; })); } async findOne(name: string, q?: any): Promise { + assertEngineFindOnePredicate(name, q); return (await this.find(name, q))[0] ?? null; } async insert(name: string, data: any): Promise { diff --git a/packages/plugins/plugin-webhooks/src/bootstrap-declared-webhooks.test.ts b/packages/plugins/plugin-webhooks/src/bootstrap-declared-webhooks.test.ts index e1fb59a76c..4928c2f597 100644 --- a/packages/plugins/plugin-webhooks/src/bootstrap-declared-webhooks.test.ts +++ b/packages/plugins/plugin-webhooks/src/bootstrap-declared-webhooks.test.ts @@ -13,6 +13,7 @@ import { describe, expect, it, vi } from 'vitest'; import { AutoEnqueuer, type HttpEnqueueFn } from './auto-enqueuer.js'; import { bootstrapDeclaredWebhooks } from './bootstrap-declared-webhooks.js'; import { bindWebhookProvenanceStamp } from './webhook-provenance.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // --------------------------------------------------------------------------- // Fakes @@ -65,6 +66,7 @@ class FakeEngine { return typeof q?.limit === 'number' ? out.slice(0, q.limit) : out; } async findOne(name: string, q?: any): Promise { + assertEngineFindOnePredicate(name, q); return (await this.find(name, q))[0] ?? null; } async insert(name: string, data: any): Promise { diff --git a/packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts b/packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts index 68034daf16..ac8cf7f0c7 100644 --- a/packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts +++ b/packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts @@ -39,7 +39,7 @@ import type { RealtimeEventHandler, RealtimeEventPayload, } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { HttpDispatcher, MemoryHttpOutbox, @@ -92,6 +92,7 @@ function makeEngine( const engine: any = { async find() { return rows; }, async findOne(_n: string, opts?: any) { + assertEngineFindOnePredicate(_n, opts); const id = opts?.where?.id; if (id === undefined) return rows[0] ?? null; return rows.find((r) => r.id === id) ?? null; diff --git a/packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts b/packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts index 5041041c52..340f01796b 100644 --- a/packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts +++ b/packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts @@ -26,7 +26,7 @@ import type { RealtimeEventHandler, RealtimeEventPayload, } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { MemoryHttpOutbox, HttpDispatcher, type FetchImpl } from '@objectstack/service-messaging'; import { AutoEnqueuer } from './auto-enqueuer.js'; @@ -65,7 +65,8 @@ class FakeRealtime implements IRealtimeService { function makeEngine(rows: any[]): IDataEngine { return { async find() { return rows; }, - async findOne() { return rows[0] ?? null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return rows[0] ?? null; }, async insert(_n: string, d: any) { return d; }, async update(_n: string, data: any, options?: any) { assertEngineUpdateDispatch(data, options); diff --git a/packages/rest/src/meta-compound-save-force-parity.test.ts b/packages/rest/src/meta-compound-save-force-parity.test.ts index bf02336994..c9d188afbf 100644 --- a/packages/rest/src/meta-compound-save-force-parity.test.ts +++ b/packages/rest/src/meta-compound-save-force-parity.test.ts @@ -80,7 +80,7 @@ import { describe, it, expect, vi } from 'vitest'; // `.js` on purpose — NodeNext resolution requires the extension (#7248). import { RestServer } from './rest-server.js'; -import { assertEngineUpdateDispatch, assertEngineDeleteDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; const META = '/api/v1/meta'; @@ -200,6 +200,7 @@ function boot() { return [...rows.values()].filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; return null; diff --git a/packages/rest/src/meta-compound-save-mode-parity.test.ts b/packages/rest/src/meta-compound-save-mode-parity.test.ts index 3c3d6a0d4a..2a26dbc61e 100644 --- a/packages/rest/src/meta-compound-save-mode-parity.test.ts +++ b/packages/rest/src/meta-compound-save-mode-parity.test.ts @@ -73,7 +73,7 @@ import { describe, it, expect, vi } from 'vitest'; // `.js` on purpose — NodeNext resolution requires the extension (#7248). import { RestServer } from './rest-server.js'; -import { assertEngineUpdateDispatch, assertEngineDeleteDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; const META = '/api/v1/meta'; @@ -185,6 +185,7 @@ function boot() { return [...rows.values()].filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; return null; diff --git a/packages/rest/src/meta-object-extension-agreement.test.ts b/packages/rest/src/meta-object-extension-agreement.test.ts index 34b1a98f64..bd1ba89a5a 100644 --- a/packages/rest/src/meta-object-extension-agreement.test.ts +++ b/packages/rest/src/meta-object-extension-agreement.test.ts @@ -32,7 +32,7 @@ // the anti-vacuity case pins that they ARE discriminated. import { describe, it, expect, vi } from 'vitest'; -import { SchemaRegistry } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -152,7 +152,7 @@ async function measure(opts: { // tenant overlay row is a different layer with its own precedence. Both // routes read this store, so it is held constant across every host. find: async () => [], - findOne: async () => undefined, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return undefined; }, }; const services = new Map(); diff --git a/packages/rest/src/meta-object-extension-property-classes.test.ts b/packages/rest/src/meta-object-extension-property-classes.test.ts index c14bcc348b..c2422193f0 100644 --- a/packages/rest/src/meta-object-extension-property-classes.test.ts +++ b/packages/rest/src/meta-object-extension-property-classes.test.ts @@ -56,7 +56,7 @@ // `meta-object-overlay-extension-fold.test.ts` (#8045, idempotency). import { describe, it, expect, vi } from 'vitest'; -import { SchemaRegistry } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -267,7 +267,7 @@ async function measure(opts: { find: async (table: string, q: { where: Record }) => table === 'sys_metadata' ? rows.filter((r) => matches(r, q.where)) : [], findOne: async (table: string, q: { where: Record }) => - table === 'sys_metadata' ? rows.find((r) => matches(r, q.where)) : undefined, + { assertEngineFindOnePredicate(table, q); return table === 'sys_metadata' ? rows.find((r) => matches(r, q.where)) : undefined; }, }; const services = new Map(); diff --git a/packages/rest/src/meta-object-materialization-agreement.test.ts b/packages/rest/src/meta-object-materialization-agreement.test.ts index ee50af5a1e..e5d98a79cf 100644 --- a/packages/rest/src/meta-object-materialization-agreement.test.ts +++ b/packages/rest/src/meta-object-materialization-agreement.test.ts @@ -92,7 +92,7 @@ // `SchemaRegistry.materializeServedObjectOnto`. import { describe, it, expect, vi } from 'vitest'; -import { SchemaRegistry } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -227,7 +227,7 @@ async function measure(opts: { const engine = { registry, find: async () => [], - findOne: async () => undefined, + findOne: async (object: string, query?: EngineFindOneQueryInput) => { assertEngineFindOnePredicate(object, query); return undefined; }, }; const services = new Map(); diff --git a/packages/rest/src/meta-object-overlay-extension-fold.test.ts b/packages/rest/src/meta-object-overlay-extension-fold.test.ts index 4468f1a2a7..84f12205c4 100644 --- a/packages/rest/src/meta-object-overlay-extension-fold.test.ts +++ b/packages/rest/src/meta-object-overlay-extension-fold.test.ts @@ -39,7 +39,7 @@ // `prefolded` and `bridged` hosts here are what hold that. import { describe, it, expect, vi } from 'vitest'; -import { SchemaRegistry } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -243,7 +243,7 @@ async function measure(opts: { find: async (table: string, q: { where: Record }) => table === 'sys_metadata' ? rows.filter((r) => matches(r, q.where)) : [], findOne: async (table: string, q: { where: Record }) => - table === 'sys_metadata' ? rows.find((r) => matches(r, q.where)) : undefined, + { assertEngineFindOnePredicate(table, q); return table === 'sys_metadata' ? rows.find((r) => matches(r, q.where)) : undefined; }, }; const services = new Map(); diff --git a/packages/rest/src/meta-object-search-companion-agreement.test.ts b/packages/rest/src/meta-object-search-companion-agreement.test.ts index af3cf7ea96..f11ac9c4ea 100644 --- a/packages/rest/src/meta-object-search-companion-agreement.test.ts +++ b/packages/rest/src/meta-object-search-companion-agreement.test.ts @@ -65,7 +65,7 @@ // the companion is genuinely DISCRIMINATED rather than always-present. import { describe, it, expect, vi } from 'vitest'; -import { SchemaRegistry } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -208,7 +208,7 @@ async function measure(opts: { registry, find: async (_t: string, o?: { where?: Record }) => matchesRow(o?.where), findOne: async (_t: string, o?: { where?: Record }) => - matchesRow(o?.where)[0], + { assertEngineFindOnePredicate(_t, o); return matchesRow(o?.where)[0]; }, }; const services = new Map(); diff --git a/packages/rest/src/meta-published-overlay.test.ts b/packages/rest/src/meta-published-overlay.test.ts index 751b99f6f6..b06920199a 100644 --- a/packages/rest/src/meta-published-overlay.test.ts +++ b/packages/rest/src/meta-published-overlay.test.ts @@ -42,7 +42,7 @@ import { describe, it, expect, vi } from 'vitest'; // The producer's OWN write-verb dispatch decisions, so the fake engine below // cannot accept a call ObjectQL itself would refuse — a double looser than the // real engine is how a dead route once shipped with its suite green. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { MetadataManager } from '@objectstack/metadata'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -100,6 +100,7 @@ function makeStubEngine(opts: { failSysMetadataReads?: Error } = {}) { const engine: any = { async findOne(table: string, options: { where: Record }) { + assertEngineFindOnePredicate(table, options); if (table === 'sys_metadata' && opts.failSysMetadataReads) throw opts.failSysMetadataReads; if (table === 'sys_metadata_history') return null; return findRow(options.where)?.row ?? null; diff --git a/packages/rest/src/public-form-lookup-picker.test.ts b/packages/rest/src/public-form-lookup-picker.test.ts index 901f08a2de..88b6eb478a 100644 --- a/packages/rest/src/public-form-lookup-picker.test.ts +++ b/packages/rest/src/public-form-lookup-picker.test.ts @@ -29,7 +29,7 @@ import { describe, expect, it, vi } from 'vitest'; // The engine-double contract (#4434 / #5619): a fake engine's update/delete // must be exactly as strict as ObjectQL's dispatch, or a dead route ships with // its suite green. Both predicates live in metadata-core. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -42,7 +42,8 @@ function stubEngine() { return { rows, engine: { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return rows.slice(); }, async insert(table: string, data: Record) { if (table === 'sys_metadata_audit') return { id: 'audit_skip' }; diff --git a/packages/rest/src/public-form-routes.stored-row.test.ts b/packages/rest/src/public-form-routes.stored-row.test.ts index d0d9e7a82a..73c415dcf3 100644 --- a/packages/rest/src/public-form-routes.stored-row.test.ts +++ b/packages/rest/src/public-form-routes.stored-row.test.ts @@ -55,7 +55,7 @@ import { describe, expect, it, vi } from 'vitest'; // depends in this direction, and that reverse edge is a cycle turbo refuses. import { assertEngineDeleteDispatch, - assertEngineUpdateDispatch, + assertEngineUpdateDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput, } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; // `.js` extension required under `moduleResolution: nodenext`. Without it the @@ -73,7 +73,8 @@ function stubEngine() { return { rows, engine: { - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async find() { return rows.slice(); }, async insert(table: string, data: Record) { if (table === 'sys_metadata_audit') return { id: 'audit_skip' }; diff --git a/packages/rest/src/rest-server-meta-read-org-scope.test.ts b/packages/rest/src/rest-server-meta-read-org-scope.test.ts index 752c48ca64..22f0e705ac 100644 --- a/packages/rest/src/rest-server-meta-read-org-scope.test.ts +++ b/packages/rest/src/rest-server-meta-read-org-scope.test.ts @@ -43,7 +43,7 @@ // org exists here for no other purpose. import { describe, it, expect, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { RestServer } from './rest-server.js'; @@ -151,6 +151,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') return null; return findRow(opts.where)?.row ?? null; }, diff --git a/packages/runtime/src/action-execution-calldata-batch-retired.test.ts b/packages/runtime/src/action-execution-calldata-batch-retired.test.ts index 8abb67b858..9db6e41b1e 100644 --- a/packages/runtime/src/action-execution-calldata-batch-retired.test.ts +++ b/packages/runtime/src/action-execution-calldata-batch-retired.test.ts @@ -61,6 +61,7 @@ import { ApiEndpointSchema } from '@objectstack/spec/api'; import { callData, type ActionExecutionDeps } from './action-execution.js'; import { HttpDispatcher, type HttpProtocolContext } from './http-dispatcher.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; const EC = { userId: 'u1', isSystem: false, positions: [], permissions: [] } as any; /** [#5155] Every service lookup resolves off the REQUEST's kernel. */ @@ -87,7 +88,7 @@ function engine(store = rows()) { const id = bag?.where?.id; return id == null ? [...store] : store.filter((r) => r.id === String(id)); }, - findOne: async (_o: string, opts: any) => store.find((r) => r.id === String(opts?.where?.id)) ?? null, + findOne: async (_o: string, opts: any) => { assertEngineFindOnePredicate(_o, opts); return store.find((r) => r.id === String(opts?.where?.id)) ?? null; }, } as any; } diff --git a/packages/runtime/src/action-execution-calldata-not-found.test.ts b/packages/runtime/src/action-execution-calldata-not-found.test.ts index 1981ee3fea..a95697d081 100644 --- a/packages/runtime/src/action-execution-calldata-not-found.test.ts +++ b/packages/runtime/src/action-execution-calldata-not-found.test.ts @@ -53,7 +53,7 @@ import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protoco // mirroring it) is what makes that impossible. `@objectstack/objectql` is // already a `dependencies` entry of `@objectstack/runtime`, so no manifest // change is needed to reach it. -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { ApiEndpointSchema, DeleteDataResponseSchema } from '@objectstack/spec/api'; import type { ApiEndpoint } from '@objectstack/spec/api'; @@ -125,7 +125,7 @@ function fallbackHarness(store = rows()) { function protocolHarness(store = rows()) { const engine: any = { registry: { getObject: (n: string) => (n === 'task' ? SCHEMA : undefined) }, - findOne: async (_o: string, opts: any) => store.get(String(opts?.where?.id)) ?? null, + findOne: async (_o: string, opts: any) => { assertEngineFindOnePredicate(_o, opts); return store.get(String(opts?.where?.id)) ?? null; }, find: async () => [...store.values()], update: async (_o: string, data: any, opts: any) => { const id = String(opts?.where?.id); diff --git a/packages/runtime/src/domains/meta-published-runtime-publish.test.ts b/packages/runtime/src/domains/meta-published-runtime-publish.test.ts index 087a66b3fa..14ad781f07 100644 --- a/packages/runtime/src/domains/meta-published-runtime-publish.test.ts +++ b/packages/runtime/src/domains/meta-published-runtime-publish.test.ts @@ -33,7 +33,7 @@ import { describe, it, expect } from 'vitest'; // from `@objectstack/metadata-core` rather than `@objectstack/objectql` // (which re-exports it): objectql depends on this side of the graph, so that // import would close a cycle turbo rejects outright. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { MetadataManager } from '@objectstack/metadata'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { HttpDispatcher } from '../http-dispatcher.js'; @@ -89,6 +89,7 @@ function makeStubEngine() { const engine: any = { async findOne(table: string, opts: { where: Record }) { + assertEngineFindOnePredicate(table, opts); if (table === 'sys_metadata_history') return null; return findRow(opts.where)?.row ?? null; }, diff --git a/packages/runtime/src/domains/meta-save-destructive-remedy.test.ts b/packages/runtime/src/domains/meta-save-destructive-remedy.test.ts index 3499504dd8..fcd85cdf7f 100644 --- a/packages/runtime/src/domains/meta-save-destructive-remedy.test.ts +++ b/packages/runtime/src/domains/meta-save-destructive-remedy.test.ts @@ -69,7 +69,7 @@ */ import { describe, it, expect } from 'vitest'; -import { assertEngineUpdateDispatch, assertEngineDeleteDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { HttpDispatcher } from '../http-dispatcher.js'; @@ -152,6 +152,7 @@ function boot() { return [...rows.values()].filter((r) => match(r, o?.where ?? {})); }, async findOne(table: string, o: { where: Record }) { + assertEngineFindOnePredicate(table, o); if (table !== 'sys_metadata') return null; for (const r of rows.values()) if (match(r, o?.where ?? {})) return r; return null; diff --git a/packages/runtime/src/meta-compound-arity-mint-door.test.ts b/packages/runtime/src/meta-compound-arity-mint-door.test.ts index 2785baa494..a249d2af57 100644 --- a/packages/runtime/src/meta-compound-arity-mint-door.test.ts +++ b/packages/runtime/src/meta-compound-arity-mint-door.test.ts @@ -46,7 +46,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; // [#5619] The producer's OWN write-verb dispatch decisions, so this double // cannot accept a call the real ObjectQL engine would refuse. Imported from // `@objectstack/metadata-core` and never `@objectstack/objectql`. -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { HttpDispatcher } from './http-dispatcher.js'; import type { HttpDispatcherResult } from './http-dispatcher.js'; @@ -130,6 +130,7 @@ function makeEngine() { return tableOf(table).filter((r) => matches(r, opts?.where)); }, async findOne(table: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(table, opts); return tableOf(table).find((r) => matches(r, opts?.where)) ?? null; }, async insert(table: string, data: Record) { diff --git a/packages/runtime/src/meta-field-overlay-lock.test.ts b/packages/runtime/src/meta-field-overlay-lock.test.ts index 590d1c40f0..9bc3c5be74 100644 --- a/packages/runtime/src/meta-field-overlay-lock.test.ts +++ b/packages/runtime/src/meta-field-overlay-lock.test.ts @@ -98,7 +98,7 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { HttpDispatcher } from './http-dispatcher.js'; import type { HttpDispatcherResult } from './http-dispatcher.js'; @@ -281,6 +281,7 @@ function makeEngine() { return tableOf(table).filter((r) => matches(r, opts?.where)); }, async findOne(table: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(table, opts); return tableOf(table).find((r) => matches(r, opts?.where)) ?? null; }, async insert(table: string, data: Record) { diff --git a/packages/runtime/src/meta-overlay-read-your-writes.test.ts b/packages/runtime/src/meta-overlay-read-your-writes.test.ts index 92addbca46..13e5f1dd01 100644 --- a/packages/runtime/src/meta-overlay-read-your-writes.test.ts +++ b/packages/runtime/src/meta-overlay-read-your-writes.test.ts @@ -45,7 +45,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ObjectStackProtocolImplementation, resetEnvWritableMetadataTypes } from '@objectstack/metadata-protocol'; -import { SchemaRegistry, assertEngineUpdateDispatch, assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { SchemaRegistry, assertEngineUpdateDispatch, assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { resolveRouteActionDeclaration, type ActionExecutionDeps } from './action-execution.js'; /** @@ -65,7 +65,7 @@ function makeEngine(registry: SchemaRegistry) { const engine: any = { registry, find: vi.fn(async (_table: string, opts: any) => rows.filter((r) => matches(r, opts?.where ?? {}))), - findOne: vi.fn(async (table: string, opts: any) => (await engine.find(table, opts))[0] ?? null), + findOne: vi.fn(async (table: string, opts: any) => { assertEngineFindOnePredicate(table, opts); return (await engine.find(table, opts))[0] ?? null; }), insert: vi.fn(async (_table: string, data: any) => { const row = { id: data.id ?? `row_${nextId++}`, ...data }; rows.push(row); diff --git a/packages/runtime/src/meta-write-org-scope.test.ts b/packages/runtime/src/meta-write-org-scope.test.ts index 1ee53bded2..54c732a023 100644 --- a/packages/runtime/src/meta-write-org-scope.test.ts +++ b/packages/runtime/src/meta-write-org-scope.test.ts @@ -68,7 +68,7 @@ import { // REST `/meta` write doors share it. This suite still drives the DISPATCHER // through the real stack — that is why it stays in this package. declaresOrgOverride, - organizationIdForMetaWrite, + organizationIdForMetaWrite, assertEngineFindOnePredicate, } from '@objectstack/metadata-core'; import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol'; import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel'; @@ -161,6 +161,7 @@ function makeEngine() { return tableOf(table).filter((r) => matches(r, opts?.where)); }, async findOne(table: string, opts?: { where?: Record }) { + assertEngineFindOnePredicate(table, opts); return tableOf(table).find((r) => matches(r, opts?.where)) ?? null; }, async insert(table: string, data: Record) { diff --git a/packages/runtime/src/seed-loader.test.ts b/packages/runtime/src/seed-loader.test.ts index 6d5ce022f7..48d4984760 100644 --- a/packages/runtime/src/seed-loader.test.ts +++ b/packages/runtime/src/seed-loader.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { SeedLoaderService } from './seed-loader'; import type { IDataEngine, IMetadataService } from '@objectstack/spec/contracts'; import type { SeedLoaderRequest, SeedLoaderConfig } from '@objectstack/spec/data'; -import { assertEngineDeleteDispatch, assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; // ========================================================================== // Mock Helpers @@ -41,6 +41,7 @@ function createMockEngine(data: Record = {}): IDataEngine { return records; }), findOne: vi.fn(async (objectName: string, query?: any) => { + assertEngineFindOnePredicate(objectName, query); const results = await (store[objectName] || []); return results[0] || null; }), diff --git a/packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts b/packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts index 7886b2740a..10c26eacfc 100644 --- a/packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts +++ b/packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts @@ -43,7 +43,7 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { AutomationEngine } from '../engine.js'; import { registerCrudNodes } from './crud-nodes.js'; @@ -82,7 +82,7 @@ function pinnedFakeData() { const calls: { update?: any; delete?: any } = {}; const service = { find: async (_object: string, options: any) => rows.filter((r) => matches(r, options?.where)), - findOne: async (_object: string, options: any) => rows.find((r) => matches(r, options?.where)) ?? null, + findOne: async (_object: string, options: any) => { assertEngineFindOnePredicate(_object, options); return rows.find((r) => matches(r, options?.where)) ?? null; }, insert: async (_object: string, data: any) => ({ id: 'new', ...data }), update: async (_object: string, data: any, options: any) => { calls.update = options; diff --git a/packages/services/service-automation/src/builtin/crud-config-aliases.test.ts b/packages/services/service-automation/src/builtin/crud-config-aliases.test.ts index 5716e84afa..3c796fc7d7 100644 --- a/packages/services/service-automation/src/builtin/crud-config-aliases.test.ts +++ b/packages/services/service-automation/src/builtin/crud-config-aliases.test.ts @@ -23,6 +23,7 @@ import { describe, it, expect } from 'vitest'; import { normalizeStackInput } from '@objectstack/spec'; import { AutomationEngine } from '../engine.js'; import { registerCrudNodes } from './crud-nodes.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function silentLogger(): any { const l: any = { info() {}, warn() {}, error() {}, debug() {} }; @@ -34,7 +35,8 @@ function fakeData() { const calls: Array<{ op: string; obj: string; opts?: any }> = []; const data: any = { async find(obj: string, opts: any) { calls.push({ op: 'find', obj, opts }); return [{ id: 'r1' }]; }, - async findOne(obj: string, opts: any) { calls.push({ op: 'findOne', obj, opts }); return { id: 'r1' }; }, + async findOne(obj: string, opts: any) { + assertEngineFindOnePredicate(obj, opts); calls.push({ op: 'findOne', obj, opts }); return { id: 'r1' }; }, async insert(obj: string, fields: any) { calls.push({ op: 'insert', obj, opts: { fields } }); return { id: `${obj}_1`, ...fields }; }, async update(obj: string, fields: any, opts: any) { calls.push({ op: 'update', obj, opts: { ...opts, fields } }); return { ok: true }; }, async delete(obj: string, opts: any) { calls.push({ op: 'delete', obj, opts }); return { ok: true }; }, diff --git a/packages/services/service-automation/src/builtin/crud-filter-guard.test.ts b/packages/services/service-automation/src/builtin/crud-filter-guard.test.ts index cfe292cb2b..509031c8c8 100644 --- a/packages/services/service-automation/src/builtin/crud-filter-guard.test.ts +++ b/packages/services/service-automation/src/builtin/crud-filter-guard.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { AutomationEngine } from '../engine.js'; import { registerCrudNodes } from './crud-nodes.js'; import { interpolateFilter } from './template.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * framework#3810 — a flow filter must never silently widen. @@ -30,7 +31,8 @@ function fakeData() { const seen: { find?: any; update?: any; delete?: any } = {}; const service = { find: async (_o: string, q: any) => { seen.find = q.where; return []; }, - findOne: async (_o: string, q: any) => { seen.find = q.where; return null; }, + findOne: async (_o: string, q: any) => { + assertEngineFindOnePredicate(_o, q); seen.find = q.where; return null; }, update: async (_o: string, _d: any, o: any) => { seen.update = o?.where; return { modified: 1 }; }, delete: async (_o: string, o: any) => { seen.delete = o?.where; return { deleted: 1 }; }, getObject: () => ({ name: 'deal', fields: {} }), diff --git a/packages/services/service-automation/src/builtin/crud-output-var.test.ts b/packages/services/service-automation/src/builtin/crud-output-var.test.ts index a804261360..dd541202d8 100644 --- a/packages/services/service-automation/src/builtin/crud-output-var.test.ts +++ b/packages/services/service-automation/src/builtin/crud-output-var.test.ts @@ -9,6 +9,7 @@ import { describe, it, expect } from 'vitest'; import { AutomationEngine } from '../engine.js'; import { registerCrudNodes } from './crud-nodes.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; function makeLogger(): any { const l: any = { info() {}, warn() {}, error() {}, debug() {} }; @@ -23,7 +24,8 @@ function fakeData() { async insert(obj: string, fields: any) { n += 1; return { id: `${obj}_${n}`, ...fields }; }, async update(obj: string, fields: any, opts: any) { updates.push({ obj, fields, opts }); return { ok: true }; }, async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, }; return { data, updates }; } diff --git a/packages/services/service-automation/src/builtin/crud-runas.test.ts b/packages/services/service-automation/src/builtin/crud-runas.test.ts index e88ec563ba..ecbc8dfba5 100644 --- a/packages/services/service-automation/src/builtin/crud-runas.test.ts +++ b/packages/services/service-automation/src/builtin/crud-runas.test.ts @@ -21,6 +21,7 @@ import { UnscopedRunDataAccessError, } from '../runtime-identity.js'; import type { AutomationContext } from '@objectstack/spec/contracts'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function makeLogger(): any { const l: any = { info() {}, warn() {}, error() {}, debug() {} }; @@ -33,7 +34,8 @@ function fakeData() { const calls: Array<{ op: string; obj: string; ctx: any }> = []; const data: any = { async find(obj: string, q: any) { calls.push({ op: 'find', obj, ctx: q?.context }); return [{ id: 'r1' }]; }, - async findOne(obj: string, q: any) { calls.push({ op: 'findOne', obj, ctx: q?.context }); return { id: 'r1' }; }, + async findOne(obj: string, q: any) { + assertEngineFindOnePredicate(obj, q); calls.push({ op: 'findOne', obj, ctx: q?.context }); return { id: 'r1' }; }, async insert(obj: string, _f: any, opts: any) { calls.push({ op: 'insert', obj, ctx: opts?.context }); return { id: `${obj}_1` }; }, async update(obj: string, _f: any, opts: any) { calls.push({ op: 'update', obj, ctx: opts?.context }); return { ok: true }; }, async delete(obj: string, opts: any) { calls.push({ op: 'delete', obj, ctx: opts?.context }); return { ok: true }; }, diff --git a/packages/services/service-automation/src/builtin/notify-organization-stamp.integration.test.ts b/packages/services/service-automation/src/builtin/notify-organization-stamp.integration.test.ts index f404afa4b1..8849129ca8 100644 --- a/packages/services/service-automation/src/builtin/notify-organization-stamp.integration.test.ts +++ b/packages/services/service-automation/src/builtin/notify-organization-stamp.integration.test.ts @@ -12,6 +12,7 @@ import { import { AutomationEngine } from '../engine.js'; import { registerNotifyNode } from './notify-node.js'; import type { MessagingServiceSurface } from './notify-node.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * [#11303] The `notify` node is the producer that decides whether the whole @@ -76,7 +77,8 @@ function capturingEngine(): { engine: any; written: WrittenRow[] } { return { ...row, id }; }, async find() { return []; }, - async findOne() { return undefined; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return undefined; }, }; return { engine, written }; } diff --git a/packages/services/service-automation/src/record-lookup-expand.integration.test.ts b/packages/services/service-automation/src/record-lookup-expand.integration.test.ts index 184f79daa0..511b37c533 100644 --- a/packages/services/service-automation/src/record-lookup-expand.integration.test.ts +++ b/packages/services/service-automation/src/record-lookup-expand.integration.test.ts @@ -17,6 +17,7 @@ import { describe, it, expect } from 'vitest'; import { LiteKernel } from '@objectstack/core'; import { AutomationServicePlugin } from './plugin.js'; import { AutomationEngine } from './engine.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; interface CrudCall { op: string; obj: string; ctx: any; expand?: any; fields?: any } @@ -33,6 +34,7 @@ function fakeObjectQl(opts: { throwOnFindOne?: boolean } = {}) { const engine: any = { async find(object: string, o: any) { crud.push({ op: 'find', obj: object, ctx: o?.context }); return []; }, async findOne(object: string, o: any) { + assertEngineFindOnePredicate(object, o); crud.push({ op: 'findOne', obj: object, ctx: o?.context, expand: o?.expand }); if (opts.throwOnFindOne) throw new Error('boom'); return object === 'lead' ? { ...LEAD } : undefined; diff --git a/packages/services/service-automation/src/run-summary.test.ts b/packages/services/service-automation/src/run-summary.test.ts index e6251dbaf8..62f0979104 100644 --- a/packages/services/service-automation/src/run-summary.test.ts +++ b/packages/services/service-automation/src/run-summary.test.ts @@ -8,7 +8,7 @@ // all read. import { describe, it, expect } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/objectql'; import { AutomationEngine } from './engine.js'; import type { StepLogEntry, NodeExecutor, RunRecord } from './engine.js'; import { summarizeRun, formatRunSummaryLine } from './run-summary.js'; @@ -200,7 +200,8 @@ function sweepEngine(rows: Array>, logger = makeLogger() let seq = 0; const data: any = { async find() { return rows; }, - async findOne() { return rows[0] ?? null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return rows[0] ?? null; }, async insert(obj: string, fields: any) { seq += 1; const r = { id: `${obj}_${seq}`, ...fields }; written.push(r); return r; }, async update() { return 0; }, async delete() { return 0; }, @@ -396,7 +397,8 @@ describe('node executors report what they touched', () => { const logger = makeLogger(); const data: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert(_o: string, f: any) { return { id: 'x', ...f }; }, async update() { return 7; }, // driver.updateMany contract: Promise async delete() { return 0; }, @@ -501,7 +503,8 @@ describe('node executors report what they touched', () => { const written: any[] = []; const data: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert(o: string, f: any) { const r = { id: `${o}_1`, ...f }; written.push(r); return r; }, }; const ctx: any = { logger, getService: (n: string) => (n === 'data' ? data : undefined) }; @@ -544,7 +547,8 @@ describe('map rolls its per-item child runs up', () => { const written: any[] = []; const data: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert(o: string, f: any) { const r = { id: `${o}_${written.length + 1}`, ...f }; written.push(r); return r; }, }; const ctx: any = { logger, getService: (n: string) => (n === 'data' ? data : undefined) }; @@ -594,7 +598,8 @@ describe('a child run that PAUSED still counts toward its parent', () => { const written: any[] = []; const data: any = { async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert(o: string, f: any) { const r = { id: `${o}_${written.length + 1}`, ...f }; written.push(r); return r; }, }; const ctx: any = { logger, getService: (n: string) => (n === 'data' ? data : undefined) }; diff --git a/packages/services/service-automation/src/runas-grant-resolution.integration.test.ts b/packages/services/service-automation/src/runas-grant-resolution.integration.test.ts index fc55d8fda2..c0b2c88674 100644 --- a/packages/services/service-automation/src/runas-grant-resolution.integration.test.ts +++ b/packages/services/service-automation/src/runas-grant-resolution.integration.test.ts @@ -18,6 +18,7 @@ import { describe, it, expect } from 'vitest'; import { LiteKernel } from '@objectstack/core'; import { AutomationServicePlugin } from './plugin.js'; import { AutomationEngine } from './engine.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * A fake ObjectQL engine that both serves the authz tables (for grant @@ -35,7 +36,8 @@ function fakeObjectQl(tables: Record) { ); const engine: any = { async find(object: string, opts: any) { crud.push({ op: 'find', obj: object, ctx: opts?.context }); return match(object, opts?.where); }, - async findOne(object: string, opts: any) { crud.push({ op: 'findOne', obj: object, ctx: opts?.context }); return match(object, opts?.where)[0]; }, + async findOne(object: string, opts: any) { + assertEngineFindOnePredicate(object, opts); crud.push({ op: 'findOne', obj: object, ctx: opts?.context }); return match(object, opts?.where)[0]; }, async insert(object: string, _f: any, opts: any) { crud.push({ op: 'insert', obj: object, ctx: opts?.context }); return { id: `${object}_1` }; }, async update(object: string, _f: any, opts: any) { crud.push({ op: 'update', obj: object, ctx: opts?.context }); return { ok: true }; }, async delete(object: string, opts: any) { crud.push({ op: 'delete', obj: object, ctx: opts?.context }); return { ok: true }; }, diff --git a/packages/services/service-datasource/package.json b/packages/services/service-datasource/package.json index a2a42d714e..b070dde947 100644 --- a/packages/services/service-datasource/package.json +++ b/packages/services/service-datasource/package.json @@ -36,6 +36,7 @@ "@objectstack/driver-mongodb": "workspace:*", "@objectstack/driver-sql": "workspace:*", "@objectstack/driver-sqlite-wasm": "workspace:*", + "@objectstack/metadata-core": "workspace:*", "@objectstack/plugin-hono-server": "workspace:*", "@types/node": "^26.2.0", "tsup": "^8.5.1", diff --git a/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts b/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts index 8d4fab9c7e..09ed5ff535 100644 --- a/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts +++ b/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts @@ -14,6 +14,7 @@ import { // `scripts/check-test-source-alias.mjs` (the clocked-window rule) and #10115 / PR #10120, // where the same shape cost 30 ejected merge-queue builds in one night. import '@objectstack/spec/kernel'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Minimal PluginContext + in-memory metadata service. Boots the plugin and @@ -321,6 +322,7 @@ describe('DatasourceAdminServicePlugin: runtime datasource durability', () => { registerDatasourceDef() {}, getDriverByName() { return undefined; }, findOne: async (_o: string, q: { where?: Record }) => { + assertEngineFindOnePredicate(_o, q); const w = q.where ?? {}; return rows.find((r) => Object.entries(w).every(([k, v]) => { if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`); return r[k] === v; })); }, diff --git a/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts b/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts index feffb3e028..0648abb349 100644 --- a/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts +++ b/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts @@ -47,7 +47,7 @@ import { describe, it, expect, afterEach } from 'vitest'; import { createHmac } from 'node:crypto'; -import { ObjectQL, assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { ObjectQL, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { SqlDriver } from '@objectstack/driver-sql'; import type { IDataEngine } from '@objectstack/spec/contracts'; import { SqlHttpOutbox } from './sql-http-outbox.js'; @@ -288,7 +288,7 @@ describe('sys_http_delivery — authored headers vs the data API (#8118)', () => // its authored configuration. const noAccessor = { find: (o: string, q: unknown) => (eng as any).find(o, q), - findOne: (o: string, q: unknown) => (eng as any).findOne(o, q), + findOne: (o: string, q: unknown) => { assertEngineFindOnePredicate(o, q); return (eng as any).findOne(o, q); }, insert: (o: string, d: unknown) => (eng as any).insert(o, d), update: (o: string, d: unknown, q: unknown) => { // Engine-double contract: this fake's update() must be exactly diff --git a/packages/services/service-messaging/src/email-channel.test.ts b/packages/services/service-messaging/src/email-channel.test.ts index f531e3a209..284d7fa430 100644 --- a/packages/services/service-messaging/src/email-channel.test.ts +++ b/packages/services/service-messaging/src/email-channel.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect } from 'vitest'; import { createEmailChannel } from './email-channel.js'; import { NotificationTemplateStore } from './template-renderer.js'; import type { Delivery } from './channel.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function silentCtx() { return { logger: { info: () => {}, warn: () => {}, error: () => {} } }; @@ -32,6 +33,7 @@ function fakeData(opts: { users?: Record; templates?: any[] } = const templates = opts.templates ?? []; return { async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); const w = query?.where ?? {}; if (object === 'sys_user') { const email = users[String(w.id)]; diff --git a/packages/services/service-messaging/src/inbox-channel.test.ts b/packages/services/service-messaging/src/inbox-channel.test.ts index fcff1912fd..7390e5dc12 100644 --- a/packages/services/service-messaging/src/inbox-channel.test.ts +++ b/packages/services/service-messaging/src/inbox-channel.test.ts @@ -3,6 +3,7 @@ import { describe, it, expect } from 'vitest'; import { createInboxChannel, INBOX_OBJECT, RECEIPT_OBJECT } from './inbox-channel.js'; import type { Delivery } from './channel.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function silentCtx() { return { logger: { info: () => {}, warn: () => {}, error: () => {} } }; @@ -41,6 +42,7 @@ function fakeData( }, async find() { return []; }, async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); findOnes.push({ object, query }); return findOneImpl ? findOneImpl(object, query) : null; }, diff --git a/packages/services/service-messaging/src/messaging-service-plugin.test.ts b/packages/services/service-messaging/src/messaging-service-plugin.test.ts index 86f015a49f..c97371ccd7 100644 --- a/packages/services/service-messaging/src/messaging-service-plugin.test.ts +++ b/packages/services/service-messaging/src/messaging-service-plugin.test.ts @@ -3,6 +3,7 @@ import { describe, it, expect } from 'vitest'; import { MessagingServicePlugin } from './messaging-service-plugin.js'; import type { MessagingService } from './messaging-service.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; /** * A lightweight fake PluginContext — enough surface for the plugin's init(): @@ -26,7 +27,8 @@ function fakeCtx() { return { id: `row_${inserts.length}`, ...row }; }, async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async update() { return {}; }, async delete() { return {}; }, }); @@ -109,7 +111,8 @@ function provisionCtx() { return [] as unknown[]; }, async insert() { return {}; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async update() { return {}; }, async delete() { return {}; }, }; diff --git a/packages/services/service-messaging/src/messaging-service.test.ts b/packages/services/service-messaging/src/messaging-service.test.ts index 32c2288004..e85f405327 100644 --- a/packages/services/service-messaging/src/messaging-service.test.ts +++ b/packages/services/service-messaging/src/messaging-service.test.ts @@ -5,6 +5,7 @@ import { MessagingService } from './messaging-service.js'; import { InboxCallerError } from './inbox-caller.js'; import { MemoryNotificationOutbox } from './memory-outbox.js'; import type { Delivery, MessagingChannel, SendResult } from './channel.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; function silentLogger() { return { info: () => {}, warn: () => {}, error: () => {} }; @@ -42,6 +43,7 @@ function fakeData(findOneImpl?: (obj: string, q: any) => any) { }, async find() { return []; }, async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); findOnes.push({ object, query }); return findOneImpl ? findOneImpl(object, query) : null; }, @@ -182,7 +184,8 @@ describe('MessagingService', () => { if (object === 'sys_team_member') return [{ user_id: 'u_sales' }]; return []; }, - async findOne(object: string) { + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return object === 'lead' ? { id: 'l1', owner_id: 'u_owner' } : null; }, async update() { return {}; }, @@ -236,7 +239,8 @@ describe('MessagingService', () => { } return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async update() { return {}; }, async delete() { return {}; }, async count() { return 0; }, @@ -390,7 +394,8 @@ describe('MessagingService', () => { return { id: 'row' }; }, async find() { return []; }, - async findOne(object: string) { + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); if (object !== 'sys_notification') return null; // First call = the fast-path miss; second = post-conflict lookup finds the winner. if (firstLookup) { firstLookup = false; return null; } @@ -422,7 +427,8 @@ describe('MessagingService', () => { const engine = { async insert() { throw new Error('disk full'); }, async find() { return []; }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async update() { return {}; }, async delete() { return {}; }, async count() { return 0; }, @@ -465,6 +471,7 @@ function inboxEngine(seed: { inbox?: any[]; receipts?: any[] } = {}) { return typeof query.limit === 'number' ? rows.slice(0, query.limit) : rows; }, async findOne(object: string, query: any = {}) { + assertEngineFindOnePredicate(object, query); return (store[object] ?? []).find((r) => matches(r, query.where)) ?? null; }, async insert(object: string, row: any) { diff --git a/packages/services/service-messaging/src/notification-schema-conformance.test.ts b/packages/services/service-messaging/src/notification-schema-conformance.test.ts index ba56ef30d1..8b699b04b5 100644 --- a/packages/services/service-messaging/src/notification-schema-conformance.test.ts +++ b/packages/services/service-messaging/src/notification-schema-conformance.test.ts @@ -52,7 +52,7 @@ // actually receives in production. import { describe, it, expect } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/metadata-core'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core'; import { ListNotificationsResponseSchema, MarkNotificationsReadResponseSchema, @@ -120,6 +120,7 @@ function inboxEngine() { return typeof query.limit === 'number' ? rows.slice(0, query.limit) : rows; }, async findOne(object: string, query: any = {}) { + assertEngineFindOnePredicate(object, query); return (store[object] ?? []).find((r) => matches(r, query.where)) ?? null; }, async insert(object: string, row: any) { diff --git a/packages/services/service-messaging/src/preference-resolver.test.ts b/packages/services/service-messaging/src/preference-resolver.test.ts index f2c3df710b..451830ee34 100644 --- a/packages/services/service-messaging/src/preference-resolver.test.ts +++ b/packages/services/service-messaging/src/preference-resolver.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'; import { PreferenceResolver, quietHoursDeferral, digestDeferral } from './preference-resolver.js'; +import { assertEngineFindOnePredicate, type EngineFindOneQueryInput } from '@objectstack/metadata-core'; function silentLogger() { return { info: () => {}, warn: () => {}, error: () => {} }; @@ -26,7 +27,8 @@ function fakeData(rows: any[] = [], opts: { throwOnFind?: boolean } = {}) { (w.organization_id == null || r.organization_id === w.organization_id), ); }, - async findOne() { return null; }, + async findOne(object: string, query?: EngineFindOneQueryInput) { + assertEngineFindOnePredicate(object, query); return null; }, async insert(_o: string, r: any) { return { id: 'x', ...r }; }, async update() { return {}; }, async delete() { return {}; }, diff --git a/packages/services/service-messaging/src/read-receipt-organization.test.ts b/packages/services/service-messaging/src/read-receipt-organization.test.ts index 62e0926b15..b3e9f50091 100644 --- a/packages/services/service-messaging/src/read-receipt-organization.test.ts +++ b/packages/services/service-messaging/src/read-receipt-organization.test.ts @@ -3,6 +3,7 @@ import { describe, it, expect } from 'vitest'; import { MessagingService, NOTIFICATION_EVENT_OBJECT } from './messaging-service.js'; import { RECEIPT_OBJECT } from './inbox-channel.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * [#11303] The SECOND `sys_notification_receipt` producer. @@ -44,6 +45,7 @@ function engineWithNotification(notificationOrg: string | null) { return { ...row, id: 'rec_1' }; }, async findOne(object: string, opts: any) { + assertEngineFindOnePredicate(object, opts); if (object === NOTIFICATION_EVENT_OBJECT) { return { id: 'evt_pin', organization_id: notificationOrg }; } diff --git a/packages/services/service-messaging/src/recipient-resolver.test.ts b/packages/services/service-messaging/src/recipient-resolver.test.ts index ea7d79fec9..59f69c025c 100644 --- a/packages/services/service-messaging/src/recipient-resolver.test.ts +++ b/packages/services/service-messaging/src/recipient-resolver.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'; import { RecipientResolver } from './recipient-resolver.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function silentLogger() { return { warn: () => {}, info: () => {} }; @@ -36,6 +37,7 @@ function fakeData(opts: { return []; }, async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); calls.push({ method: 'findOne', object, query }); if (object === opts.throwOn) throw new Error('locked'); if (object === 'sys_user') { diff --git a/packages/services/service-messaging/src/sms-channel.test.ts b/packages/services/service-messaging/src/sms-channel.test.ts index 2ca457054e..67adb3f292 100644 --- a/packages/services/service-messaging/src/sms-channel.test.ts +++ b/packages/services/service-messaging/src/sms-channel.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect } from 'vitest'; import { createSmsChannel } from './sms-channel.js'; import { NotificationTemplateStore } from './template-renderer.js'; import type { Delivery } from './channel.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; function silentCtx() { return { logger: { info: () => {}, warn: () => {}, error: () => {} } }; @@ -32,6 +33,7 @@ function fakeData(opts: { users?: Record; templates?: any[] } = const templates = opts.templates ?? []; return { async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); const w = query?.where ?? {}; if (object === 'sys_user') { const phone = users[String(w.id)]; diff --git a/packages/services/service-messaging/src/sql-outbox-audit-columns.test.ts b/packages/services/service-messaging/src/sql-outbox-audit-columns.test.ts index c25802ca70..12072720d2 100644 --- a/packages/services/service-messaging/src/sql-outbox-audit-columns.test.ts +++ b/packages/services/service-messaging/src/sql-outbox-audit-columns.test.ts @@ -20,6 +20,7 @@ import { describe, it, expect } from 'vitest'; import type { IDataEngine } from '@objectstack/spec/contracts'; import { SqlNotificationOutbox } from './sql-outbox.js'; import { SqlHttpOutbox } from './sql-http-outbox.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; interface RecordedUpdate { object: string; @@ -65,6 +66,7 @@ function makeEngine(findResults: Array>> = []) { return findResults[findCall++] ?? []; }, async findOne(_object: string, opts?: { fields?: string[] }) { + assertEngineFindOnePredicate(_object, opts); // `ack()` reads the row it is completing; `enqueue()` probes for a // dedup winner and must miss so the insert path runs. if (opts?.fields?.includes('attempts')) return { ...row }; diff --git a/packages/services/service-messaging/src/template-renderer.test.ts b/packages/services/service-messaging/src/template-renderer.test.ts index d3feb61534..b8efd1f448 100644 --- a/packages/services/service-messaging/src/template-renderer.test.ts +++ b/packages/services/service-messaging/src/template-renderer.test.ts @@ -6,6 +6,7 @@ import { renderNotification, NotificationTemplateStore, } from './template-renderer.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; describe('interpolate', () => { it('substitutes {{ path.to.value }} from the context, linearly', () => { @@ -69,6 +70,7 @@ describe('NotificationTemplateStore', () => { queries, engine: { async findOne(object: string, query: any) { + assertEngineFindOnePredicate(object, query); queries.push({ object, where: query?.where }); const w = query?.where ?? {}; return ( diff --git a/packages/services/service-storage/src/attachment-access-hooks.test.ts b/packages/services/service-storage/src/attachment-access-hooks.test.ts index e103447b22..4a3117fe77 100644 --- a/packages/services/service-storage/src/attachment-access-hooks.test.ts +++ b/packages/services/service-storage/src/attachment-access-hooks.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { describe, it, expect, vi } from 'vitest'; -import { ObjectQL } from '@objectstack/objectql'; +import { ObjectQL, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { installAttachmentAccessHooks, type AttachmentSharingLike } from './attachment-access-hooks.js'; import type { AttachmentLifecycleEngine } from './attachment-lifecycle.js'; @@ -24,9 +24,9 @@ function install(opts: { return typeof options?.limit === 'number' ? rows.slice(0, options.limit) : rows; }, findOne: async (_object, options: any) => - (opts.attachments ?? []).find((r) => + { assertEngineFindOnePredicate(_object, options); return (opts.attachments ?? []).find((r) => Object.entries(options?.where ?? {}).every(([k, v]) => { if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`); return r[k] === v; }), - ) ?? null, + ) ?? null; }, update: async () => ({}), }; installAttachmentAccessHooks(engine, () => opts.sharing, silentLogger()); diff --git a/packages/services/service-storage/src/attachment-lifecycle.test.ts b/packages/services/service-storage/src/attachment-lifecycle.test.ts index cc9d6b6d48..5113a2b348 100644 --- a/packages/services/service-storage/src/attachment-lifecycle.test.ts +++ b/packages/services/service-storage/src/attachment-lifecycle.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. import { describe, it, expect, vi } from 'vitest'; -import { ObjectQL } from '@objectstack/objectql'; +import { ObjectQL, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { installAttachmentLifecycleHooks, createSysFileReapGuard, @@ -51,6 +51,7 @@ function fakeEngine(seed: { return typeof options?.limit === 'number' ? rows.slice(0, options.limit) : rows; }, async findOne(object, options: any) { + assertEngineFindOnePredicate(object, options); return tables[object].find((r) => matches(r, options?.where ?? {})) ?? null; }, async update(object, data: any, _options) { diff --git a/packages/services/service-storage/src/file-reference-lifecycle.test.ts b/packages/services/service-storage/src/file-reference-lifecycle.test.ts index e2526bf9ab..b10a3ddea0 100644 --- a/packages/services/service-storage/src/file-reference-lifecycle.test.ts +++ b/packages/services/service-storage/src/file-reference-lifecycle.test.ts @@ -8,6 +8,7 @@ import { FileFieldBulkWriteError, type FileReferenceEngine, } from './file-reference-lifecycle.js'; +import { assertEngineFindOnePredicate } from '@objectstack/objectql'; const silentLogger = () => ({ info: vi.fn(), warn: vi.fn(), debug: vi.fn() }); @@ -74,6 +75,7 @@ function fakeEngine(opts: { return typeof options?.limit === 'number' ? rows.slice(0, options.limit) : rows; }, async findOne(object, options: any) { + assertEngineFindOnePredicate(object, options); calls.push({ op: 'findOne', object, arg: options?.where }); return (tables[object] ?? []).find((r) => matches(r, options?.where ?? {})) ?? null; }, diff --git a/packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts b/packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts index a023a9457d..43759159c7 100644 --- a/packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts +++ b/packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts @@ -21,7 +21,7 @@ */ import { describe, it, expect, vi } from 'vitest'; -import { assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { isDataMigrationVerified, mayActIrreversibly, @@ -88,6 +88,7 @@ function reapEngine() { return tables[object].filter((r) => Object.entries(where).every(([k, v]) => { if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`); return r[k] === v; })); }, async findOne(object: string, options: any) { + assertEngineFindOnePredicate(object, options); const where = options?.where ?? {}; return tables[object].find((r) => Object.entries(where).every(([k, v]) => { if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`); return r[k] === v; })) ?? null; }, diff --git a/packages/services/service-storage/src/metadata-store.test.ts b/packages/services/service-storage/src/metadata-store.test.ts index 80fc683395..844521a5fc 100644 --- a/packages/services/service-storage/src/metadata-store.test.ts +++ b/packages/services/service-storage/src/metadata-store.test.ts @@ -17,7 +17,7 @@ // returns `null` rather than throwing. import { describe, it, expect } from 'vitest'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import type { IDataEngine } from '@objectstack/spec/contracts'; import { StorageMetadataStore, @@ -65,6 +65,7 @@ function createFakeEngine(opts: { failing?: EngineMethod[] } = {}) { return { ...data }; }, async findOne(object: string, query?: any) { + assertEngineFindOnePredicate(object, query); boom('findOne', object); const id = query?.where?.id; return table(object).get(String(id)) ?? null; diff --git a/packages/services/service-storage/src/storage-routes.metadata-outage.test.ts b/packages/services/service-storage/src/storage-routes.metadata-outage.test.ts index b24b6dbf21..f441ff2f78 100644 --- a/packages/services/service-storage/src/storage-routes.metadata-outage.test.ts +++ b/packages/services/service-storage/src/storage-routes.metadata-outage.test.ts @@ -15,7 +15,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import type { IHttpRequest, IHttpResponse, RouteHandler, IDataEngine } from '@objectstack/spec/contracts'; -import { assertEngineDeleteDispatch } from '@objectstack/objectql'; +import { assertEngineDeleteDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { LocalStorageAdapter } from './local-storage-adapter.js'; import { StorageMetadataStore } from './metadata-store.js'; import { registerStorageRoutes } from './storage-routes.js'; @@ -85,6 +85,7 @@ function createFakeEngine(opts: { failing?: EngineMethod[] } = {}) { return { ...data }; }, async findOne(object: string, query?: any) { + assertEngineFindOnePredicate(object, query); boom('findOne', object); return table(object).get(String(query?.where?.id)) ?? null; }, diff --git a/packages/services/service-storage/src/tombstone-download-live-reference.test.ts b/packages/services/service-storage/src/tombstone-download-live-reference.test.ts index ef50e56b53..f7b45b1d3b 100644 --- a/packages/services/service-storage/src/tombstone-download-live-reference.test.ts +++ b/packages/services/service-storage/src/tombstone-download-live-reference.test.ts @@ -50,7 +50,7 @@ import type { IHttpRequest, IHttpResponse, RouteHandler } from '@objectstack/spe // (`check:engine-double-contract`, #4550/#5480). It declares no `delete`: this // suite never deletes a row, and a verb a double does not need is a contract // it cannot get wrong. -import { assertEngineUpdateDispatch } from '@objectstack/objectql'; +import { assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/objectql'; import { LocalStorageAdapter } from './local-storage-adapter.js'; import { StorageMetadataStore } from './metadata-store.js'; import { registerStorageRoutes } from './storage-routes.js'; @@ -117,6 +117,7 @@ function fakeEngine(seed: { return typeof options?.limit === 'number' ? rows.slice(0, options.limit) : rows; }, async findOne(object: string, options: any) { + assertEngineFindOnePredicate(object, options); return (tables[object] ?? []).find((r) => matches(r, options?.where ?? {})) ?? null; }, async insert(object: string, data: any) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c92abf8061..53fff4986b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2336,6 +2336,9 @@ importers: '@objectstack/driver-sqlite-wasm': specifier: workspace:* version: link:../../drivers/driver-sqlite-wasm + '@objectstack/metadata-core': + specifier: workspace:* + version: link:../../metadata-core '@objectstack/plugin-hono-server': specifier: workspace:* version: link:../../plugins/plugin-hono-server From 88784b7b7022078ab4e08ed99c0ceddaeeb249c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 09:08:07 +0000 Subject: [PATCH 2/3] wip(engine-doubles): recovered uncommitted gate half from a stalled dispatch NOT REVIEWED. The dispatch that wrote this stalled before committing or pushing; this commit exists only so the work survives the container, and the resuming dev is expected to inspect it, not to trust it. Contents as found on disk: the findOne slice added to check-engine-double-contract's SLICES table (assertEngineFindOnePredicate / resolveEngineFindOnePredicate, origin #4419), plus the regenerated baseline and pinned ledgers. No gate run, no test run, and no ablation was performed against it here. --- scripts/check-engine-double-contract.mjs | 12 + scripts/engine-double-contract.baseline.json | 24 +- scripts/engine-double-contract.pinned.json | 1176 +++++++++++++++++- 3 files changed, 1206 insertions(+), 6 deletions(-) diff --git a/scripts/check-engine-double-contract.mjs b/scripts/check-engine-double-contract.mjs index 05918877e5..e5dbf928a9 100644 --- a/scripts/check-engine-double-contract.mjs +++ b/scripts/check-engine-double-contract.mjs @@ -385,6 +385,18 @@ const SLICES = [ pinCall: 'assertEngineUpdateDispatch(data, options)', origin: '#5480', }, + { + verb: 'findOne', + producer: 'ObjectQL.findOne', + symbols: new Set(['assertEngineFindOnePredicate', 'resolveEngineFindOnePredicate']), + modules: [ + /^@objectstack\/objectql$/, + /^@objectstack\/metadata-core$/, + /engine-findone-predicate(\.js)?$/, + ], + pinCall: 'assertEngineFindOnePredicate(object, query)', + origin: '#4419', + }, ]; /** The verbs the ledger may name -- see the DECLARED invariant. */ diff --git a/scripts/engine-double-contract.baseline.json b/scripts/engine-double-contract.baseline.json index 918bfa6f29..123fafbac4 100644 --- a/scripts/engine-double-contract.baseline.json +++ b/scripts/engine-double-contract.baseline.json @@ -7,8 +7,8 @@ "— so it may accept a call the real engine refuses, which is exactly how #4434 shipped a dead", "REST route with its suite green.", "", - "`verb` is REQUIRED and must name a slice the checker actually scans (currently `delete` and", - "`update` — the DECLARED invariant). It is not defaulted: an entry that inherited a verb would", + "`verb` is REQUIRED and must name a slice the checker actually scans (currently `delete`,", + "`update` and `findOne` — the DECLARED invariant). It is not defaulted: an entry that inherited a verb would", "be an exemption whose scope nobody could read, and the two verbs genuinely differ — see the", "script header on `data.id`, which `update` takes verbatim and `delete` has no analogue for.", "A file may therefore carry two entries, one per verb, and a fake pinned on `delete` earns", @@ -43,7 +43,9 @@ "in one act — there was no producer-side predicate to route through before #5480, so none of", "them could have been pinned however carefully they were written.", "", - "THE #8639 BATCH WAS A FIRST MEASUREMENT, NOT A RAISED RATCHET — AND IT IS NOW CLOSED (#8651). Until #8639 the checker’s `implOf` returned null for a CALL-EXPRESSION initializer, so every engine double spelling its write verb `vi.fn(async …)` — the spelling a test reaches for whenever it wants to assert call counts — was invisible to discovery: not pinned, not ledgered, absent from BOTH sides of a ledger that reconciles in both directions. Teaching `implOf` to unwrap a call whose sole argument is a function moved discovery from 379 doubles to 435 (+56, 0 lost). Of the 93 verb members the fix newly made readable, 56 are engine-shaped and 37 are correctly vetoed as driver / scoped-repository / non-double shapes. 21 of the 56 were PINNED in #8639’s own PR (every one whose pin was a single call). The remaining 29 were carried here as \"MEASURED (#8639)\" entries, each naming why a one-line pin did not reach it — a signature declaring too few parameters to hand the predicate, a deliberate tripwire double whose body opens by throwing, a concise arrow body, or a package depending on neither home of the predicate. #8651 closed all 29: signatures widened, concise bodies converted to blocks, `@objectstack/metadata-core` added as a devDependency of `@objectstack/mcp`, and the genuine tripwires routed through the predicate AHEAD of their throw — so the assertion each one carries (\"this path performs no write at all\") is unchanged: the body still never runs on a passing suite, and a call that did arrive still fails the test, whichever of the two errors it raises. Their entries were deleted in that same PR, which is what SHRINK-ONLY requires of a pin. No entry below carries \"MEASURED (#8639)\" any more. This paragraph is kept because the discovery history it records is what makes the 379 → 435 jump readable to the next author, and because adding entries here normally reads as weakening while that batch was the opposite — the same act #5629 and #5480 each recorded once before." + "THE #8639 BATCH WAS A FIRST MEASUREMENT, NOT A RAISED RATCHET — AND IT IS NOW CLOSED (#8651). Until #8639 the checker’s `implOf` returned null for a CALL-EXPRESSION initializer, so every engine double spelling its write verb `vi.fn(async …)` — the spelling a test reaches for whenever it wants to assert call counts — was invisible to discovery: not pinned, not ledgered, absent from BOTH sides of a ledger that reconciles in both directions. Teaching `implOf` to unwrap a call whose sole argument is a function moved discovery from 379 doubles to 435 (+56, 0 lost). Of the 93 verb members the fix newly made readable, 56 are engine-shaped and 37 are correctly vetoed as driver / scoped-repository / non-double shapes. 21 of the 56 were PINNED in #8639’s own PR (every one whose pin was a single call). The remaining 29 were carried here as \"MEASURED (#8639)\" entries, each naming why a one-line pin did not reach it — a signature declaring too few parameters to hand the predicate, a deliberate tripwire double whose body opens by throwing, a concise arrow body, or a package depending on neither home of the predicate. #8651 closed all 29: signatures widened, concise bodies converted to blocks, `@objectstack/metadata-core` added as a devDependency of `@objectstack/mcp`, and the genuine tripwires routed through the predicate AHEAD of their throw — so the assertion each one carries (\"this path performs no write at all\") is unchanged: the body still never runs on a passing suite, and a call that did arrive still fails the test, whichever of the two errors it raises. Their entries were deleted in that same PR, which is what SHRINK-ONLY requires of a pin. No entry below carries \"MEASURED (#8639)\" any more. This paragraph is kept because the discovery history it records is what makes the 379 → 435 jump readable to the next author, and because adding entries here normally reads as weakening while that batch was the opposite — the same act #5629 and #5480 each recorded once before.", + "", + "THE `findOne` SLICE'S FIRST MEASUREMENT ADDED EXACTLY ONE ROW, AND THAT IS THE POINT (objectstack#12068, the remainder of #11957). The two paragraphs above record slices whose first measurement entered this ledger wholesale \u2014 116 doubles for `update` (#5480), 29 for the #8639 batch \u2014 because the population was past a reviewable diff. `findOne` was measured the same way and came out LARGER than either, 213 files / 254 doubles on origin/main @ 4045b954d3, and it is NOT ledgered: 211 of the 213 were ADOPTED in the slice's own PR. What made the difference is not effort, it is that the adoption is decidable per double \u2014 `assertEngineFindOnePredicate(object, query)` is one line in front of a member every one of those fakes already declares \u2014 so the batch could be driven from this gate's own reported (file, line) population rather than from a hand list, and every affected package's suite could then answer whether the looseness was load-bearing. TWO rows remain, below, and neither is deferred adoption. `packages/spec/src/contracts/data-engine.test.ts` is EXEMPT for the structural reason that file's two sibling rows already carry: @objectstack/spec cannot import the predicate in principle. `packages/runtime/src/sandbox/quickjs-runner.test.ts` is a MISATTRIBUTION rather than an unguarded fake — an object-BOUND `ctx.api.object(name)` facade with no by-id member for the repository veto to read, so its position 0 holds the query and there is no object-name argument the predicate could be handed. The wrong pin was attempted and measured red before that row was written. Read this as the shape a new slice SHOULD take when its adoption is mechanical \u2014 the wholesale ledger is the concession a slice makes when it cannot be, not the default." ], "entries": [ { @@ -702,6 +704,14 @@ "why": "MEASURED (#5480): the `update` slice of this gate is NEW — `resolveEngineUpdateDispatch` did not exist before #5480, so no double in the repo could route through it and the whole discovered set enters this ledger in one act. Not newly written looseness and not a raised ratchet: it is the first measurement of a contract that had no producer-side predicate to measure against, which is exactly what this script's header used to list under deliberately-not-covered (\"update's twin dispatch ... needs its own producer-side predicate extracted first\"). Discovered at line 3693. The package already depends on @objectstack/objectql (`dependencies`), so this is a one-line pin whenever a batch takes it — deferred here because #5480's slice is the producer-side predicate plus the gate that reads it, and flipping ~100 unmeasured suites red belongs in the per-package batches that follow, exactly as #5629 did for delete. WHAT THIS ENTRY DOES NOT CLAIM: unlike the #5629 delete batch above it carries NO per-file dormancy probe. Nothing here says the looseness is unexercised — only that the double is structurally free to be looser than ObjectQL.update on the shape a hand-written guard always drops (`where: { id: { $in: [...] } }` looks like an id and is a multi-row predicate) — a shape the producer refuses in `data.id` too since objectstack#5748 put the payload half through the SAME scalar test, so `data.id` still outranks `where` and `multi`, but only when it IS a scalar id.", "closes": "open the fake's update with assertEngineUpdateDispatch(data, options) and run the package's suite" }, + { + "file": "packages/runtime/src/sandbox/quickjs-runner.test.ts", + "verb": "findOne", + "unguarded": 1, + "kind": "DEBT", + "why": "MEASURED (objectstack#12068): this is NOT an engine double — it is the object-BOUND facade `ctx.api.object(name)` returns, and it is the residual this script's header already names under \"a scoped repository that declares NO repository-only member\", one verb further along. The factory is `const repoFor = (tx) => (name: string) => ({ insert, findOne, count })`: the object name arrives from the CLOSURE, so position 0 of `findOne` holds the QUERY, and `quickjs-runner.ts`'s `installApiMethod` forwards the VM's arguments verbatim (`m.apply(proxy, args)`) — `ctx.api.object('a').findOne({ id: 1 })` reaches this member as `findOne({ id: 1 })`. It is discovered anyway because the repository veto needs a REPOSITORY_ONLY_MEMBER (`updateById`/`deleteById`) and this facade declares neither, while `insert` supplies ENGINE_ONLY_MEMBERS evidence at an arity the parameter test cannot judge. Discovered at line 878. WHY NOT PINNED, and it is not deferral: `assertEngineFindOnePredicate(object, query)` needs the object name, and there is no object-name position in this signature to hand it. Adopting it anyway was TRIED and MEASURED rather than reasoned about — the sweep's codemod widened the signature to `(object, query)` and the suite went red with `findOne('[object Object]') selects no particular record`, the predicate reading the query as the object name. That red is the correct outcome of a wrong pin and it is why this row exists instead. Padding the facade with `updateById`/`deleteById` to buy the veto is the ceremony the #11626 section of this script rejects, and closing it for real is a REPOSITORY_ONLY_MEMBERS measurement (`--census` first, as that constant's own note requires), not a test edit. WHAT THIS ENTRY DOES NOT CLAIM: no dormancy. This `findOne` IS driven — `reads inside the tx also reuse the handle` calls it — so the looseness is live, and the row records a MISATTRIBUTION rather than an unguarded engine fake: there is no `ObjectQL.findOne` contract for an object-bound repository member to be looser than.", + "closes": "either widen REPOSITORY_ONLY_MEMBERS so an object-bound facade with no by-id member leaves scan scope (a `--census` measurement, per that constant's note — it would also retire the sibling residual at packages/runtime/src/action-body-identity.test.ts:71), or restate this fixture as a fake ENGINE the sandbox test drives through `ctx.api.object`. Not closable by a one-line pin: the signature has no object-name position." + }, { "file": "packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts", "verb": "update", @@ -1118,6 +1128,14 @@ "why": "Not a stand-in that code under test drives — it is a TYPE-CONFORMANCE witness that IDataEngine is implementable, asserting only `typeof engine.delete === 'function'`. And it cannot be pinned even in principle: @objectstack/objectql depends on @objectstack/spec, so the import would invert the dependency. Ran clean under the guard anyway (spec: 295/295 files passed with the dispatch guard installed in every engine double). RE-MEASURED (#5629): this entry's count moves 1 -> 5 without a line of test code changing — 4 further doubles in this file (lines 46, 82, 119, 152) became visible when #5629 stopped discarding deletes that declare no parameters. Not a regression and not a raised ratchet: the doubles were always here, the scan could not reach them. The four newly visible doubles are the same kind as the one this entry already records: `const engine: IDataEngine = { … }` type-conformance witnesses inside the contract test for that interface. Each is built to exercise or declare something OTHER than delete — reads through `find`/`findOne`/`count`, the trailing options argument on every read, the optional `execute` and `vectorFind` members — and none of them calls `delete`, which is present only because the interface requires the member. EXEMPT for the reason already stated, which applies to each of them unchanged. Dormant looseness, probed rather than assumed: a `process.stderr.write` marker injected as the first statement of this delete printed NOTHING while the file's suite passed, so no path this suite drives calls it. The control that makes that silence evidence instead of a broken probe: the same injection in `run-summary.test.ts`'s PINNED delete DID print, in the same run of the same harness. Dormant is not harmless — it means the looseness is unexercised today, so a future test that starts deleting through this fake inherits a double that accepts what ObjectQL.delete refuses.", "closes": "nothing — permanent" }, + { + "file": "packages/spec/src/contracts/data-engine.test.ts", + "verb": "findOne", + "unguarded": 5, + "kind": "EXEMPT", + "why": "MEASURED (objectstack#12068, the remainder of #11957): the `findOne` slice of this gate is NEW — `assertEngineFindOnePredicate` did not exist before #12072, so no double in the repo could route through it. Unlike #5480's and #8639's first measurements this slice ledgered almost nothing: of the 213 files it discovered, 212 were adopted in the slice's own PR and this is the one that remains. EXEMPT for the reason this file's `delete` and `update` rows already carry, restated here because a per-verb ledger may not inherit a verdict: @objectstack/spec CANNOT import the predicate even in principle — both of its homes (@objectstack/metadata-core since #5619, and @objectstack/objectql) DEPEND ON @objectstack/spec, so the import would invert that edge and turbo rejects the resulting task graph outright. There is no devDependency route here, which is precisely what separates this row from the `service-datasource` rows above, where the edge WAS available and the `findOne` verb took it in this same PR. Discovered at lines 25, 47, 83, 120, 153 — the five `const engine: IDataEngine = { … }` type-conformance witnesses this file exists to declare. WHAT THIS ENTRY DOES NOT CLAIM, and it is the OPPOSITE of the `delete` row's claim for the same file: this looseness is NOT dormant. The witnesses DRIVE `findOne`, and two of those drives are shapes the real engine REFUSES — `await engine.findOne('users')` (line 66: no `where`, no `orderBy`) and `await engine.findOne('sys_user', { where: {} }, sys)` (line 107: `{}` is the match-every-row shape #4419 rejects). Both are correct as TYPE conformance, which is all this file asserts, and both would throw against ObjectQL. So the exemption is structural, not a clean bill: a reader must not take those two call sites as evidence that a predicate-less `findOne` is a supported shape. Recorded here rather than silently exempted, and handed to the spec seat separately.", + "closes": "nothing — permanent (the import would invert @objectstack/spec's dependency edge). If the two predicate-less call sites at lines 66 and 107 are ever restated to select a particular record, that is a readability fix for the witnesses and does NOT remove this row: the five doubles stay unpinnable." + }, { "file": "packages/spec/src/contracts/data-engine.test.ts", "verb": "update", diff --git a/scripts/engine-double-contract.pinned.json b/scripts/engine-double-contract.pinned.json index 2370f0d3b2..231f6fc7bc 100644 --- a/scripts/engine-double-contract.pinned.json +++ b/scripts/engine-double-contract.pinned.json @@ -6,16 +6,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/core/src/utils/migration-journal.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/core/src/utils/migration-journal.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/mcp/src/__tests__/plugin.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/mcp/src/mcp-stdio-tools.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/mcp/src/mcp-stdio-tools.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/mcp/src/mcp-stdio-tools.test.ts", "verb": "update", @@ -26,6 +41,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/mcp/src/mcp-write-response-internal-fields.tripwire.test.ts", "verb": "update", @@ -36,16 +56,36 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/mcp/src/plugin.record-resource-exposure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/mcp/src/plugin.record-resource-exposure.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/mcp/src/stdio-data-bridge.exposure.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/mcp/src/stdio-data-bridge.not-found.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/migrations/recorded-by-sentinel.test.ts", "verb": "update", @@ -61,6 +101,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts", "verb": "update", @@ -71,6 +116,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol-publish-drafts-closure.test.ts", "verb": "update", @@ -81,6 +131,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol-publish-drafts-endpoint-gate.test.ts", "verb": "update", @@ -91,6 +146,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts", "verb": "update", @@ -101,6 +161,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts", "verb": "update", @@ -111,6 +176,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts", "verb": "update", @@ -121,6 +191,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.audit-field-governance.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.audit-field-governance.test.ts", "verb": "update", @@ -131,6 +206,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-atomic.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.batch-atomic.test.ts", "verb": "update", @@ -141,6 +221,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.batch-not-attempted.test.ts", "verb": "update", @@ -151,6 +236,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts", "verb": "update", @@ -161,6 +251,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.batch-row-driver-text.test.ts", "verb": "update", @@ -171,6 +266,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.batch-row-http-status.test.ts", "verb": "update", @@ -181,6 +281,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.batch-verb-driver-code.test.ts", "verb": "update", @@ -191,6 +296,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.batch-verb-driver-text.test.ts", "verb": "update", @@ -201,6 +311,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.bulk-record-not-found.test.ts", "verb": "update", @@ -211,6 +326,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.capability-write-door.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.capability-write-door.test.ts", "verb": "update", @@ -221,6 +341,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.code-only-types.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.code-only-types.test.ts", "verb": "update", @@ -231,6 +356,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.container-issue-descent.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.container-issue-descent.test.ts", "verb": "update", @@ -241,6 +371,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts", "verb": "update", @@ -251,6 +386,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.delete-object-registry-unregister.test.ts", "verb": "update", @@ -261,6 +401,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts", "verb": "update", @@ -271,21 +416,46 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.ts", + "verb": "findOne", + "pinned": 5 + }, { "file": "packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.diff-canonical-type-and-history-outage.test.ts", "verb": "update", @@ -296,6 +466,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.diff-credential-redaction.test.ts", "verb": "update", @@ -306,6 +481,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts", "verb": "update", @@ -316,16 +496,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts", "verb": "update", "pinned": 2 }, + { + "file": "packages/metadata-protocol/src/protocol.dropped-fields.test.ts", + "verb": "findOne", + "pinned": 3 + }, { "file": "packages/metadata-protocol/src/protocol.dropped-fields.test.ts", "verb": "update", @@ -336,6 +531,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts", "verb": "update", @@ -346,16 +546,36 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.graft-folded-form-sections.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.hydrate-overlay-canonical-type.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/metadata-protocol/src/protocol.i18n-bundle-list-merge.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.injected-system-columns.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.injected-system-columns.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.injected-system-columns.test.ts", "verb": "update", @@ -366,6 +586,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts", "verb": "update", @@ -376,6 +601,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.item-name-grammar.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.item-name-grammar.test.ts", "verb": "update", @@ -386,6 +616,11 @@ "verb": "delete", "pinned": 2 }, + { + "file": "packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts", "verb": "update", @@ -396,16 +631,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.lifecycle-audit-rows.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.lock-gate-canonical-type.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.lock-gate-fail-closed.test.ts", "verb": "update", @@ -416,16 +666,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.many-data-atomic.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.many-data-atomic.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.meta-list-runtime-baseline-dedup.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.meta-types-mint-door-agreement.test.ts", "verb": "update", @@ -436,16 +701,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.metadata-redaction.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.metadata-redaction.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.migrate-stored-noncanonical-type.test.ts", "verb": "update", @@ -456,6 +736,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts", "verb": "update", @@ -466,6 +751,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts", "verb": "update", @@ -476,6 +766,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts", "verb": "update", @@ -486,6 +781,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts", "verb": "update", @@ -496,6 +796,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.package-closure-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.package-closure-gate.test.ts", "verb": "update", @@ -506,6 +811,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.package-publish-audit-rows.test.ts", "verb": "update", @@ -516,6 +826,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.platform-schedule-org-gate.test.ts", "verb": "update", @@ -526,6 +841,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.publish-commit-capture-read-failure.test.ts", "verb": "update", @@ -536,6 +856,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts", "verb": "update", @@ -546,6 +871,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.publish-item-rebind-announce.test.ts", "verb": "update", @@ -558,12 +888,22 @@ }, { "file": "packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts", - "verb": "update", + "verb": "findOne", "pinned": 1 }, { - "file": "packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts", - "verb": "delete", + "file": "packages/metadata-protocol/src/protocol.publish-side-effects-canonical-type.test.ts", + "verb": "update", + "pinned": 1 + }, + { + "file": "packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts", + "verb": "delete", + "pinned": 1 + }, + { + "file": "packages/metadata-protocol/src/protocol.publish-stored-type-canonical.test.ts", + "verb": "findOne", "pinned": 1 }, { @@ -576,16 +916,36 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.read-decorations.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.read-decorations.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts", + "verb": "findOne", + "pinned": 6 + }, + { + "file": "packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.record-not-found.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.record-not-found.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.record-not-found.test.ts", "verb": "update", @@ -596,16 +956,36 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.record-package-commit-durability.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.reference-derivation.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/metadata-protocol/src/protocol.reference-target-unanswerable.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.revert-stored-type-canonical.test.ts", "verb": "update", @@ -616,6 +996,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts", "verb": "update", @@ -626,16 +1011,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.save-meta-missing-item.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts", "verb": "update", @@ -646,6 +1046,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.save-union-issues.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.save-union-issues.test.ts", "verb": "update", @@ -656,6 +1061,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.stored-conversions.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.stored-conversions.test.ts", "verb": "update", @@ -666,6 +1076,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.stored-migration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.stored-migration.test.ts", "verb": "update", @@ -676,6 +1091,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.stored-residue-resave.test.ts", "verb": "update", @@ -686,6 +1106,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts", "verb": "update", @@ -696,11 +1121,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.upsert-existence.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/protocol.upsert-existence.test.ts", "verb": "update", @@ -721,16 +1156,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/protocol.write-response-internal-fields.tripwire.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/search-clone-schema-conformance.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-composite-external-id.test.ts", "verb": "update", @@ -741,6 +1191,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-deferred-dropped.test.ts", "verb": "update", @@ -751,6 +1206,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts", "verb": "update", @@ -761,6 +1221,11 @@ "verb": "delete", "pinned": 2 }, + { + "file": "packages/metadata-protocol/src/seed-loader-driver-text.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/metadata-protocol/src/seed-loader-driver-text.test.ts", "verb": "update", @@ -771,6 +1236,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-engine-schema-fallback.test.ts", "verb": "update", @@ -781,6 +1251,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-env-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-env-scope.test.ts", "verb": "update", @@ -791,6 +1266,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-existing-records-read-failure.test.ts", "verb": "update", @@ -801,6 +1281,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-multi-value-reference.test.ts", "verb": "update", @@ -811,6 +1296,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-name-probe.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-name-probe.test.ts", "verb": "update", @@ -821,6 +1311,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-pointer-pair.test.ts", "verb": "update", @@ -831,6 +1326,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-replay.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-replay.test.ts", "verb": "update", @@ -841,6 +1341,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-retry.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-retry.test.ts", "verb": "update", @@ -851,6 +1356,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-state-machine-exempt.test.ts", "verb": "update", @@ -861,6 +1371,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-summary-stale.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-summary-stale.test.ts", "verb": "update", @@ -871,6 +1386,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts", "verb": "update", @@ -881,6 +1401,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts", "verb": "update", @@ -891,6 +1416,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.draft-drain.test.ts", "verb": "update", @@ -901,6 +1431,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts", "verb": "update", @@ -911,6 +1446,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.history-counters.test.ts", "verb": "update", @@ -921,6 +1461,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts", "verb": "update", @@ -931,6 +1476,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/sys-metadata-repository.recorded-by.test.ts", "verb": "update", @@ -941,6 +1491,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata-protocol/src/view-container-runtime-expansion.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata-protocol/src/view-container-runtime-expansion.test.ts", "verb": "update", @@ -956,6 +1511,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/metadata/src/migrations/migrate-sys-notification-to-event.test.ts", "verb": "update", @@ -971,6 +1531,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/layered-overlay-integration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/layered-overlay-integration.test.ts", "verb": "update", @@ -981,6 +1546,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/meta-object-primary-designation-roundtrip.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/meta-object-primary-designation-roundtrip.test.ts", "verb": "update", @@ -991,6 +1561,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/meta-object-search-companion-roundtrip.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/meta-object-search-companion-roundtrip.test.ts", "verb": "update", @@ -1001,6 +1576,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/meta-object-tenant-index-roundtrip.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/meta-object-tenant-index-roundtrip.test.ts", "verb": "update", @@ -1011,11 +1591,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/package-disable-enforcement.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/package-disable-enforcement.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-boot-hydration-scoped.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-boot-hydration-scoped.test.ts", "verb": "update", @@ -1026,6 +1616,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-boot-object-package-binding.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-boot-object-package-binding.test.ts", "verb": "update", @@ -1036,6 +1631,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-commit-history.test.ts", + "verb": "findOne", + "pinned": 3 + }, { "file": "packages/objectql/src/protocol-commit-history.test.ts", "verb": "update", @@ -1046,6 +1646,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-delete-object-registry-heal.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-delete-object-registry-heal.test.ts", "verb": "update", @@ -1056,6 +1661,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-meta-effective-schema.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-meta-effective-schema.test.ts", "verb": "update", @@ -1066,6 +1676,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-meta-type-canonicalization.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-meta-type-canonicalization.test.ts", "verb": "update", @@ -1076,6 +1691,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-object-overlay-layer.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-object-overlay-layer.test.ts", "verb": "update", @@ -1086,6 +1706,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-org-overlay-registry-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-org-overlay-registry-gate.test.ts", "verb": "update", @@ -1096,6 +1721,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-packaged-object-base.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-packaged-object-base.test.ts", "verb": "update", @@ -1111,11 +1741,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-publish-rollback.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-publish-rollback.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-registry-shadow.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-registry-shadow.test.ts", "verb": "update", @@ -1126,6 +1766,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-save-meta-repo-path.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-save-meta-repo-path.test.ts", "verb": "update", @@ -1136,6 +1781,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-view-identity-overlay.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-view-identity-overlay.test.ts", "verb": "update", @@ -1146,6 +1796,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/protocol-writepath-object-ownership.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/protocol-writepath-object-ownership.test.ts", "verb": "update", @@ -1156,6 +1811,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/registry-tenant-index-author-declared-column.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/registry-tenant-index-author-declared-column.test.ts", "verb": "update", @@ -1166,6 +1826,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/registry-tenant-index-declaration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/registry-tenant-index-declaration.test.ts", "verb": "update", @@ -1176,6 +1841,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/registry-tenant-index-follows-wall.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/registry-tenant-index-follows-wall.test.ts", "verb": "update", @@ -1196,6 +1866,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/objectql/src/sys-metadata-repository.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/objectql/src/sys-metadata-repository.test.ts", "verb": "update", @@ -1266,6 +1941,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/accept-invitation-adopt-membership.test.ts", "verb": "update", @@ -1281,6 +1961,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/admin-revoke-user-session-match-guard.test.ts", "verb": "update", @@ -1291,26 +1976,56 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/audience-posture-setting.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/audience-posture.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/audience-posture.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/audience-posture.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/auth-contains-filter.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/auth-manager.jwt-eddsa-fallback.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-auth/src/auth-manager.optional-plugin-isolation.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/auth-manager.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/auth-manager.test.ts", + "verb": "findOne", + "pinned": 4 + }, { "file": "packages/plugins/plugin-auth/src/auth-manager.test.ts", "verb": "update", @@ -1321,21 +2036,41 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/auth-where-operator-coverage.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/change-email-delete-user-wiring.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/first-session-membership-ordering.test.ts", + "verb": "findOne", + "pinned": 2 + }, { "file": "packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/impersonation-bearer-rotation.test.ts", "verb": "update", @@ -1346,16 +2081,31 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/membership-policy-setting.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/organization-add-member.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/organization-add-member.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/organization-add-member.test.ts", "verb": "update", @@ -1366,6 +2116,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/remove-member-permission-guard.test.ts", "verb": "update", @@ -1376,6 +2131,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/remove-user-atomicity.test.ts", "verb": "update", @@ -1386,21 +2146,41 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/revoke-session-match-guard.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/scim-case-insensitive-identifier.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/session-of-record.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/session-of-record.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/session-tombstone.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/session-tombstone.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/session-tombstone.test.ts", "verb": "update", @@ -1446,6 +2226,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/authored-row-write-verdict.test.ts", "verb": "update", @@ -1466,6 +2251,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/check-only-write-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/check-only-write-scope.test.ts", "verb": "update", @@ -1476,11 +2266,51 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/controlled-by-parent-chain.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/controlled-by-parent-detail-write-authority.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/controlled-by-parent-master-widener.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/controlled-by-parent-sharing.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/explain-engine.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/field-masking-rule.test.ts", + "verb": "findOne", + "pinned": 2 + }, + { + "file": "packages/plugins/plugin-security/src/invitation-placement.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/packaged-permission-set-lock-gate.test.ts", "verb": "update", @@ -1491,11 +2321,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts", "verb": "update", @@ -1511,11 +2351,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/permission-denied-user-copy.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/permission-set-active.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/permission-set-drift.test.ts", "verb": "update", @@ -1531,11 +2381,31 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/permission-set-projection.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/permission-set-resolution-memo.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-security/src/resolve-permission-sets-for-context.pin.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/row-write-widener-composition.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/row-write-widener-composition.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/row-write-widener-composition.test.ts", "verb": "update", @@ -1546,11 +2416,21 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/security-denial-user-copy.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/security-denial-user-copy.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/security-plugin.test.ts", + "verb": "findOne", + "pinned": 5 + }, { "file": "packages/plugins/plugin-security/src/security-plugin.test.ts", "verb": "update", @@ -1561,6 +2441,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/select-only-write-visibility.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/select-only-write-visibility.test.ts", "verb": "update", @@ -1576,6 +2461,11 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/suggested-audience-bindings-install-path.test.ts", "verb": "delete", @@ -1591,6 +2481,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-security/src/vama-write-path-convergence.test.ts", "verb": "update", @@ -1606,6 +2501,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-sharing/src/authored-row-write-deferral.test.ts", "verb": "update", @@ -1656,6 +2556,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-sharing/src/share-link-eligibility.test.ts", "verb": "update", @@ -1706,11 +2611,26 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-webhooks/src/auto-enqueuer.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/plugins/plugin-webhooks/src/bootstrap-declared-webhooks.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-webhooks/src/webhook-drop-durable-record.test.ts", "verb": "update", @@ -1721,6 +2641,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/plugins/plugin-webhooks/src/webhook-signing-secret.test.ts", "verb": "update", @@ -1731,6 +2656,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/meta-compound-save-force-parity.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/meta-compound-save-force-parity.test.ts", "verb": "update", @@ -1741,16 +2671,51 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/meta-compound-save-mode-parity.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/meta-compound-save-mode-parity.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/rest/src/meta-object-extension-agreement.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/rest/src/meta-object-extension-property-classes.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/rest/src/meta-object-materialization-agreement.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/rest/src/meta-object-overlay-extension-fold.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/rest/src/meta-object-search-companion-agreement.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/meta-published-overlay.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/meta-published-overlay.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/meta-published-overlay.test.ts", "verb": "update", @@ -1761,6 +2726,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/public-form-lookup-picker.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/public-form-lookup-picker.test.ts", "verb": "update", @@ -1771,6 +2741,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/public-form-routes.stored-row.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/public-form-routes.stored-row.test.ts", "verb": "update", @@ -1791,21 +2766,41 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/rest/src/rest-server-meta-read-org-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/rest/src/rest-server-meta-read-org-scope.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/runtime/src/action-execution-calldata-batch-retired.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/action-execution-calldata-not-found.test.ts", "verb": "delete", "pinned": 2 }, + { + "file": "packages/runtime/src/action-execution-calldata-not-found.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/domains/meta-published-runtime-publish.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/domains/meta-published-runtime-publish.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/domains/meta-published-runtime-publish.test.ts", "verb": "update", @@ -1816,6 +2811,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/domains/meta-save-destructive-remedy.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/domains/meta-save-destructive-remedy.test.ts", "verb": "update", @@ -1846,6 +2846,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/meta-compound-arity-mint-door.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/meta-compound-arity-mint-door.test.ts", "verb": "update", @@ -1856,6 +2861,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/meta-field-overlay-lock.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/meta-field-overlay-lock.test.ts", "verb": "update", @@ -1866,6 +2876,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/meta-overlay-read-your-writes.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/meta-overlay-read-your-writes.test.ts", "verb": "update", @@ -1876,6 +2891,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/meta-write-org-scope.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/meta-write-org-scope.test.ts", "verb": "update", @@ -1886,6 +2906,11 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/runtime/src/seed-loader.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/runtime/src/seed-loader.test.ts", "verb": "update", @@ -1896,6 +2921,36 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-automation/src/builtin/crud-bulk-intent.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-automation/src/builtin/crud-config-aliases.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-automation/src/builtin/crud-filter-guard.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-automation/src/builtin/crud-output-var.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-automation/src/builtin/crud-runas.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-automation/src/builtin/notify-organization-stamp.integration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-automation/src/builtin/wait-node-degraded-run.test.ts", "verb": "update", @@ -1906,11 +2961,31 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/services/service-automation/src/record-lookup-expand.integration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-automation/src/run-summary.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-automation/src/run-summary.test.ts", + "verb": "findOne", + "pinned": 5 + }, + { + "file": "packages/services/service-automation/src/runas-grant-resolution.integration.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-job/src/db-job-adapter.degraded-outcome.test.ts", "verb": "update", @@ -1926,16 +3001,76 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/services/service-messaging/src/email-channel.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/inbox-channel.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/messaging-service-plugin.test.ts", + "verb": "findOne", + "pinned": 2 + }, + { + "file": "packages/services/service-messaging/src/messaging-service.test.ts", + "verb": "findOne", + "pinned": 6 + }, + { + "file": "packages/services/service-messaging/src/notification-schema-conformance.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-messaging/src/notification-schema-conformance.test.ts", "verb": "update", "pinned": 1 }, + { + "file": "packages/services/service-messaging/src/preference-resolver.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/read-receipt-organization.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/recipient-resolver.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/sms-channel.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/sql-outbox-audit-columns.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-messaging/src/template-renderer.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-queue/src/db-queue-adapter.test.ts", "verb": "delete", @@ -1961,6 +3096,26 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/services/service-storage/src/attachment-access-hooks.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-storage/src/attachment-lifecycle.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-storage/src/file-reference-lifecycle.test.ts", + "verb": "findOne", + "pinned": 1 + }, + { + "file": "packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-storage/src/lax-deviation-reclamation-gate.test.ts", "verb": "update", @@ -1971,16 +3126,31 @@ "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-storage/src/metadata-store.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-storage/src/storage-routes.metadata-outage.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-storage/src/storage-routes.metadata-outage.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-storage/src/stranded-orphan-inventory.test.ts", "verb": "delete", "pinned": 1 }, + { + "file": "packages/services/service-storage/src/tombstone-download-live-reference.test.ts", + "verb": "findOne", + "pinned": 1 + }, { "file": "packages/services/service-storage/src/tombstone-download-live-reference.test.ts", "verb": "update", From 4262dda29843495d4cb765ae497ad436db557a66 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 10:33:35 +0000 Subject: [PATCH 3/3] fix(engine-doubles): close the two defects the recovered findOne sweep left, and alias metadata-core in service-datasource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supersedes the "NOT REVIEWED" disclaimer on 88784b7b70: the gate half and the 211-file adoption below it have now been run, ablated and re-measured. Three things this commit changes, all of them found by running what that commit never ran. 1. TS2345 in service-messaging. `delivery-headers-at-rest.integration.test.ts` passed the fake's `q: unknown` straight into the predicate, whose parameter is `EngineFindOneQueryInput | null | undefined`. vitest never type-checks, so the suite was green and `tsc --noEmit` was red — the one direction a test-only sweep can go wrong invisibly. Cast at the call, matching the sibling `assertEngineUpdateDispatch(d as any, q as any)` pin three lines below it. 2. service-datasource's engine double reached `@objectstack/metadata-core` through `dist/`. `check:test-source-alias` names the repair and refuses the alternative: alias the specifier in the package's own vitest config, never widen the shrink-only registry. A pin resolved through a build artifact is pinned to whatever was last built rather than to the predicate beside it, which is the single failure a contract pin exists to prevent. 3. Import order in that same test file is now load-bearing, and says so. Aliasing metadata-core to source pulls its import graph into the package's resolution domain, which switches on the clocked-window rule for the whole package — and that rule then misreported a file that has paid its `@objectstack/spec/kernel` load at module top since #10120. Cause is a detector defect filed as #12555: the gate's import regex swallows a bare side-effect import whenever an `import … from …` follows it later in the file. Not worked around in the gate — the fix there needs its own `--self-test` cases — so the statement is placed where the detector reads it correctly, with a comment naming the constraint and the card that removes it. --- .../src/__tests__/datasource-admin-plugin.test.ts | 8 +++++++- .../services/service-datasource/vitest.config.ts | 13 ++++++++++++- .../delivery-headers-at-rest.integration.test.ts | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts b/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts index 09ed5ff535..e6e111eb60 100644 --- a/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts +++ b/packages/services/service-datasource/src/__tests__/datasource-admin-plugin.test.ts @@ -8,13 +8,19 @@ import { type DatasourceAdminServicePluginOptions, } from '../datasource-admin-plugin.js'; +import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; + // [#10126] Pay the first transform of these dist-resolved workspace deps at MODULE // LOAD. Each is reached below through a dynamic `import()` inside an `it()` body or a // hook -- both of which vitest clocks, while collection is clocked against nothing. See // `scripts/check-test-source-alias.mjs` (the clocked-window rule) and #10115 / PR #10120, // where the same shape cost 30 ejected merge-queue builds in one night. +// +// ORDER IS LOAD-BEARING until #12555 lands: check-test-source-alias's import regex +// swallows a bare side-effect import whenever an `import … from …` follows it later in +// the file, so this statement must stay BELOW every `from` import or the clocked-window +// rule reports this file as unpaid while the line it asks for is already here. import '@objectstack/spec/kernel'; -import { assertEngineFindOnePredicate } from '@objectstack/metadata-core'; /** * Minimal PluginContext + in-memory metadata service. Boots the plugin and diff --git a/packages/services/service-datasource/vitest.config.ts b/packages/services/service-datasource/vitest.config.ts index 07c958f4cc..bef9273c58 100644 --- a/packages/services/service-datasource/vitest.config.ts +++ b/packages/services/service-datasource/vitest.config.ts @@ -37,6 +37,17 @@ export default defineConfig({ // matches by PREFIX, so a bare `@objectstack/core` entry would also swallow // `@objectstack/core/logger` and resolve it to `core/src/index.ts/logger` // (ENOTDIR). Same shape as `service-storage` and `service-knowledge`. - alias: [{ find: /^@objectstack\/core$/, replacement: path.resolve(__dirname, '../../core/src/index.ts') }], + // `@objectstack/metadata-core` became a VALUE import of this package's TEST + // layer when its engine double adopted `assertEngineFindOnePredicate` + // (objectstack#12068). Same reasoning as the entry above, and the same + // direction of danger: unaliased it would follow the workspace link to + // `packages/metadata-core/dist/index.js`, so the double would be pinned to + // whatever the last build of that predicate emitted rather than to the + // predicate sitting next to it — a green pin over a stale contract, which is + // the one failure a contract pin exists to make impossible. + alias: [ + { find: /^@objectstack\/core$/, replacement: path.resolve(__dirname, '../../core/src/index.ts') }, + { find: /^@objectstack\/metadata-core$/, replacement: path.resolve(__dirname, '../../metadata-core/src/index.ts') }, + ], }, }); diff --git a/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts b/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts index 0648abb349..45cd2ced10 100644 --- a/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts +++ b/packages/services/service-messaging/src/delivery-headers-at-rest.integration.test.ts @@ -288,7 +288,7 @@ describe('sys_http_delivery — authored headers vs the data API (#8118)', () => // its authored configuration. const noAccessor = { find: (o: string, q: unknown) => (eng as any).find(o, q), - findOne: (o: string, q: unknown) => { assertEngineFindOnePredicate(o, q); return (eng as any).findOne(o, q); }, + findOne: (o: string, q: unknown) => { assertEngineFindOnePredicate(o, q as any); return (eng as any).findOne(o, q); }, insert: (o: string, d: unknown) => (eng as any).insert(o, d), update: (o: string, d: unknown, q: unknown) => { // Engine-double contract: this fake's update() must be exactly