@haverstack/adapter-conformance 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
2
+
3
+ To the extent possible under law, the Haverstack contributors have waived all
4
+ copyright and related or neighboring rights to Haverstack. This work is
5
+ published from the United States.
6
+
7
+ https://creativecommons.org/publicdomain/zero/1.0/
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @haverstack/adapter-conformance
2
+
3
+ A runnable conformance suite for `StackRecordAdapter` and `StackBlobAdapter` implementers.
4
+
5
+ `docs/spec/adapters.md` documents the adapter contract in prose. This package is the checked version: point it at your adapter and it runs the invariants — content-path filtering, `_config` protection, cursor stability, version snapshots, full-text search consistency, capability honesty — that every first-party adapter is held to.
6
+
7
+ > **Status:** Early development. APIs are unstable.
8
+
9
+ ## Installation
10
+
11
+ ```sh
12
+ npm install --save-dev @haverstack/adapter-conformance vitest
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { runRecordAdapterConformance } from '@haverstack/adapter-conformance';
19
+ import { MyRecordAdapter, MY_RECORD_CAPABILITIES } from './my-adapter.js';
20
+
21
+ runRecordAdapterConformance({
22
+ name: 'MyRecordAdapter',
23
+ capabilities: MY_RECORD_CAPABILITIES,
24
+ open: () => MyRecordAdapter.initialize({ path: tempDbPath() }),
25
+ close: (adapter) => adapter.close?.(),
26
+ });
27
+ ```
28
+
29
+ Call this at the top level of a `*.test.ts` file — it registers a normal `describe`/`it` tree via vitest, so it runs alongside your own tests. `open()` is called before every test and `close()` after, so give it a fresh, empty adapter instance each time.
30
+
31
+ `capabilities` is passed explicitly (the same object your adapter's own `capabilities` getter returns) rather than read off an opened instance, because vitest builds its test tree synchronously — before any `beforeEach` has run. Suites gated on a capability you don't declare (full-text search, content-field sort, path-reaching content filters) are skipped, never failed; every filtering test that does run includes a record that must **not** match, so declaring a capability you don't actually honor still fails loudly.
32
+
33
+ For blob adapters:
34
+
35
+ ```ts
36
+ import { runBlobAdapterConformance } from '@haverstack/adapter-conformance';
37
+ import { MyBlobAdapter } from './my-blob-adapter.js';
38
+
39
+ runBlobAdapterConformance({
40
+ name: 'MyBlobAdapter',
41
+ listFiles: true, // omit or set false if you don't implement the optional listFiles()
42
+ open: () => new MyBlobAdapter(tempDir()),
43
+ });
44
+ ```
45
+
46
+ Errors are asserted against `.code` (the `StackErrorCode` every `StackError` subclass carries) rather than `instanceof`, so the suite runs unmodified against an adapter reached through an RPC boundary — a Durable Object, a remote worker — where a thrown error's prototype chain doesn't survive the trip.
47
+
48
+ ## What's covered
49
+
50
+ **Record adapters:** CRUD and not-found/conflict errors, the `_config` singleton's reserved-id exclusion from queries, associations (idempotent, never bump version), version snapshots and `restoreVersion` (content + `parentId` restored, associations untouched), `commitMigration`, cursor pagination (a cursor minted under one sort refused when replayed under another), content-path filtering (array spread, nested objects, `contentPresent`, null-matches-absent), sort by content field, and full-text search (including that a content patch is reflected — no stale matches under the old content).
51
+
52
+ **Blob adapters:** content-addressed `putAttachment` (fileId is the SHA-256 hex of the bytes, identical bytes dedupe), byte-exact `getAttachment`, the `not_found`/`bad_request` fileId error contract, `deleteAttachment`, and the optional `listFiles()`.
53
+
54
+ See `docs/spec/adapters.md` for the full prose contract this suite checks.
55
+
56
+ ## License
57
+
58
+ [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) — public domain.
59
+
60
+ ## Monorepo
61
+
62
+ Part of [haverstack/core](https://github.com/haverstack/core).
package/dist/blob.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Blob adapter conformance suite
3
+ * -------------------------------------------------------
4
+ * The blob half of the adapter contract is small (StackBlobAdapter is three
5
+ * required methods plus one optional one) but has the same failure shape as
6
+ * the record half: content-addressing and the fileId error contract are
7
+ * documented in prose and easy to get subtly wrong in a from-scratch
8
+ * implementation — a fileId that isn't actually the SHA-256 of the bytes
9
+ * it names breaks any caller that compares fileIds instead of re-fetching,
10
+ * and a malformed fileId misreported as `not_found` instead of `bad_request`
11
+ * hides a caller bug behind the wrong error class.
12
+ */
13
+ import type { StackBlobAdapter } from '@haverstack/core/adapter';
14
+ export interface BlobAdapterConformanceOptions {
15
+ /** Used in describe() titles. */
16
+ name: string;
17
+ /** Produce a fresh, empty adapter instance. Called before every test. */
18
+ open: () => Promise<StackBlobAdapter> | StackBlobAdapter;
19
+ /** Release what `open()` acquired. Called after every test. */
20
+ close?: (adapter: StackBlobAdapter) => Promise<void> | void;
21
+ /**
22
+ * Whether this adapter implements the optional `listFiles()`. Passed
23
+ * explicitly, like `capabilities` in the record suite, so the listFiles
24
+ * describe block can be registered or skipped at vitest's synchronous
25
+ * collection time rather than probed off an opened instance.
26
+ */
27
+ listFiles?: boolean;
28
+ }
29
+ export declare function runBlobAdapterConformance(options: BlobAdapterConformanceOptions): void;
30
+ //# sourceMappingURL=blob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAYjE,MAAM,WAAW,6BAA6B;IAC5C,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC;IACzD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5D;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAwFtF"}
package/dist/blob.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Blob adapter conformance suite
3
+ * -------------------------------------------------------
4
+ * The blob half of the adapter contract is small (StackBlobAdapter is three
5
+ * required methods plus one optional one) but has the same failure shape as
6
+ * the record half: content-addressing and the fileId error contract are
7
+ * documented in prose and easy to get subtly wrong in a from-scratch
8
+ * implementation — a fileId that isn't actually the SHA-256 of the bytes
9
+ * it names breaks any caller that compares fileIds instead of re-fetching,
10
+ * and a malformed fileId misreported as `not_found` instead of `bad_request`
11
+ * hides a caller bug behind the wrong error class.
12
+ */
13
+ import { describe, test, expect, beforeEach, afterEach } from 'vitest';
14
+ import { expectStackErrorCode } from './errors.js';
15
+ const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
16
+ async function sha256Hex(data) {
17
+ const digest = await crypto.subtle.digest('SHA-256', data);
18
+ return Array.from(new Uint8Array(digest))
19
+ .map((b) => b.toString(16).padStart(2, '0'))
20
+ .join('');
21
+ }
22
+ export function runBlobAdapterConformance(options) {
23
+ const { name, open, close, listFiles = false } = options;
24
+ describe(`blob adapter conformance: ${name}`, () => {
25
+ let adapter;
26
+ // `open()` can reject; `adapter` is then unset (or still holds the
27
+ // previous test's, already closed). Closing either way throws over
28
+ // the top of the real initialization error and hides it.
29
+ let opened = false;
30
+ beforeEach(async () => {
31
+ opened = false;
32
+ adapter = await open();
33
+ opened = true;
34
+ });
35
+ afterEach(async () => {
36
+ if (!opened)
37
+ return;
38
+ opened = false;
39
+ await close?.(adapter);
40
+ });
41
+ test('putAttachment returns the SHA-256 hex of the stored bytes', async () => {
42
+ const data = new TextEncoder().encode('hello attachment');
43
+ const fileId = await adapter.putAttachment(data);
44
+ expect(fileId).toMatch(SHA256_HEX_RE);
45
+ expect(fileId).toBe(await sha256Hex(data));
46
+ });
47
+ test('getAttachment returns exactly the bytes that were stored', async () => {
48
+ const data = new TextEncoder().encode('roundtrip me');
49
+ const fileId = await adapter.putAttachment(data);
50
+ const retrieved = await adapter.getAttachment(fileId);
51
+ expect(new Uint8Array(retrieved)).toEqual(data);
52
+ });
53
+ test('putAttachment is content-addressed — identical bytes yield the same fileId', async () => {
54
+ const data = new TextEncoder().encode('same content twice');
55
+ const first = await adapter.putAttachment(data);
56
+ const second = await adapter.putAttachment(data);
57
+ expect(first).toBe(second);
58
+ });
59
+ test('getAttachment reports not_found for a well-formed but unknown fileId', async () => {
60
+ const neverStored = 'a'.repeat(64);
61
+ await expectStackErrorCode(adapter.getAttachment(neverStored), 'not_found');
62
+ });
63
+ test('getAttachment reports bad_request for a malformed fileId', async () => {
64
+ await expectStackErrorCode(adapter.getAttachment('not-a-sha256-hash'), 'bad_request');
65
+ });
66
+ test('deleteAttachment removes the blob — a later getAttachment reports not_found', async () => {
67
+ const fileId = await adapter.putAttachment(new TextEncoder().encode('temporary'));
68
+ await adapter.deleteAttachment(fileId);
69
+ await expectStackErrorCode(adapter.getAttachment(fileId), 'not_found');
70
+ });
71
+ test('deleteAttachment is non-fatal for a fileId that was never stored', async () => {
72
+ await expect(adapter.deleteAttachment('b'.repeat(64))).resolves.toBeUndefined();
73
+ });
74
+ if (listFiles) {
75
+ describe('listFiles', () => {
76
+ test('returns an empty array for an empty store', async () => {
77
+ expect(await adapter.listFiles()).toEqual([]);
78
+ });
79
+ test('lists every stored blob with its fileId, size and modifiedAt', async () => {
80
+ const data = new TextEncoder().encode('a stored blob');
81
+ const fileId = await adapter.putAttachment(data);
82
+ const files = await adapter.listFiles();
83
+ const entry = files.find((f) => f.fileId === fileId);
84
+ expect(entry?.size).toBe(data.byteLength);
85
+ expect(entry?.modifiedAt).toBeInstanceOf(Date);
86
+ });
87
+ test('a deleted blob no longer appears', async () => {
88
+ const fileId = await adapter.putAttachment(new TextEncoder().encode('gone soon'));
89
+ await adapter.deleteAttachment(fileId);
90
+ const files = await adapter.listFiles();
91
+ expect(files.some((f) => f.fileId === fileId)).toBe(false);
92
+ });
93
+ });
94
+ }
95
+ });
96
+ }
97
+ //# sourceMappingURL=blob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Error assertions
3
+ * -------------------------------------------------------
4
+ * Checked against `.code` — the wire discriminator every StackError
5
+ * subclass carries — rather than `instanceof`. An RPC-backed adapter (a
6
+ * Durable Object, a remote server process) reconstructs a thrown error on
7
+ * the far side of that boundary without its original prototype chain, so
8
+ * `instanceof StackConflictError` can fail even when the adapter behaved
9
+ * correctly; `.code` is what survives. Using it here is what lets this
10
+ * suite run unmodified against an adapter that lives behind such a
11
+ * boundary, not just one that shares a process with the test runner.
12
+ */
13
+ import type { StackErrorCode } from '@haverstack/core';
14
+ /** Assert `promise` rejects with a StackError carrying this `code`. */
15
+ export declare function expectStackErrorCode(promise: Promise<unknown>, code: StackErrorCode): Promise<void>;
16
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,uEAAuE;AACvE,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACzB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf"}
package/dist/errors.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Error assertions
3
+ * -------------------------------------------------------
4
+ * Checked against `.code` — the wire discriminator every StackError
5
+ * subclass carries — rather than `instanceof`. An RPC-backed adapter (a
6
+ * Durable Object, a remote server process) reconstructs a thrown error on
7
+ * the far side of that boundary without its original prototype chain, so
8
+ * `instanceof StackConflictError` can fail even when the adapter behaved
9
+ * correctly; `.code` is what survives. Using it here is what lets this
10
+ * suite run unmodified against an adapter that lives behind such a
11
+ * boundary, not just one that shares a process with the test runner.
12
+ */
13
+ import { expect } from 'vitest';
14
+ /** Assert `promise` rejects with a StackError carrying this `code`. */
15
+ export async function expectStackErrorCode(promise, code) {
16
+ let caught;
17
+ try {
18
+ await promise;
19
+ }
20
+ catch (err) {
21
+ caught = err;
22
+ }
23
+ if (caught === undefined) {
24
+ expect.fail(`expected the call to reject with a StackError coded "${code}", but it resolved`);
25
+ }
26
+ expect(caught.code).toBe(code);
27
+ }
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAyB,EACzB,IAAoB;IAEpB,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,wDAAwD,IAAI,oBAAoB,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,CAAE,MAA6B,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Fixture builders shared by the record and blob suites. Deliberately
3
+ * schema-light: content-path filtering and sorting are exercised against
4
+ * raw `content` shapes without a registered StackType wherever a real
5
+ * adapter's own behavior doesn't require one — see the comments at each
6
+ * call site in record.ts for the one exception (content-field sort, which
7
+ * MemoryAdapter resolves through a registered type's schema).
8
+ */
9
+ import type { StackRecord, StackType } from '@haverstack/core';
10
+ /** A fresh id on every call, unique within a test run. */
11
+ export declare function uniqueId(prefix?: string): string;
12
+ export declare const CONFORMANCE_TYPE_ID = "org.haverstack.conformance/note@1";
13
+ export declare function conformanceType(overrides?: Partial<StackType>): StackType;
14
+ export declare function makeRecord(overrides?: Partial<StackRecord>): StackRecord;
15
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI/D,0DAA0D;AAC1D,wBAAgB,QAAQ,CAAC,MAAM,SAAQ,GAAG,MAAM,CAG/C;AAED,eAAO,MAAM,mBAAmB,sCAAsC,CAAC;AAEvE,wBAAgB,eAAe,CAAC,SAAS,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,SAAS,CAc7E;AAED,wBAAgB,UAAU,CAAC,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAU5E"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Fixture builders shared by the record and blob suites. Deliberately
3
+ * schema-light: content-path filtering and sorting are exercised against
4
+ * raw `content` shapes without a registered StackType wherever a real
5
+ * adapter's own behavior doesn't require one — see the comments at each
6
+ * call site in record.ts for the one exception (content-field sort, which
7
+ * MemoryAdapter resolves through a registered type's schema).
8
+ */
9
+ let counter = 0;
10
+ /** A fresh id on every call, unique within a test run. */
11
+ export function uniqueId(prefix = 'rec') {
12
+ counter += 1;
13
+ return `${prefix}-${Date.now().toString(36)}-${counter.toString(36)}`;
14
+ }
15
+ export const CONFORMANCE_TYPE_ID = 'org.haverstack.conformance/note@1';
16
+ export function conformanceType(overrides = {}) {
17
+ return {
18
+ id: CONFORMANCE_TYPE_ID,
19
+ baseId: 'org.haverstack.conformance/note',
20
+ version: 1,
21
+ name: 'Conformance Note',
22
+ schema: {
23
+ title: { kind: 'string' },
24
+ priority: { kind: 'number' },
25
+ },
26
+ schemaHash: 'conformance-note-v1',
27
+ createdAt: new Date(),
28
+ ...overrides,
29
+ };
30
+ }
31
+ export function makeRecord(overrides = {}) {
32
+ return {
33
+ id: uniqueId(),
34
+ typeId: CONFORMANCE_TYPE_ID,
35
+ createdAt: new Date(),
36
+ updatedAt: new Date(),
37
+ content: { title: 'Untitled' },
38
+ version: 1,
39
+ ...overrides,
40
+ };
41
+ }
42
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,0DAA0D;AAC1D,MAAM,UAAU,QAAQ,CAAC,MAAM,GAAG,KAAK;IACrC,OAAO,IAAI,CAAC,CAAC;IACb,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAEvE,MAAM,UAAU,eAAe,CAAC,YAAgC,EAAE;IAChE,OAAO;QACL,EAAE,EAAE,mBAAmB;QACvB,MAAM,EAAE,iCAAiC;QACzC,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE;YACN,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC7B;QACD,UAAU,EAAE,qBAAqB;QACjC,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,YAAkC,EAAE;IAC7D,OAAO;QACL,EAAE,EAAE,QAAQ,EAAE;QACd,MAAM,EAAE,mBAAmB;QAC3B,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;QAC9B,OAAO,EAAE,CAAC;QACV,GAAG,SAAS;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { runRecordAdapterConformance } from './record.js';
2
+ export type { RecordAdapterConformanceOptions } from './record.js';
3
+ export { runBlobAdapterConformance } from './blob.js';
4
+ export type { BlobAdapterConformanceOptions } from './blob.js';
5
+ export { expectStackErrorCode } from './errors.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { runRecordAdapterConformance } from './record.js';
2
+ export { runBlobAdapterConformance } from './blob.js';
3
+ export { expectStackErrorCode } from './errors.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Record adapter conformance suite
3
+ * -------------------------------------------------------
4
+ * The middle ground docs/spec/adapters.md describes in prose and, until
5
+ * now, nothing runnable checked a third-party StackRecordAdapter against:
6
+ * an adapter author calls runRecordAdapterConformance() with a way to open
7
+ * their adapter and the capabilities it declares, and gets back the
8
+ * invariants every first-party adapter is held to — FTS5-style search
9
+ * consistency, content-path semantics, version snapshots, `_config`
10
+ * protection, cursor stability, and capability honesty.
11
+ *
12
+ * Capability-gated: `capabilities` is read once, before any test is
13
+ * registered, because vitest builds its test tree synchronously at
14
+ * collection time — long before a `beforeEach` opening a fresh adapter
15
+ * instance has ever run. An adapter that doesn't declare a capability is
16
+ * never penalized for lacking it; declaring one wrong (claiming
17
+ * `filter.content: 'path'` while only matching whole field names, say)
18
+ * is caught because every filtering test here includes a record that
19
+ * must NOT match, not just one that should — the same "unfiltered
20
+ * superset presented as a filtered result" failure
21
+ * assertQueryCapabilities() exists to prevent.
22
+ */
23
+ import type { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
24
+ export interface RecordAdapterConformanceOptions {
25
+ /** Used in describe() titles, so pick something that reads well there. */
26
+ name: string;
27
+ /** Produce a fresh, empty adapter instance. Called before every test. */
28
+ open: () => Promise<StackRecordAdapter> | StackRecordAdapter;
29
+ /** Release what `open()` acquired. Called after every test. */
30
+ close?: (adapter: StackRecordAdapter) => Promise<void> | void;
31
+ /**
32
+ * The capabilities this adapter declares — the same object its own
33
+ * `capabilities` getter returns. Passed explicitly rather than read off
34
+ * an opened instance so suite registration stays synchronous; see the
35
+ * module doc above.
36
+ */
37
+ capabilities: AdapterCapabilities;
38
+ }
39
+ export declare function runRecordAdapterConformance(options: RecordAdapterConformanceOptions): void;
40
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIxF,MAAM,WAAW,+BAA+B;IAC9C,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAAC;IAC7D,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,+BAA+B,GAAG,IAAI,CAopB1F"}
package/dist/record.js ADDED
@@ -0,0 +1,560 @@
1
+ /**
2
+ * Record adapter conformance suite
3
+ * -------------------------------------------------------
4
+ * The middle ground docs/spec/adapters.md describes in prose and, until
5
+ * now, nothing runnable checked a third-party StackRecordAdapter against:
6
+ * an adapter author calls runRecordAdapterConformance() with a way to open
7
+ * their adapter and the capabilities it declares, and gets back the
8
+ * invariants every first-party adapter is held to — FTS5-style search
9
+ * consistency, content-path semantics, version snapshots, `_config`
10
+ * protection, cursor stability, and capability honesty.
11
+ *
12
+ * Capability-gated: `capabilities` is read once, before any test is
13
+ * registered, because vitest builds its test tree synchronously at
14
+ * collection time — long before a `beforeEach` opening a fresh adapter
15
+ * instance has ever run. An adapter that doesn't declare a capability is
16
+ * never penalized for lacking it; declaring one wrong (claiming
17
+ * `filter.content: 'path'` while only matching whole field names, say)
18
+ * is caught because every filtering test here includes a record that
19
+ * must NOT match, not just one that should — the same "unfiltered
20
+ * superset presented as a filtered result" failure
21
+ * assertQueryCapabilities() exists to prevent.
22
+ */
23
+ import { describe, test, expect, beforeEach, afterEach } from 'vitest';
24
+ import { SYSTEM_TYPES } from '@haverstack/core';
25
+ import { expectStackErrorCode } from './errors.js';
26
+ import { CONFORMANCE_TYPE_ID, conformanceType, makeRecord, uniqueId } from './helpers.js';
27
+ export function runRecordAdapterConformance(options) {
28
+ const { name, open, close, capabilities } = options;
29
+ describe(`record adapter conformance: ${name}`, () => {
30
+ let adapter;
31
+ // `open()` can reject; `adapter` is then unset (or still holds the
32
+ // previous test's, already closed). Closing either way throws over
33
+ // the top of the real initialization error and hides it.
34
+ let opened = false;
35
+ beforeEach(async () => {
36
+ opened = false;
37
+ adapter = await open();
38
+ opened = true;
39
+ });
40
+ afterEach(async () => {
41
+ if (!opened)
42
+ return;
43
+ opened = false;
44
+ await close?.(adapter);
45
+ });
46
+ // -----------------------------------------------------------------
47
+ // Capabilities
48
+ // -----------------------------------------------------------------
49
+ describe('capabilities', () => {
50
+ test('declares a well-formed AdapterCapabilities object', () => {
51
+ expect(['none', 'field', 'path']).toContain(capabilities.filter.content);
52
+ expect(typeof capabilities.filter.contentPresent).toBe('boolean');
53
+ expect(typeof capabilities.filter.search).toBe('boolean');
54
+ expect(Array.isArray(capabilities.sort.fields)).toBe(true);
55
+ expect(typeof capabilities.sort.contentField).toBe('boolean');
56
+ });
57
+ test('filter.contentPresent is false whenever filter.content is "none"', () => {
58
+ // contentPresent asks whether a value is there at all — meaningless
59
+ // to promise from an adapter that reaches no content to look at.
60
+ if (capabilities.filter.content === 'none') {
61
+ expect(capabilities.filter.contentPresent).toBe(false);
62
+ }
63
+ });
64
+ });
65
+ // -----------------------------------------------------------------
66
+ // Types
67
+ // -----------------------------------------------------------------
68
+ describe('types', () => {
69
+ test('saveType and getType roundtrip', async () => {
70
+ const type = conformanceType();
71
+ await adapter.saveType(type);
72
+ const stored = await adapter.getType(type.id);
73
+ expect(stored?.id).toBe(type.id);
74
+ expect(stored?.schema).toEqual(type.schema);
75
+ });
76
+ test('getType returns null for an unknown id', async () => {
77
+ expect(await adapter.getType('org.haverstack.conformance/nope@1')).toBeNull();
78
+ });
79
+ test('listTypes returns every saved type', async () => {
80
+ await adapter.saveType(conformanceType());
81
+ await adapter.saveType(conformanceType({ id: 'org.haverstack.conformance/other@1' }));
82
+ const types = await adapter.listTypes();
83
+ expect(types.map((t) => t.id).sort()).toEqual(['org.haverstack.conformance/other@1', CONFORMANCE_TYPE_ID].sort());
84
+ });
85
+ });
86
+ // -----------------------------------------------------------------
87
+ // CRUD
88
+ // -----------------------------------------------------------------
89
+ describe('records — CRUD', () => {
90
+ test('createRecord and getRecord roundtrip', async () => {
91
+ const record = makeRecord({ content: { title: 'Hello' } });
92
+ await adapter.createRecord(record);
93
+ const stored = await adapter.getRecord(record.id);
94
+ expect(stored?.id).toBe(record.id);
95
+ expect(stored?.content).toEqual({ title: 'Hello' });
96
+ });
97
+ test('getRecord returns null for an unknown id', async () => {
98
+ expect(await adapter.getRecord(uniqueId('missing'))).toBeNull();
99
+ });
100
+ test('createRecord throws a conflict for a duplicate id, never a silent overwrite', async () => {
101
+ const record = makeRecord();
102
+ await adapter.createRecord(record);
103
+ await expectStackErrorCode(adapter.createRecord(makeRecord({ id: record.id })), 'conflict');
104
+ expect((await adapter.getRecord(record.id))?.content).toEqual(record.content);
105
+ });
106
+ test('mutateRecord merges the content patch at the top level and bumps version', async () => {
107
+ const record = makeRecord({ content: { title: 'Before', priority: 1 } });
108
+ await adapter.createRecord(record);
109
+ const updated = await adapter.mutateRecord(record.id, {
110
+ contentPatch: { title: 'After' },
111
+ });
112
+ expect(updated.content).toEqual({ title: 'After', priority: 1 });
113
+ expect(updated.version).toBe(record.version + 1);
114
+ });
115
+ test('soft deleteRecord sets deletedAt; undeleteRecord clears it', async () => {
116
+ const record = makeRecord();
117
+ await adapter.createRecord(record);
118
+ const deleted = await adapter.deleteRecord(record.id);
119
+ expect(deleted?.deletedAt).toBeInstanceOf(Date);
120
+ const undeleted = await adapter.undeleteRecord(record.id);
121
+ expect(undeleted.deletedAt).toBeUndefined();
122
+ });
123
+ test('hard deleteRecord removes the record and its version history', async () => {
124
+ const record = makeRecord();
125
+ await adapter.createRecord(record);
126
+ await adapter.saveVersion(record.id, {
127
+ version: record.version,
128
+ typeId: record.typeId,
129
+ content: record.content,
130
+ updatedAt: record.updatedAt,
131
+ });
132
+ await adapter.deleteRecord(record.id, { hard: true });
133
+ expect(await adapter.getRecord(record.id)).toBeNull();
134
+ expect(await adapter.getVersions(record.id)).toEqual([]);
135
+ });
136
+ });
137
+ // -----------------------------------------------------------------
138
+ // The `_config` singleton — reserved, never enumerable
139
+ // -----------------------------------------------------------------
140
+ describe('the `_config` record', () => {
141
+ test('queryRecords never returns a record whose id is the reserved `_config` id', async () => {
142
+ // Every first-party adapter creates this singleton during its own
143
+ // initialization, outside this interface — nothing here writes it.
144
+ // The invariant holds either way: if the adapter under test has one,
145
+ // a generic query must still never surface it.
146
+ const { records } = await adapter.queryRecords({});
147
+ expect(records.some((r) => r.id === SYSTEM_TYPES.CONFIG)).toBe(false);
148
+ });
149
+ });
150
+ // -----------------------------------------------------------------
151
+ // Associations
152
+ // -----------------------------------------------------------------
153
+ describe('associations', () => {
154
+ test('associate adds a tag; dissociate removes it; neither bumps version', async () => {
155
+ const record = makeRecord();
156
+ await adapter.createRecord(record);
157
+ const tagged = await adapter.associate(record.id, { kind: 'tag', label: 'important' });
158
+ expect(tagged.associations).toContainEqual({ kind: 'tag', label: 'important' });
159
+ expect(tagged.version).toBe(record.version);
160
+ const untagged = await adapter.dissociate(record.id, {
161
+ kind: 'tag',
162
+ label: 'important',
163
+ });
164
+ expect(untagged.associations ?? []).not.toContainEqual({
165
+ kind: 'tag',
166
+ label: 'important',
167
+ });
168
+ expect(untagged.version).toBe(record.version);
169
+ });
170
+ test('associate is idempotent — a duplicate does not create a second entry', async () => {
171
+ const record = makeRecord();
172
+ await adapter.createRecord(record);
173
+ await adapter.associate(record.id, { kind: 'tag', label: 'dup' });
174
+ const twice = await adapter.associate(record.id, { kind: 'tag', label: 'dup' });
175
+ expect(twice.associations?.filter((a) => a.kind === 'tag' && a.label === 'dup')).toHaveLength(1);
176
+ });
177
+ test('associate on a nonexistent record reports not_found rather than creating an orphan', async () => {
178
+ await expectStackErrorCode(adapter.associate(uniqueId('missing'), { kind: 'tag', label: 'x' }), 'not_found');
179
+ });
180
+ });
181
+ // -----------------------------------------------------------------
182
+ // Versions — snapshots and restore
183
+ // -----------------------------------------------------------------
184
+ describe('versions', () => {
185
+ test('saveVersion and getVersion roundtrip', async () => {
186
+ const record = makeRecord();
187
+ await adapter.createRecord(record);
188
+ await adapter.saveVersion(record.id, {
189
+ version: record.version,
190
+ typeId: record.typeId,
191
+ content: record.content,
192
+ updatedAt: record.updatedAt,
193
+ });
194
+ const version = await adapter.getVersion(record.id, record.version);
195
+ expect(version?.content).toEqual(record.content);
196
+ });
197
+ test('restoreVersion restores content and parentId, bumps version, and never touches associations', async () => {
198
+ const record = makeRecord({ content: { title: 'v1' } });
199
+ await adapter.createRecord(record);
200
+ await adapter.saveVersion(record.id, {
201
+ version: record.version,
202
+ typeId: record.typeId,
203
+ content: record.content,
204
+ updatedAt: record.updatedAt,
205
+ });
206
+ await adapter.mutateRecord(record.id, { contentPatch: { title: 'v2' } });
207
+ await adapter.associate(record.id, { kind: 'tag', label: 'keep-me' });
208
+ const restored = await adapter.restoreVersion(record.id, record.version);
209
+ expect(restored.content).toEqual({ title: 'v1' });
210
+ expect(restored.version).toBe(record.version + 2); // patch, then restore
211
+ expect(restored.associations).toContainEqual({ kind: 'tag', label: 'keep-me' });
212
+ });
213
+ test('restoreVersion throws not_found for an unknown version', async () => {
214
+ const record = makeRecord();
215
+ await adapter.createRecord(record);
216
+ await expectStackErrorCode(adapter.restoreVersion(record.id, 999), 'not_found');
217
+ });
218
+ });
219
+ // -----------------------------------------------------------------
220
+ // Change journal — the second durable tier
221
+ // -----------------------------------------------------------------
222
+ describe('change journal', () => {
223
+ test('getJournal is implemented, and an empty log means nothing changed', async () => {
224
+ // Not an optional method: "nothing changed" and "this adapter does
225
+ // not remember" are different answers, and an empty log has to mean
226
+ // the first unconditionally. See docs/spec/journal.md § Reading it.
227
+ expect(typeof adapter.getJournal).toBe('function');
228
+ const record = makeRecord();
229
+ await adapter.createRecord(record);
230
+ expect(await adapter.getJournal(record.id)).toEqual([]);
231
+ });
232
+ test('an entry is stamped from the row the write produced, not from the caller', async () => {
233
+ const record = makeRecord({ content: { title: 'v1' } });
234
+ await adapter.createRecord(record, {
235
+ journal: { ops: ['create'], kind: 'created' },
236
+ });
237
+ await adapter.mutateRecord(record.id, { contentPatch: { title: 'v2' } }, { bumpsVersion: true, journal: { ops: ['patch'], kind: 'changed' } });
238
+ const log = await adapter.getJournal(record.id);
239
+ expect(log).toHaveLength(2);
240
+ // version, typeId and parentId are the adapter's to stamp: Stack
241
+ // supplies only the half the record cannot report afterwards.
242
+ expect(log[0].version).toBe(record.version);
243
+ expect(log[1].version).toBe(record.version + 1);
244
+ expect(log[0].typeId).toBe(record.typeId);
245
+ expect(log[0].at).toBeInstanceOf(Date);
246
+ });
247
+ test('seq is dense from 1, ascending, and counted per record', async () => {
248
+ const first = makeRecord();
249
+ const second = makeRecord();
250
+ await adapter.createRecord(first, { journal: { ops: ['create'], kind: 'created' } });
251
+ await adapter.createRecord(second, { journal: { ops: ['create'], kind: 'created' } });
252
+ await adapter.associate(first.id, { kind: 'tag', label: 'a' }, { journal: { ops: ['associate'], kind: 'changed' } });
253
+ await adapter.associate(first.id, { kind: 'tag', label: 'b' }, { journal: { ops: ['associate'], kind: 'changed' } });
254
+ expect((await adapter.getJournal(first.id)).map((e) => e.seq)).toEqual([1, 2, 3]);
255
+ // Per record, so a busy neighbour never advances this one's log.
256
+ expect((await adapter.getJournal(second.id)).map((e) => e.seq)).toEqual([1]);
257
+ });
258
+ test('an association change journals a version that stands still', async () => {
259
+ const record = makeRecord();
260
+ await adapter.createRecord(record);
261
+ await adapter.associate(record.id, { kind: 'tag', label: 'starred' }, {
262
+ journal: {
263
+ ops: ['associate'],
264
+ kind: 'changed',
265
+ associationsAdded: [{ kind: 'tag', label: 'starred' }],
266
+ },
267
+ });
268
+ await adapter.dissociate(record.id, { kind: 'tag', label: 'starred' }, {
269
+ journal: {
270
+ ops: ['dissociate'],
271
+ kind: 'changed',
272
+ associationsRemoved: [{ kind: 'tag', label: 'starred' }],
273
+ },
274
+ });
275
+ const log = await adapter.getJournal(record.id);
276
+ expect(log.map((e) => e.version)).toEqual([record.version, record.version]);
277
+ expect(log[0].associationsAdded).toEqual([{ kind: 'tag', label: 'starred' }]);
278
+ expect(log[1].associationsRemoved).toEqual([{ kind: 'tag', label: 'starred' }]);
279
+ });
280
+ test('associationsReplaced survives the round trip', async () => {
281
+ // The one field with no counterpart on the change feed, and the
282
+ // whole reason this tier exists: nothing else retains the
283
+ // attachmentRecordId a re-point discarded.
284
+ const record = makeRecord();
285
+ await adapter.createRecord(record);
286
+ const replaced = {
287
+ kind: 'attachment',
288
+ label: 'avatar',
289
+ fileId: 'a'.repeat(64),
290
+ attachmentRecordId: uniqueId('old'),
291
+ };
292
+ await adapter.associate(record.id, { kind: 'attachment', label: 'avatar', fileId: 'a'.repeat(64) }, {
293
+ journal: {
294
+ ops: ['associate'],
295
+ kind: 'changed',
296
+ associationsAdded: [{ kind: 'attachment', label: 'avatar', fileId: 'a'.repeat(64) }],
297
+ associationsReplaced: [replaced],
298
+ },
299
+ });
300
+ const [entry] = await adapter.getJournal(record.id);
301
+ expect(entry.associationsReplaced).toEqual([replaced]);
302
+ });
303
+ test('previousParentId tells "did not move" apart from "moved off the root"', async () => {
304
+ // The one field on any entry where null is a value rather than an
305
+ // input spelling. Collapsing both to absent loses which happened.
306
+ const parent = makeRecord();
307
+ await adapter.createRecord(parent);
308
+ const record = makeRecord();
309
+ await adapter.createRecord(record, { journal: { ops: ['create'], kind: 'created' } });
310
+ await adapter.mutateRecord(record.id, { parentId: parent.id }, {
311
+ bumpsVersion: true,
312
+ journal: { ops: ['reparent'], kind: 'changed', previousParentId: null },
313
+ });
314
+ const log = await adapter.getJournal(record.id);
315
+ expect(log[0].previousParentId).toBeUndefined();
316
+ expect(log[1].previousParentId).toBeNull();
317
+ expect(log[1].parentId).toBe(parent.id);
318
+ });
319
+ test('sinceSeq is exclusive, limit bounds, and omitting both reads the whole log', async () => {
320
+ const record = makeRecord();
321
+ await adapter.createRecord(record, { journal: { ops: ['create'], kind: 'created' } });
322
+ for (const label of ['a', 'b', 'c']) {
323
+ await adapter.associate(record.id, { kind: 'tag', label }, { journal: { ops: ['associate'], kind: 'changed' } });
324
+ }
325
+ expect(await adapter.getJournal(record.id)).toHaveLength(4);
326
+ expect((await adapter.getJournal(record.id, { sinceSeq: 2 })).map((e) => e.seq)).toEqual([
327
+ 3, 4,
328
+ ]);
329
+ expect((await adapter.getJournal(record.id, { limit: 2 })).map((e) => e.seq)).toEqual([
330
+ 1, 2,
331
+ ]);
332
+ expect((await adapter.getJournal(record.id, { sinceSeq: 1, limit: 2 })).map((e) => e.seq)).toEqual([2, 3]);
333
+ expect(await adapter.getJournal(record.id, { sinceSeq: 99 })).toEqual([]);
334
+ });
335
+ test('a failed mutation leaves no entry behind', async () => {
336
+ const missing = uniqueId('missing');
337
+ await expectStackErrorCode(adapter.mutateRecord(missing, { contentPatch: { title: 'x' } }, { bumpsVersion: true, journal: { ops: ['patch'], kind: 'changed' } }), 'not_found');
338
+ expect(await adapter.getJournal(missing)).toEqual([]);
339
+ });
340
+ test('a soft delete keeps the journal; a hard delete destroys it', async () => {
341
+ const record = makeRecord();
342
+ await adapter.createRecord(record, { journal: { ops: ['create'], kind: 'created' } });
343
+ await adapter.deleteRecord(record.id, {
344
+ journal: { ops: ['delete'], kind: 'deleted' },
345
+ });
346
+ // A tombstone is recoverable, and its journal is part of what
347
+ // recovers it.
348
+ expect(await adapter.getJournal(record.id)).toHaveLength(2);
349
+ await adapter.deleteRecord(record.id, { hard: true });
350
+ expect(await adapter.getJournal(record.id)).toEqual([]);
351
+ // And the log restarts: a purge that left rows behind would hand
352
+ // the next record at this id a history that is not its own.
353
+ await adapter.createRecord(makeRecord({ id: record.id }), {
354
+ journal: { ops: ['create'], kind: 'created' },
355
+ });
356
+ expect((await adapter.getJournal(record.id)).map((e) => e.seq)).toEqual([1]);
357
+ });
358
+ });
359
+ // -----------------------------------------------------------------
360
+ // Migration
361
+ // -----------------------------------------------------------------
362
+ describe('commitMigration', () => {
363
+ test('changes typeId and content together, and bumps version', async () => {
364
+ const record = makeRecord({ typeId: 'org.haverstack.conformance/note@1' });
365
+ await adapter.createRecord(record);
366
+ const migrated = await adapter.commitMigration(record.id, 'org.haverstack.conformance/note@2', { title: 'migrated' });
367
+ expect(migrated.typeId).toBe('org.haverstack.conformance/note@2');
368
+ expect(migrated.content).toEqual({ title: 'migrated' });
369
+ expect(migrated.version).toBe(record.version + 1);
370
+ });
371
+ });
372
+ // -----------------------------------------------------------------
373
+ // Cursor pagination
374
+ // -----------------------------------------------------------------
375
+ describe('cursor pagination', () => {
376
+ test('pages through more records than fit in one page', async () => {
377
+ const ids = [];
378
+ for (let i = 0; i < 5; i++) {
379
+ const record = makeRecord();
380
+ ids.push(record.id);
381
+ await adapter.createRecord(record);
382
+ }
383
+ const seen = new Set();
384
+ let cursor;
385
+ do {
386
+ const page = await adapter.queryRecords({ limit: 2, cursor: cursor ?? undefined });
387
+ expect(page.records.length).toBeLessThanOrEqual(2);
388
+ for (const r of page.records)
389
+ seen.add(r.id);
390
+ cursor = page.cursor;
391
+ } while (cursor);
392
+ for (const id of ids)
393
+ expect(seen.has(id)).toBe(true);
394
+ });
395
+ test('a malformed cursor is refused rather than misread', async () => {
396
+ await expectStackErrorCode(adapter.queryRecords({ cursor: 'not-a-real-cursor' }), 'bad_request');
397
+ });
398
+ test('a cursor minted under one sort field is refused when replayed under a different one', async () => {
399
+ await adapter.createRecord(makeRecord());
400
+ await adapter.createRecord(makeRecord());
401
+ const minted = await adapter.queryRecords({
402
+ limit: 1,
403
+ sort: { field: 'createdAt' },
404
+ });
405
+ expect(minted.cursor).not.toBeNull();
406
+ await expectStackErrorCode(adapter.queryRecords({
407
+ limit: 1,
408
+ cursor: minted.cursor,
409
+ sort: { field: 'updatedAt' },
410
+ }), 'bad_request');
411
+ });
412
+ });
413
+ // -----------------------------------------------------------------
414
+ // Content filtering — capability-gated on filter.content
415
+ // -----------------------------------------------------------------
416
+ if (capabilities.filter.content !== 'none') {
417
+ describe('content filtering', () => {
418
+ test('filters by a top-level scalar field, excluding what does not match', async () => {
419
+ const match = makeRecord({ content: { title: 'a', priority: 1 } });
420
+ const decoy = makeRecord({ content: { title: 'b', priority: 2 } });
421
+ await adapter.createRecord(match);
422
+ await adapter.createRecord(decoy);
423
+ const result = await adapter.queryRecords({ filter: { content: { priority: 1 } } });
424
+ // The positive assertion first: `not.toContain` and `every` are
425
+ // both vacuously true on an empty result, so without this an
426
+ // adapter that declares filter.content and returns nothing at
427
+ // all would pass the very test meant to catch it.
428
+ expect(result.records.map((r) => r.id)).toContain(match.id);
429
+ expect(result.records.map((r) => r.id)).not.toContain(decoy.id);
430
+ expect(result.records.every((r) => r.content.priority === 1)).toBe(true);
431
+ });
432
+ test('a null filter value matches records where the field is absent or stored as null', async () => {
433
+ const absent = makeRecord({ content: { title: 'no priority' } });
434
+ const explicitNull = makeRecord({ content: { title: 'null priority', priority: null } });
435
+ const present = makeRecord({ content: { title: 'has one', priority: 1 } });
436
+ await adapter.createRecord(absent);
437
+ await adapter.createRecord(explicitNull);
438
+ await adapter.createRecord(present);
439
+ const result = await adapter.queryRecords({ filter: { content: { priority: null } } });
440
+ const ids = result.records.map((r) => r.id).sort();
441
+ expect(ids).toEqual([absent.id, explicitNull.id].sort());
442
+ expect(ids).not.toContain(present.id);
443
+ });
444
+ test('a scalar filter value never matches an object or array stored at the same path', async () => {
445
+ const scalarMatch = makeRecord({ content: { title: 'x', priority: 1 } });
446
+ const nested = makeRecord({ content: { title: 'y', priority: { high: true } } });
447
+ await adapter.createRecord(scalarMatch);
448
+ await adapter.createRecord(nested);
449
+ const result = await adapter.queryRecords({ filter: { content: { priority: 1 } } });
450
+ expect(result.records.map((r) => r.id)).toContain(scalarMatch.id);
451
+ expect(result.records.map((r) => r.id)).not.toContain(nested.id);
452
+ });
453
+ if (capabilities.filter.contentPresent) {
454
+ test('contentPresent matches records holding a non-null value at the path', async () => {
455
+ const present = makeRecord({ content: { title: 'x', priority: 1 } });
456
+ const absent = makeRecord({ content: { title: 'y' } });
457
+ const nullValue = makeRecord({ content: { title: 'z', priority: null } });
458
+ await adapter.createRecord(present);
459
+ await adapter.createRecord(absent);
460
+ await adapter.createRecord(nullValue);
461
+ const result = await adapter.queryRecords({ filter: { contentPresent: ['priority'] } });
462
+ expect(result.records.map((r) => r.id)).toEqual([present.id]);
463
+ });
464
+ }
465
+ if (capabilities.filter.content === 'path') {
466
+ test('an array is spread element-wise when filtering a nested path', async () => {
467
+ const match = makeRecord({
468
+ content: { title: 'x', emails: [{ value: 'a@b.c' }, { value: 'd@e.f' }] },
469
+ });
470
+ const noMatch = makeRecord({
471
+ content: { title: 'y', emails: [{ value: 'other@x.y' }] },
472
+ });
473
+ await adapter.createRecord(match);
474
+ await adapter.createRecord(noMatch);
475
+ const result = await adapter.queryRecords({
476
+ filter: { content: { 'emails.value': 'a@b.c' } },
477
+ });
478
+ expect(result.records.map((r) => r.id)).toEqual([match.id]);
479
+ });
480
+ test('a nested path filter reaches a property inside a plain (non-array) object', async () => {
481
+ const match = makeRecord({ content: { title: 'x', address: { city: 'Berlin' } } });
482
+ const noMatch = makeRecord({ content: { title: 'y', address: { city: 'Lyon' } } });
483
+ await adapter.createRecord(match);
484
+ await adapter.createRecord(noMatch);
485
+ const result = await adapter.queryRecords({
486
+ filter: { content: { 'address.city': 'Berlin' } },
487
+ });
488
+ expect(result.records.map((r) => r.id)).toEqual([match.id]);
489
+ });
490
+ test('a filter path descending through a scalar matches nothing, rather than throwing', async () => {
491
+ await adapter.createRecord(makeRecord({ content: { title: 'x', priority: 1 } }));
492
+ const result = await adapter.queryRecords({
493
+ filter: { content: { 'priority.nested': 'anything' } },
494
+ });
495
+ expect(result.records).toEqual([]);
496
+ });
497
+ }
498
+ });
499
+ }
500
+ // -----------------------------------------------------------------
501
+ // Sort by content field — capability-gated on sort.contentField
502
+ // -----------------------------------------------------------------
503
+ if (capabilities.sort.contentField) {
504
+ describe('sort by content field', () => {
505
+ test('orders records by a declared content field', async () => {
506
+ // Registered so an adapter that resolves the field's sort kind
507
+ // through the type schema (rather than off the raw JSON value)
508
+ // sorts numerically instead of falling back to no ordering.
509
+ await adapter.saveType(conformanceType());
510
+ await adapter.createRecord(makeRecord({ content: { title: 'x', priority: 3 } }));
511
+ await adapter.createRecord(makeRecord({ content: { title: 'y', priority: 1 } }));
512
+ await adapter.createRecord(makeRecord({ content: { title: 'z', priority: 2 } }));
513
+ const result = await adapter.queryRecords({
514
+ sort: { contentField: 'priority', direction: 'asc' },
515
+ });
516
+ expect(result.records.map((r) => r.content.priority)).toEqual([
517
+ 1, 2, 3,
518
+ ]);
519
+ });
520
+ });
521
+ }
522
+ // -----------------------------------------------------------------
523
+ // Full-text search — capability-gated on filter.search
524
+ // -----------------------------------------------------------------
525
+ if (capabilities.filter.search) {
526
+ describe('full-text search', () => {
527
+ test('finds a record containing the search term', async () => {
528
+ const match = makeRecord({ content: { title: 'the quick brown fox' } });
529
+ const noMatch = makeRecord({ content: { title: 'something unrelated' } });
530
+ await adapter.createRecord(match);
531
+ await adapter.createRecord(noMatch);
532
+ const result = await adapter.queryRecords({ filter: { search: 'brown' } });
533
+ expect(result.records.map((r) => r.id)).toContain(match.id);
534
+ expect(result.records.map((r) => r.id)).not.toContain(noMatch.id);
535
+ });
536
+ // The invariant the issue's docs/spec/adapters.md § FTS5 names
537
+ // explicitly: an external-content index's remove step must run
538
+ // before the row's content changes, or a stale entry stays
539
+ // searchable under content the record no longer holds.
540
+ test('a content patch is reflected in search — the old content is no longer searchable', async () => {
541
+ const record = makeRecord({ content: { title: 'original wording' } });
542
+ await adapter.createRecord(record);
543
+ await adapter.mutateRecord(record.id, { contentPatch: { title: 'updated wording' } });
544
+ const stale = await adapter.queryRecords({ filter: { search: 'original' } });
545
+ expect(stale.records.map((r) => r.id)).not.toContain(record.id);
546
+ const fresh = await adapter.queryRecords({ filter: { search: 'updated' } });
547
+ expect(fresh.records.map((r) => r.id)).toContain(record.id);
548
+ });
549
+ test('a hard-deleted record is no longer found by search', async () => {
550
+ const record = makeRecord({ content: { title: 'ephemeral content' } });
551
+ await adapter.createRecord(record);
552
+ await adapter.deleteRecord(record.id, { hard: true });
553
+ const result = await adapter.queryRecords({ filter: { search: 'ephemeral' } });
554
+ expect(result.records.map((r) => r.id)).not.toContain(record.id);
555
+ });
556
+ });
557
+ }
558
+ });
559
+ }
560
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@haverstack/adapter-conformance",
3
+ "version": "0.2.0",
4
+ "description": "Runnable conformance suite for StackRecordAdapter/StackBlobAdapter implementers — the invariants docs/spec/adapters.md documents, as tests you point at your own adapter",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/haverstack/core.git",
20
+ "directory": "packages/adapter-conformance"
21
+ },
22
+ "license": "CC0-1.0",
23
+ "keywords": [
24
+ "haverstack",
25
+ "conformance",
26
+ "adapter",
27
+ "storage",
28
+ "testing"
29
+ ],
30
+ "peerDependencies": {
31
+ "vitest": "^4.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@haverstack/core": "^0.33.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "typescript": "^5.5.0",
39
+ "vitest": "^4.1.11"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc --noEmit",
45
+ "lint": "eslint src tests"
46
+ }
47
+ }