Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/src/utils/migration-journal.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,7 +27,7 @@ import {
assertEngineUpdateDispatch,
type EngineDeleteDispatchInput,
type EngineUpdateDispatchData,
type EngineUpdateDispatchInput,
type EngineUpdateDispatchInput, assertEngineFindOnePredicate,
} from '@objectstack/metadata-core';
import {
runMigrationJournal,
Expand DownExpand Up@@ -85,6 +85,7 @@ class FakeEngine {
}

async findOne(objectName: string, query?: { where?: Record<string, unknown> }): Promise<FakeRow | null> {
assertEngineFindOnePredicate(objectName, query);
return (await this.find(objectName, query))[0] ?? null;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/mcp/src/__tests__/plugin.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand DownExpand Up@@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions packages/mcp/src/mcp-stdio-tools.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand DownExpand Up@@ -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; }),
};
}

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';
Expand DownExpand Up@@ -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<string, unknown>) =>
writtenRow((data?.id as string) ?? 'new-1', data)),
update: vi.fn(async (_o: string, data: Record<string, unknown>, options?: any) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/mcp/src/plugin.record-resource-exposure.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand DownExpand Up@@ -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; }),
};
}

Expand Down
3 changes: 2 additions & 1 deletion packages/mcp/src/stdio-data-bridge.exposure.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand All@@ -54,7 +55,7 @@ import { createStdioDataBridge, GATED_ACTIONS, type McpExposureError } from './s
function makeEngine(rows: Array<Record<string, unknown>> = [{ 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(),
Expand Down
3 changes: 2 additions & 1 deletion packages/mcp/src/stdio-data-bridge.not-found.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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(),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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,
Expand DownExpand Up@@ -77,6 +77,7 @@ class FakeEngine {
}

async findOne(objectName: string, query?: { where?: Record<string, unknown> }): Promise<FakeRow | null> {
assertEngineFindOnePredicate(objectName, query);
return (await this.find(objectName, query))[0] ?? null;
}

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 {
Expand DownExpand Up@@ -117,6 +117,7 @@ function makeStubEngine() {

const engine: any = {
async findOne(table: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(table, opts);
if (table === 'sys_metadata_history') {
return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 {
Expand DownExpand Up@@ -155,6 +155,7 @@ function makeStubEngine(options?: { liveObjects?: unknown[]; livePermissions?: u

const engine: any = {
async findOne(table: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(table, opts);
if (table === 'sys_metadata_history') {
return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

Expand DownExpand Up@@ -149,6 +149,7 @@ function makeStubEngine(namespace?: string) {

const engine: any = {
async findOne(table: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(table, opts);
if (table === 'sys_metadata_history') {
return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

/**
Expand DownExpand Up@@ -126,6 +126,7 @@ function makeStubEngine() {

const engine: any = {
async findOne(table: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(table, opts);
if (table === 'sys_metadata_history') {
return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

/**
Expand DownExpand Up@@ -135,6 +135,7 @@ function makeStubEngine() {

const engine: any = {
async findOne(table: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(table, opts);
if (table === 'sys_metadata_history') {
return historyRows.find((h) => matchesHistory(h, opts.where)) ?? null;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

Expand All@@ -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<string, unknown> }) {
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;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

Expand DownExpand Up@@ -82,6 +82,7 @@ function makeStubEngine() {
};
const engine: any = {
async findOne(_t: string, opts: { where: Record<string, unknown> }) {
assertEngineFindOnePredicate(_t, opts);
return findRow(opts.where)?.row ?? null;
},
async find(_t: string, opts: { where: Record<string, unknown> }) {
Expand Down
4 changes: 2 additions & 2 deletions packages/metadata-protocol/src/protocol.batch-atomic.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 = {
Expand DownExpand Up@@ -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 };
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 = {
Expand DownExpand Up@@ -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) },
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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' } } };

Expand DownExpand Up@@ -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<any>, baseContext?: any) => {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

Expand DownExpand Up@@ -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;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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';

Expand DownExpand Up@@ -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;
Expand Down
Loading
Loading