@noy-db/test-format-conformance 0.7.0-pre.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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vLannaAi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @noy-db/test-format-conformance
2
+
3
+ The `as-*` export gate, published as an executable suite.
4
+
5
+ ```ts
6
+ import { runFormatConformanceTests } from '@noy-db/test-format-conformance'
7
+
8
+ runFormatConformanceTests('as-myformat', {
9
+ format: 'myformat',
10
+ vault: () => seededExportCapableVault(),
11
+ exports: [
12
+ { name: 'toString', run: (v) => toString(v, opts) },
13
+ { name: 'download', run: (v) => download(v, opts) },
14
+ { name: 'write', run: (v) => write(v, path, { ...opts, acknowledgeRisks: true }) },
15
+ ],
16
+ writeWithoutAcknowledgement: (v, path) => write(v, path, opts),
17
+ })
18
+ ```
19
+
20
+ ## What it checks
21
+
22
+ `as-*` is the one place plaintext leaves the vault. Every package calls
23
+ `vault.assertCanExport('plaintext', <format>)`, and that call is the whole
24
+ boundary. Nine packages had converged on the same shape by convention, and
25
+ convention is what the next format author reads instead of a contract.
26
+
27
+ For **every** entry point the fixture lists:
28
+
29
+ - it **refuses** when `assertCanExport` denies, and
30
+ - it refuses **before reading a single record**.
31
+
32
+ Plus: the `write` path refuses without `acknowledgeRisks: true`.
33
+
34
+ ## Gated is not the property. Gated BEFORE decrypting is.
35
+
36
+ A gate called after `exportStream` has run refuses the caller *and decrypts
37
+ anyway*. That is the property a delegation refactor breaks silently — move the
38
+ gate from `toObject` into `download` and every existing test still passes.
39
+
40
+ Proven, not asserted: moving as-csv's gate inside its `exportStream` loop
41
+ leaves both "REFUSES" cases green and fails all three "before reading" cases.
42
+
43
+ ## The two ways this suite could have been vacuous
44
+
45
+ Both were real, and both were found by trying to break it rather than by
46
+ reading it.
47
+
48
+ **A refusal is only evidence when the same call would otherwise succeed.**
49
+ The kit's first case runs an entry point on the *ungated* vault and requires it
50
+ to resolve. On its first run it failed `as-zip` — whose export path opens a
51
+ blob slot, so the fixture vault was refusing on *blob storage*, not on the
52
+ export gate. **Six green cases in a file I had just declared passing were
53
+ passing for the wrong reason.**
54
+
55
+ **`rejects.toThrow()` is absorbed by whichever guard fires first.** The
56
+ acknowledgement case originally used a vault with no `exportCapability` grant,
57
+ so `write` refused at the export gate and never reached the flag — deleting the
58
+ acknowledgement guard from as-csv left the suite green. Fixed twice over: the
59
+ case now matches on `/acknowledgeRisks/`, and the fixture is required to supply
60
+ an export-capable vault.
61
+
62
+ ## Why a Proxy and not a fake Vault
63
+
64
+ A hand-written double would drift from `Vault`, and would only ever exercise
65
+ the methods whoever wrote it thought of. The fixture supplies a **real** vault
66
+ and the kit wraps it, so an entry point reaching for some other decrypting
67
+ method is still observed.
68
+
69
+ ## Mutation-checked
70
+
71
+ | mutation | result |
72
+ |---|---|
73
+ | as-csv: gate deleted | 6 fail |
74
+ | as-csv: gate moved after the read | 3 fail — the "before reading" cases only |
75
+ | as-csv: `acknowledgeRisks` guard deleted | 1 fails |
76
+ | as-zip: gate deleted | 6 fail |
77
+
78
+ ## All nine formats bind it
79
+
80
+ `as-blob` · `as-csv` · `as-json` · `as-ndjson` · `as-noydb` · `as-sql` ·
81
+ `as-xlsx` · `as-xml` · `as-zip`.
82
+
83
+ Wiring them found that the family is **two capability tiers**, not one:
84
+
85
+ | tier | packages | gate |
86
+ |---|---|---|
87
+ | `plaintext` | eight | `assertCanExport('plaintext', <format>)` |
88
+ | `bundle` | `as-noydb` | `assertCanExport('bundle')` — no format |
89
+
90
+ `as-noydb` emits an **encrypted** pod, so it also has **no `acknowledgeRisks`
91
+ gate**, and its source says so twice. Its fixture therefore declares no
92
+ acknowledgement case — and the suite prints
93
+ `write: SKIPPED — … UNVERIFIED here` rather than staying quiet, which is the
94
+ difference between a documented absence and a hole.
95
+
96
+ `as-aws-s3` is not in the list: it exports `asAwsS3(options)` and is a
97
+ **destination, not a format**.
98
+
99
+ ## The vacuity guard earned its place twice
100
+
101
+ It fired on `as-zip` (no `withBlobs()`) and on `as-blob` (no blob attached to
102
+ the seeded record). In both cases six refusal assertions were green and
103
+ meaningless. Neither would have been visible from reading the output.
104
+
105
+ ## This kit is for EGRESS, not only for `as-*`
106
+
107
+ The family prefix is not the criterion. **Anything that puts plaintext where the
108
+ vault no longer controls it is an export**, and belongs here — which includes
109
+ bindings that are not `as-*` packages at all.
110
+
111
+ | projection | egress? | gated |
112
+ |---|---|---|
113
+ | `as-csv`, `as-xlsx`, `as-sql`, … | writes a file | ✅ |
114
+ | `as-aws-s3` | pushes to a bucket — a **destination**, not a format | ✅ |
115
+ | a `ui-*` binding rendering to a screen | the user already unlocked the vault | ❌ not egress |
116
+ | **a `ui-*` binding pushing into Google Sheets / Excel-web / Airtable / Retool** | **a third party persists and indexes it** | ✅ **use this kit** |
117
+
118
+ > **A UI that exports is an export.** Rendering locally is covered by unlock and
119
+ > the ACL. Handing plaintext to a service that keeps a copy is the same act
120
+ > `as-csv` performs, and it calls the same gate:
121
+ > `assertCanExport('plaintext', <your format id>)`.
122
+
123
+ Since `ExportFormat` is an **open** union, your id does not need to be one hub
124
+ ships — `{ plaintext: ['gsheet'] }` is a grantable capability. Before that
125
+ change a third-party id could be *checked* and never *granted*, so the only way
126
+ to authorise one was the `'*'` wildcard.
127
+
128
+ ### What this contract is, honestly
129
+
130
+ `as-*` packages refuse because **hub's own export paths call the gate**. A
131
+ binding written outside this repo can simply not call it, and nothing stops it.
132
+ So this is a **convention plus an executable check**, not an enforcement
133
+ boundary — the same footing `to-*` runs on. Saying otherwise would repeat the
134
+ mistake this kit was built to correct: a security claim that reads as
135
+ enforcement and enforces nothing.
136
+
137
+ Passing this suite is what makes the claim checkable. See
138
+ `docs/adr/0005-no-ui-port.md` in the `noy-db` repo for the reasoning.
@@ -0,0 +1,61 @@
1
+ import { Vault, ExportFormat } from '@noy-db/hub';
2
+
3
+ /** One plaintext-producing entry point, named as a consumer would call it. */
4
+ interface FormatEntryPoint {
5
+ /** Exported function name, e.g. `'toString'`. Used in the test title. */
6
+ readonly name: string;
7
+ /** Call it against the supplied vault. Arguments are the fixture's business. */
8
+ run(vault: Vault): Promise<unknown>;
9
+ }
10
+ /** Everything an `as-*` package must supply to be checked against the gate. */
11
+ interface FormatFixture {
12
+ /**
13
+ * The TIER the package passes to `assertCanExport`. The `as-*` family is
14
+ * two capability classes, not one — discovered by wiring `as-noydb`, which
15
+ * calls `assertCanExport('bundle')` and never mentions plaintext because it
16
+ * emits an encrypted pod. A kit that assumed one tier would have made that
17
+ * fixture describe itself wrongly while still passing.
18
+ */
19
+ readonly tier: 'plaintext' | 'bundle';
20
+ /**
21
+ * The format tag, e.g. `'csv'`. REQUIRED for the plaintext tier and
22
+ * meaningless for `bundle` — hub itself throws when a plaintext check
23
+ * arrives without one, so the pairing is asserted rather than assumed.
24
+ */
25
+ readonly format?: ExportFormat;
26
+ /**
27
+ * A REAL vault with at least one record. Built fresh per case, so an entry
28
+ * point that mutates it cannot leak into the next assertion.
29
+ */
30
+ vault(): Promise<Vault>;
31
+ /**
32
+ * EVERY entry point that can produce plaintext — not a representative one.
33
+ * A format with four exports and one listed here reports a green suite for
34
+ * the three nobody checked.
35
+ */
36
+ readonly exports: ReadonlyArray<FormatEntryPoint>;
37
+ /**
38
+ * The on-disk write path, if the package has one. It must refuse without
39
+ * `acknowledgeRisks: true`; pass a call that OMITS the flag.
40
+ *
41
+ * The vault this receives is the fixture's own — NOT the denying proxy —
42
+ * and it must be export-CAPABLE. A vault that would refuse the export
43
+ * anyway makes the case unfalsifiable: the refusal arrives from the gate
44
+ * upstream and the acknowledgement is never reached. That is not
45
+ * hypothetical; it is what the first version of this kit did, and deleting
46
+ * the acknowledgement guard from as-csv left the suite green.
47
+ */
48
+ writeWithoutAcknowledgement?: (vault: Vault, path: string) => Promise<unknown>;
49
+ }
50
+ /** Thrown by the denying proxy so a refusal is attributable to the gate. */
51
+ declare class ExportDeniedByConformanceKit extends Error {
52
+ constructor(tier: string, format?: string);
53
+ }
54
+ /**
55
+ * Run the shared `as-*` export-gate contract against one format.
56
+ *
57
+ * @param name - shown in the suite title, e.g. `'as-csv'`.
58
+ */
59
+ declare function runFormatConformanceTests(name: string, fixture: FormatFixture): void;
60
+
61
+ export { ExportDeniedByConformanceKit, type FormatEntryPoint, type FormatFixture, runFormatConformanceTests };
package/dist/index.js ADDED
@@ -0,0 +1,83 @@
1
+ // src/index.ts
2
+ import { describe, it, expect } from "vitest";
3
+ var ExportDeniedByConformanceKit = class extends Error {
4
+ constructor(tier, format) {
5
+ super(`conformance: assertCanExport denied '${tier}'${format ? ` / '${format}'` : ""}`);
6
+ this.name = "ExportDeniedByConformanceKit";
7
+ }
8
+ };
9
+ function denyingVault(real, tier, format, seen) {
10
+ return new Proxy(real, {
11
+ get(target, prop, receiver) {
12
+ if (prop === "assertCanExport") {
13
+ return () => {
14
+ throw new ExportDeniedByConformanceKit(tier, format);
15
+ };
16
+ }
17
+ const value = Reflect.get(target, prop, receiver);
18
+ if (typeof value === "function" && typeof prop === "string") {
19
+ return (...args) => {
20
+ if (prop === "exportStream" || prop === "export" || prop === "snapshot") {
21
+ seen.decryptCalls.push(prop);
22
+ }
23
+ return value.apply(target, args);
24
+ };
25
+ }
26
+ return value;
27
+ }
28
+ });
29
+ }
30
+ function runFormatConformanceTests(name, fixture) {
31
+ describe(`${name} \u2014 as-* export gate conformance`, () => {
32
+ it("declares a tier, and a format iff the tier needs one", () => {
33
+ if (fixture.tier === "plaintext") {
34
+ expect(fixture.format, "the plaintext tier requires a format").toBeTruthy();
35
+ } else {
36
+ expect(fixture.format, `the '${fixture.tier}' tier takes no format`).toBeUndefined();
37
+ }
38
+ });
39
+ it("declares at least one export entry point", () => {
40
+ expect(fixture.exports.length).toBeGreaterThan(0);
41
+ });
42
+ it("the fixture vault CAN export \u2014 otherwise every refusal below is free", async () => {
43
+ const vault = await fixture.vault();
44
+ await expect(
45
+ fixture.exports[0].run(vault),
46
+ `${fixture.exports[0].name} failed on an ungated vault \u2014 check the \`format\` tag and the exportCapability grant`
47
+ ).resolves.toBeDefined();
48
+ });
49
+ for (const entry of fixture.exports) {
50
+ it(`${entry.name}: REFUSES when assertCanExport denies`, async () => {
51
+ const seen = { decryptCalls: [] };
52
+ const vault = denyingVault(await fixture.vault(), fixture.tier, fixture.format, seen);
53
+ await expect(entry.run(vault)).rejects.toThrow();
54
+ });
55
+ it(`${entry.name}: refuses BEFORE reading any record`, async () => {
56
+ const seen = { decryptCalls: [] };
57
+ const vault = denyingVault(await fixture.vault(), fixture.tier, fixture.format, seen);
58
+ await expect(entry.run(vault)).rejects.toThrow();
59
+ expect(
60
+ seen.decryptCalls,
61
+ `${entry.name} read records before the export gate refused`
62
+ ).toEqual([]);
63
+ });
64
+ }
65
+ const writeTitle = fixture.writeWithoutAcknowledgement ? "write: REFUSES without acknowledgeRisks" : "write: SKIPPED \u2014 fixture declares no acknowledgement case, so the plaintext-on-disk gate is UNVERIFIED here";
66
+ it(writeTitle, async () => {
67
+ const write = fixture.writeWithoutAcknowledgement;
68
+ if (!write) {
69
+ expect(write).toBeUndefined();
70
+ return;
71
+ }
72
+ const vault = await fixture.vault();
73
+ await expect(write(vault, "/tmp/conformance-should-not-exist")).rejects.toThrow(
74
+ /acknowledgeRisks/i
75
+ );
76
+ });
77
+ });
78
+ }
79
+ export {
80
+ ExportDeniedByConformanceKit,
81
+ runFormatConformanceTests
82
+ };
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/test-format-conformance** — the `as-*` export gate, published as\n * an executable suite.\n *\n * The `as-*` family is the one place plaintext leaves the vault. Each package\n * calls `vault.assertCanExport('plaintext', <format>)` before producing\n * anything, and that call is the whole security boundary: a projection that\n * skips it hands out decrypted records to a caller the vault would have\n * refused.\n *\n * Nothing enforced it. Nine packages had converged on the same shape by\n * convention — `toString`/`toBytes`, `download`, `write` — and convention is\n * what the next format author reads instead of a contract.\n *\n * ## Gated is not the property. Gated BEFORE decrypting is.\n *\n * A gate called after `exportStream` has already run is a gate that refuses\n * the caller and decrypts anyway. So the suite asserts BOTH:\n *\n * - every export entry point REJECTS when `assertCanExport` throws, and\n * - it rejects having read NOTHING — `exportStream` is never called.\n *\n * The second is the one a delegation refactor breaks silently: move the gate\n * from `toObject` into `download` and every existing test still passes.\n *\n * ## Why a Proxy and not a fake Vault\n *\n * `Vault` is large, and a hand-written double would drift from it — and worse,\n * would only ever exercise the methods whoever wrote the double thought of.\n * The fixture supplies a REAL vault; the kit wraps it, so an entry point that\n * reaches for some other decrypting method is still observed.\n *\n * @packageDocumentation\n */\nimport { describe, it, expect } from 'vitest'\nimport type { Vault, ExportFormat } from '@noy-db/hub'\n\n/** One plaintext-producing entry point, named as a consumer would call it. */\nexport interface FormatEntryPoint {\n /** Exported function name, e.g. `'toString'`. Used in the test title. */\n readonly name: string\n /** Call it against the supplied vault. Arguments are the fixture's business. */\n run(vault: Vault): Promise<unknown>\n}\n\n/** Everything an `as-*` package must supply to be checked against the gate. */\nexport interface FormatFixture {\n /**\n * The TIER the package passes to `assertCanExport`. The `as-*` family is\n * two capability classes, not one — discovered by wiring `as-noydb`, which\n * calls `assertCanExport('bundle')` and never mentions plaintext because it\n * emits an encrypted pod. A kit that assumed one tier would have made that\n * fixture describe itself wrongly while still passing.\n */\n readonly tier: 'plaintext' | 'bundle'\n /**\n * The format tag, e.g. `'csv'`. REQUIRED for the plaintext tier and\n * meaningless for `bundle` — hub itself throws when a plaintext check\n * arrives without one, so the pairing is asserted rather than assumed.\n */\n readonly format?: ExportFormat\n /**\n * A REAL vault with at least one record. Built fresh per case, so an entry\n * point that mutates it cannot leak into the next assertion.\n */\n vault(): Promise<Vault>\n /**\n * EVERY entry point that can produce plaintext — not a representative one.\n * A format with four exports and one listed here reports a green suite for\n * the three nobody checked.\n */\n readonly exports: ReadonlyArray<FormatEntryPoint>\n /**\n * The on-disk write path, if the package has one. It must refuse without\n * `acknowledgeRisks: true`; pass a call that OMITS the flag.\n *\n * The vault this receives is the fixture's own — NOT the denying proxy —\n * and it must be export-CAPABLE. A vault that would refuse the export\n * anyway makes the case unfalsifiable: the refusal arrives from the gate\n * upstream and the acknowledgement is never reached. That is not\n * hypothetical; it is what the first version of this kit did, and deleting\n * the acknowledgement guard from as-csv left the suite green.\n */\n writeWithoutAcknowledgement?: (vault: Vault, path: string) => Promise<unknown>\n}\n\n/** Thrown by the denying proxy so a refusal is attributable to the gate. */\nexport class ExportDeniedByConformanceKit extends Error {\n constructor(tier: string, format?: string) {\n super(`conformance: assertCanExport denied '${tier}'${format ? ` / '${format}'` : ''}`)\n this.name = 'ExportDeniedByConformanceKit'\n }\n}\n\ninterface Observation {\n decryptCalls: string[]\n}\n\n/**\n * Wrap a real vault so `assertCanExport` denies, and every method that could\n * yield plaintext records is recorded.\n *\n * `exportStream` is named explicitly because it is the shared read path; the\n * catch-all records any other function property that gets invoked, so an entry\n * point taking a different route is still visible rather than silently\n * unobserved.\n */\nfunction denyingVault(real: Vault, tier: string, format: string | undefined, seen: Observation): Vault {\n return new Proxy(real, {\n get(target, prop, receiver) {\n if (prop === 'assertCanExport') {\n return () => {\n throw new ExportDeniedByConformanceKit(tier, format)\n }\n }\n const value = Reflect.get(target, prop, receiver) as unknown\n if (typeof value === 'function' && typeof prop === 'string') {\n return (...args: unknown[]) => {\n if (prop === 'exportStream' || prop === 'export' || prop === 'snapshot') {\n seen.decryptCalls.push(prop)\n }\n return (value as (...a: unknown[]) => unknown).apply(target, args)\n }\n }\n return value\n },\n }) as Vault\n}\n\n/**\n * Run the shared `as-*` export-gate contract against one format.\n *\n * @param name - shown in the suite title, e.g. `'as-csv'`.\n */\nexport function runFormatConformanceTests(name: string, fixture: FormatFixture): void {\n describe(`${name} — as-* export gate conformance`, () => {\n it('declares a tier, and a format iff the tier needs one', () => {\n // Hub throws on `assertCanExport('plaintext')` with no format, so a\n // fixture in that state describes a call the package cannot be making.\n if (fixture.tier === 'plaintext') {\n expect(fixture.format, 'the plaintext tier requires a format').toBeTruthy()\n } else {\n expect(fixture.format, `the '${fixture.tier}' tier takes no format`).toBeUndefined()\n }\n })\n\n it('declares at least one export entry point', () => {\n // A fixture with an empty list would pass every case below without\n // running anything — a live suite iterating an empty array.\n expect(fixture.exports.length).toBeGreaterThan(0)\n })\n\n it('the fixture vault CAN export — otherwise every refusal below is free', async () => {\n // The whole suite tests refusals, and a refusal is only evidence when\n // the same call would otherwise SUCCEED. A fixture whose `format` tag\n // does not match what the package passes to `assertCanExport` — or\n // which forgets the `exportCapability` grant — makes every case below\n // pass by refusing for the wrong reason, and nothing in the output\n // distinguishes that from a working gate.\n const vault = await fixture.vault()\n await expect(\n fixture.exports[0]!.run(vault),\n `${fixture.exports[0]!.name} failed on an ungated vault — check the \\`format\\` tag and the exportCapability grant`,\n ).resolves.toBeDefined()\n })\n\n for (const entry of fixture.exports) {\n it(`${entry.name}: REFUSES when assertCanExport denies`, async () => {\n const seen: Observation = { decryptCalls: [] }\n const vault = denyingVault(await fixture.vault(), fixture.tier, fixture.format, seen)\n await expect(entry.run(vault)).rejects.toThrow()\n })\n\n it(`${entry.name}: refuses BEFORE reading any record`, async () => {\n const seen: Observation = { decryptCalls: [] }\n const vault = denyingVault(await fixture.vault(), fixture.tier, fixture.format, seen)\n await expect(entry.run(vault)).rejects.toThrow()\n // The property that a delegation refactor breaks silently: a gate\n // moved downstream still refuses the caller, having already decrypted.\n expect(\n seen.decryptCalls,\n `${entry.name} read records before the export gate refused`,\n ).toEqual([])\n })\n }\n\n const writeTitle = fixture.writeWithoutAcknowledgement\n ? 'write: REFUSES without acknowledgeRisks'\n : 'write: SKIPPED — fixture declares no acknowledgement case, so the plaintext-on-disk gate is UNVERIFIED here'\n\n it(writeTitle, async () => {\n const write = fixture.writeWithoutAcknowledgement\n if (!write) {\n // Passes loudly. Omitting the case would make an unchecked security\n // gate indistinguishable from a checked one in the output.\n expect(write).toBeUndefined()\n return\n }\n const vault = await fixture.vault()\n // Matched on the MESSAGE, not merely on \"it threw\". `rejects.toThrow()`\n // alone passes when the export gate refuses first — which is exactly\n // what happened here before this line existed, and it made the case\n // unable to fail. The flag name is the one string every such message\n // contains by construction.\n await expect(write(vault, '/tmp/conformance-should-not-exist')).rejects.toThrow(\n /acknowledgeRisks/i,\n )\n })\n })\n}\n"],"mappings":";AAkCA,SAAS,UAAU,IAAI,cAAc;AAqD9B,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACtD,YAAY,MAAc,QAAiB;AACzC,UAAM,wCAAwC,IAAI,IAAI,SAAS,OAAO,MAAM,MAAM,EAAE,EAAE;AACtF,SAAK,OAAO;AAAA,EACd;AACF;AAeA,SAAS,aAAa,MAAa,MAAc,QAA4B,MAA0B;AACrG,SAAO,IAAI,MAAM,MAAM;AAAA,IACrB,IAAI,QAAQ,MAAM,UAAU;AAC1B,UAAI,SAAS,mBAAmB;AAC9B,eAAO,MAAM;AACX,gBAAM,IAAI,6BAA6B,MAAM,MAAM;AAAA,QACrD;AAAA,MACF;AACA,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UAAI,OAAO,UAAU,cAAc,OAAO,SAAS,UAAU;AAC3D,eAAO,IAAI,SAAoB;AAC7B,cAAI,SAAS,kBAAkB,SAAS,YAAY,SAAS,YAAY;AACvE,iBAAK,aAAa,KAAK,IAAI;AAAA,UAC7B;AACA,iBAAQ,MAAuC,MAAM,QAAQ,IAAI;AAAA,QACnE;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAOO,SAAS,0BAA0B,MAAc,SAA8B;AACpF,WAAS,GAAG,IAAI,wCAAmC,MAAM;AACvD,OAAG,wDAAwD,MAAM;AAG/D,UAAI,QAAQ,SAAS,aAAa;AAChC,eAAO,QAAQ,QAAQ,sCAAsC,EAAE,WAAW;AAAA,MAC5E,OAAO;AACL,eAAO,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,wBAAwB,EAAE,cAAc;AAAA,MACrF;AAAA,IACF,CAAC;AAED,OAAG,4CAA4C,MAAM;AAGnD,aAAO,QAAQ,QAAQ,MAAM,EAAE,gBAAgB,CAAC;AAAA,IAClD,CAAC;AAED,OAAG,6EAAwE,YAAY;AAOrF,YAAM,QAAQ,MAAM,QAAQ,MAAM;AAClC,YAAM;AAAA,QACJ,QAAQ,QAAQ,CAAC,EAAG,IAAI,KAAK;AAAA,QAC7B,GAAG,QAAQ,QAAQ,CAAC,EAAG,IAAI;AAAA,MAC7B,EAAE,SAAS,YAAY;AAAA,IACzB,CAAC;AAED,eAAW,SAAS,QAAQ,SAAS;AACnC,SAAG,GAAG,MAAM,IAAI,yCAAyC,YAAY;AACnE,cAAM,OAAoB,EAAE,cAAc,CAAC,EAAE;AAC7C,cAAM,QAAQ,aAAa,MAAM,QAAQ,MAAM,GAAG,QAAQ,MAAM,QAAQ,QAAQ,IAAI;AACpF,cAAM,OAAO,MAAM,IAAI,KAAK,CAAC,EAAE,QAAQ,QAAQ;AAAA,MACjD,CAAC;AAED,SAAG,GAAG,MAAM,IAAI,uCAAuC,YAAY;AACjE,cAAM,OAAoB,EAAE,cAAc,CAAC,EAAE;AAC7C,cAAM,QAAQ,aAAa,MAAM,QAAQ,MAAM,GAAG,QAAQ,MAAM,QAAQ,QAAQ,IAAI;AACpF,cAAM,OAAO,MAAM,IAAI,KAAK,CAAC,EAAE,QAAQ,QAAQ;AAG/C;AAAA,UACE,KAAK;AAAA,UACL,GAAG,MAAM,IAAI;AAAA,QACf,EAAE,QAAQ,CAAC,CAAC;AAAA,MACd,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,QAAQ,8BACvB,4CACA;AAEJ,OAAG,YAAY,YAAY;AACzB,YAAM,QAAQ,QAAQ;AACtB,UAAI,CAAC,OAAO;AAGV,eAAO,KAAK,EAAE,cAAc;AAC5B;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,QAAQ,MAAM;AAMlC,YAAM,OAAO,MAAM,OAAO,mCAAmC,CAAC,EAAE,QAAQ;AAAA,QACtE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@noy-db/test-format-conformance",
3
+ "version": "0.7.0-pre.0",
4
+ "description": "Parameterized contract tests for noy-db as-* formats — the export-gate suite every plaintext projection must pass",
5
+ "license": "MIT",
6
+ "author": "vLannaAi <vicio@lanna.ai>",
7
+ "homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/test-format-conformance#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/vLannaAi/noy-db.git",
11
+ "directory": "packages/test-format-conformance"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/vLannaAi/noy-db/issues"
15
+ },
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "node": ">=22.0.0"
33
+ },
34
+ "peerDependencies": {
35
+ "vitest": "^3.0.0",
36
+ "@noy-db/hub": "0.7.0-pre.0"
37
+ },
38
+ "devDependencies": {
39
+ "vitest": "^3.0.0",
40
+ "@noy-db/hub": "0.7.0-pre.0"
41
+ },
42
+ "keywords": [
43
+ "noy-db",
44
+ "conformance",
45
+ "export",
46
+ "plaintext",
47
+ "testing"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "test": "vitest run --passWithNoTests",
55
+ "typecheck": "tsc --noEmit"
56
+ }
57
+ }