@noy-db/test-ceremony-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 +21 -0
- package/README.md +75 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
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,75 @@
|
|
|
1
|
+
# @noy-db/test-ceremony-conformance
|
|
2
|
+
|
|
3
|
+
The `SlotRewrapCeremony` contract, published as an executable suite.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { runCeremonyConformanceTests } from '@noy-db/test-ceremony-conformance'
|
|
7
|
+
|
|
8
|
+
runCeremonyConformanceTests('my-method', {
|
|
9
|
+
method: 'my-method',
|
|
10
|
+
ceremony: () => myRewrapCeremony(secret),
|
|
11
|
+
oldSlot: () => makeMySlot(),
|
|
12
|
+
wrongMethodSlot: async () => ({ ...(await makeMySlot()), method: 'password' }),
|
|
13
|
+
unwrap: (opts) => openMyWrap(opts), // optional — absence is REPORTED
|
|
14
|
+
})
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What it checks
|
|
18
|
+
|
|
19
|
+
When `rotateSecret` preserves a tier-2 slot it hands each ceremony a
|
|
20
|
+
`SlotRewrapContext` and takes back `EnrollAuthenticatorOptions`, which hub
|
|
21
|
+
persists atomically with the rotation. Six properties are the same for every
|
|
22
|
+
method:
|
|
23
|
+
|
|
24
|
+
1. the slot **id** survives
|
|
25
|
+
2. the **method** survives
|
|
26
|
+
3. the **effective wrap kind** survives
|
|
27
|
+
4. a slot belonging to **another method is refused**
|
|
28
|
+
5. a refusal **does not mutate** the context it was handed
|
|
29
|
+
6. the wrap holds **`ctx.newDeks`**, not the stale set
|
|
30
|
+
|
|
31
|
+
Method-specific behaviour stays in the package — PRF vs rawId fallback,
|
|
32
|
+
password strength rules, credential cancellation. A conformance kit that grew
|
|
33
|
+
those would stop being portable to a method nobody has written yet.
|
|
34
|
+
|
|
35
|
+
## Two fixture rules that are not decoration
|
|
36
|
+
|
|
37
|
+
**`wrongMethodSlot` must differ from `oldSlot` in `method` alone.** Enforced,
|
|
38
|
+
and learned the hard way: the first version of the on-password fixture used a
|
|
39
|
+
wrap-KEK slot for the refusal case, so deleting the method guard from the
|
|
40
|
+
ceremony left the suite **green** — the `wrapKind` guard was rejecting it
|
|
41
|
+
instead. Two differences means the case proves only that *something* refused.
|
|
42
|
+
|
|
43
|
+
**`unwrap` is optional, and its absence is reported in the test name.** A kit
|
|
44
|
+
that silently skipped its most valuable case would be the green-run-with-a-
|
|
45
|
+
red-job-inside, in test form.
|
|
46
|
+
|
|
47
|
+
## Why two bindings, not one
|
|
48
|
+
|
|
49
|
+
`on-password` and `on-webauthn` both run it, and the second is what made the
|
|
50
|
+
first correct.
|
|
51
|
+
|
|
52
|
+
Case 3 originally asserted `out.wrapKind` directly. It passed against
|
|
53
|
+
`on-password`, which **must** set that field. `wrapKind` is `?: 'kek'` on the
|
|
54
|
+
wrap-KEK variant, so `on-webauthn` legally omits it — and the kit called that
|
|
55
|
+
a failure. **A conformance suite written against one implementation encodes
|
|
56
|
+
that implementation's incidental shape as the contract.**
|
|
57
|
+
|
|
58
|
+
The same pass corrected a naming trap worth knowing: `wrapKind: 'kek'` names
|
|
59
|
+
the *field the slot stores* (`wrapped_kek`), not the wrapped material. A
|
|
60
|
+
WebAuthn slot wraps the keyring payload carrying `ctx.newDeks`, exactly as the
|
|
61
|
+
wrap-DEKs path does. Reading freshness off the `wrapKind` name gets it
|
|
62
|
+
backwards.
|
|
63
|
+
|
|
64
|
+
## Mutation-checked
|
|
65
|
+
|
|
66
|
+
Both bindings, against real defects introduced into real ceremonies:
|
|
67
|
+
|
|
68
|
+
| mutation | on-password | on-webauthn |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| slot id not preserved | 1 fails | 1 fails |
|
|
71
|
+
| method guard deleted | 2 fail | 2 fail |
|
|
72
|
+
| wraps the wrong keys | 1 fails | — |
|
|
73
|
+
|
|
74
|
+
The method-guard row is the one that matters: it fails **only** because the
|
|
75
|
+
refusal fixture differs in one field. Before that rule it passed.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SlotRewrapCeremony, KeyringAuthenticator, EnrollAuthenticatorOptions, EnclaveKey } from '@noy-db/hub/on';
|
|
2
|
+
|
|
3
|
+
/** Everything an implementation must supply to be checked against the contract. */
|
|
4
|
+
interface CeremonyFixture {
|
|
5
|
+
/** The method this ceremony handles — must equal what it preserves. */
|
|
6
|
+
readonly method: string;
|
|
7
|
+
/**
|
|
8
|
+
* A ready-to-run ceremony. Called once per case, so an implementation that
|
|
9
|
+
* captures a secret in a closure (all shipped ones do) is not shared
|
|
10
|
+
* between cases.
|
|
11
|
+
*/
|
|
12
|
+
ceremony(): SlotRewrapCeremony;
|
|
13
|
+
/** A slot this ceremony accepts: right `method`, right `wrapKind`. */
|
|
14
|
+
oldSlot(): Promise<KeyringAuthenticator> | KeyringAuthenticator;
|
|
15
|
+
/**
|
|
16
|
+
* A slot this ceremony must REFUSE, differing from {@link oldSlot} in
|
|
17
|
+
* `method` and NOTHING ELSE — same `wrapKind` in particular.
|
|
18
|
+
*
|
|
19
|
+
* Required, because refusal cannot be observed from a ceremony's own slots.
|
|
20
|
+
* The single-difference rule is enforced below and was learned the hard
|
|
21
|
+
* way: a fixture whose wrong-method slot ALSO had a different `wrapKind`
|
|
22
|
+
* still threw, from the wrapKind guard, so deleting the method check
|
|
23
|
+
* changed nothing and the suite stayed green. Two differences means the
|
|
24
|
+
* case proves only that *something* rejected it.
|
|
25
|
+
*/
|
|
26
|
+
wrongMethodSlot(): Promise<KeyringAuthenticator> | KeyringAuthenticator;
|
|
27
|
+
/**
|
|
28
|
+
* Re-open what the returned options wrapped, so the suite can prove the
|
|
29
|
+
* ceremony used `ctx.newDeks` rather than carrying the stale set forward.
|
|
30
|
+
* Only the method can do this, so it is optional — and its absence is
|
|
31
|
+
* REPORTED in the test name rather than passed over.
|
|
32
|
+
*/
|
|
33
|
+
unwrap?(options: EnrollAuthenticatorOptions): Promise<Map<string, EnclaveKey>>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run the shared `SlotRewrapCeremony` contract against one implementation.
|
|
37
|
+
*
|
|
38
|
+
* @param name - shown in the suite title, e.g. `'on-password'`.
|
|
39
|
+
*/
|
|
40
|
+
declare function runCeremonyConformanceTests(name: string, fixture: CeremonyFixture): void;
|
|
41
|
+
|
|
42
|
+
export { type CeremonyFixture, runCeremonyConformanceTests };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
async function makeKey() {
|
|
4
|
+
return globalThis.crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, [
|
|
5
|
+
"encrypt",
|
|
6
|
+
"decrypt"
|
|
7
|
+
]);
|
|
8
|
+
}
|
|
9
|
+
async function makeContext(oldSlot) {
|
|
10
|
+
const newDeks = /* @__PURE__ */ new Map([
|
|
11
|
+
["invoices", await makeKey()],
|
|
12
|
+
["clients", await makeKey()]
|
|
13
|
+
]);
|
|
14
|
+
return { newKek: await makeKey(), newDeks, oldSlot };
|
|
15
|
+
}
|
|
16
|
+
function runCeremonyConformanceTests(name, fixture) {
|
|
17
|
+
describe(`${name} \u2014 SlotRewrapCeremony conformance`, () => {
|
|
18
|
+
it("1. returns options that preserve the slot id", async () => {
|
|
19
|
+
const slot = await fixture.oldSlot();
|
|
20
|
+
const out = await fixture.ceremony()(await makeContext(slot));
|
|
21
|
+
expect(out.id).toBe(slot.id);
|
|
22
|
+
});
|
|
23
|
+
it("2. returns options that preserve the method", async () => {
|
|
24
|
+
const slot = await fixture.oldSlot();
|
|
25
|
+
const out = await fixture.ceremony()(await makeContext(slot));
|
|
26
|
+
expect(out.method).toBe(slot.method);
|
|
27
|
+
expect(out.method).toBe(fixture.method);
|
|
28
|
+
});
|
|
29
|
+
it("3. returns options that preserve the EFFECTIVE wrap kind", async () => {
|
|
30
|
+
const slot = await fixture.oldSlot();
|
|
31
|
+
const out = await fixture.ceremony()(await makeContext(slot));
|
|
32
|
+
expect(out.wrapKind ?? "kek").toBe(slot.wrapKind ?? "kek");
|
|
33
|
+
});
|
|
34
|
+
it("4. REFUSES a slot belonging to another method", async () => {
|
|
35
|
+
const slot = await fixture.oldSlot();
|
|
36
|
+
const other = await fixture.wrongMethodSlot();
|
|
37
|
+
expect(other.method).not.toBe(fixture.method);
|
|
38
|
+
expect(
|
|
39
|
+
other.wrapKind,
|
|
40
|
+
"wrongMethodSlot must differ from oldSlot in `method` alone \u2014 a second difference lets another guard absorb the case"
|
|
41
|
+
).toBe(slot.wrapKind);
|
|
42
|
+
await expect(fixture.ceremony()(await makeContext(other))).rejects.toThrow();
|
|
43
|
+
});
|
|
44
|
+
it("5. refuses without mutating the context it was handed", async () => {
|
|
45
|
+
const other = await fixture.wrongMethodSlot();
|
|
46
|
+
const ctx = await makeContext(other);
|
|
47
|
+
const before = [...ctx.newDeks.keys()].sort();
|
|
48
|
+
await expect(fixture.ceremony()(ctx)).rejects.toThrow();
|
|
49
|
+
expect([...ctx.newDeks.keys()].sort()).toEqual(before);
|
|
50
|
+
});
|
|
51
|
+
const freshness = fixture.unwrap ? "6. wraps the NEW dek set, not the old one" : "6. SKIPPED \u2014 no `unwrap` in the fixture, so freshness is UNVERIFIED here";
|
|
52
|
+
it(freshness, async () => {
|
|
53
|
+
const unwrap = fixture.unwrap;
|
|
54
|
+
if (!unwrap) {
|
|
55
|
+
expect(unwrap).toBeUndefined();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const slot = await fixture.oldSlot();
|
|
59
|
+
const ctx = await makeContext(slot);
|
|
60
|
+
const out = await fixture.ceremony()(ctx);
|
|
61
|
+
const wrapped = await unwrap(out);
|
|
62
|
+
expect([...wrapped.keys()].sort()).toEqual([...ctx.newDeks.keys()].sort());
|
|
63
|
+
for (const [collection, expected] of ctx.newDeks) {
|
|
64
|
+
const got = wrapped.get(collection);
|
|
65
|
+
expect(got, `${collection} missing from the rewrapped set`).toBeDefined();
|
|
66
|
+
const a = new Uint8Array(await globalThis.crypto.subtle.exportKey("raw", expected));
|
|
67
|
+
const b = new Uint8Array(await globalThis.crypto.subtle.exportKey("raw", got));
|
|
68
|
+
expect([...b], `${collection} was wrapped from the wrong key`).toEqual([...a]);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
runCeremonyConformanceTests
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/test-ceremony-conformance** — the `SlotRewrapCeremony` contract,\n * published as an executable suite.\n *\n * A `rotateSecret` that preserves a tier-2 slot hands each ceremony a\n * {@link SlotRewrapContext} and takes back `EnrollAuthenticatorOptions`, which\n * hub then persists atomically with the rotation. Everything hub validates\n * about that return value — and everything a ceremony must refuse — is the\n * same for every method. This is that shared half, so `on-password`,\n * `on-webauthn` and any third-party method answer the same questions.\n *\n * ## Why the fixture is not just a ceremony\n *\n * Two of the six properties are **unobservable from a single ceremony**:\n *\n * - **Refusal.** \"Rejects a slot of another method\" needs a slot of another\n * method. A ceremony handed only its own slots always succeeds, and a\n * suite that only ever passes it valid input reports nothing about the\n * guard hub relies on to prevent a slot-type swap mid-rotation.\n * - **Freshness.** \"Wrapped `ctx.newDeks`, not the old set\" needs the wrap\n * to be OPENED again, which only the method can do.\n *\n * So `wrongMethodSlot` is REQUIRED and `unwrap` is optional-but-reported: a\n * kit that quietly skipped its most valuable case would be the \"green run\n * with a red job inside\" in test form. When `unwrap` is absent the suite says\n * so in the test name, so a reader sees the hole in the output rather than\n * inferring coverage from a row count.\n *\n * ## What this suite does NOT cover\n *\n * Method-specific behaviour stays in the package: PRF vs rawId fallback,\n * password strength rules, credential cancellation. Those are real and tested\n * where they live. A conformance kit that grew them would stop being portable\n * to a method nobody has written yet.\n *\n * ## Why this binds `/on` rather than the root barrel\n *\n * Five types, and before `/on` existed they were scattered: three on `/team`,\n * and `KeyringAuthenticator` / `EnclaveKey` reachable only from the whole\n * library root. A third-party unlock method had to import all of\n * `@noy-db/hub` to name the signature of one ceremony.\n *\n * @packageDocumentation\n */\nimport { describe, it, expect } from 'vitest'\nimport type {\n SlotRewrapCeremony,\n SlotRewrapContext,\n KeyringAuthenticator,\n EnrollAuthenticatorOptions,\n EnclaveKey,\n} from '@noy-db/hub/on'\n\n/** Everything an implementation must supply to be checked against the contract. */\nexport interface CeremonyFixture {\n /** The method this ceremony handles — must equal what it preserves. */\n readonly method: string\n /**\n * A ready-to-run ceremony. Called once per case, so an implementation that\n * captures a secret in a closure (all shipped ones do) is not shared\n * between cases.\n */\n ceremony(): SlotRewrapCeremony\n /** A slot this ceremony accepts: right `method`, right `wrapKind`. */\n oldSlot(): Promise<KeyringAuthenticator> | KeyringAuthenticator\n /**\n * A slot this ceremony must REFUSE, differing from {@link oldSlot} in\n * `method` and NOTHING ELSE — same `wrapKind` in particular.\n *\n * Required, because refusal cannot be observed from a ceremony's own slots.\n * The single-difference rule is enforced below and was learned the hard\n * way: a fixture whose wrong-method slot ALSO had a different `wrapKind`\n * still threw, from the wrapKind guard, so deleting the method check\n * changed nothing and the suite stayed green. Two differences means the\n * case proves only that *something* rejected it.\n */\n wrongMethodSlot(): Promise<KeyringAuthenticator> | KeyringAuthenticator\n /**\n * Re-open what the returned options wrapped, so the suite can prove the\n * ceremony used `ctx.newDeks` rather than carrying the stale set forward.\n * Only the method can do this, so it is optional — and its absence is\n * REPORTED in the test name rather than passed over.\n */\n unwrap?(options: EnrollAuthenticatorOptions): Promise<Map<string, EnclaveKey>>\n}\n\nasync function makeKey(): Promise<EnclaveKey> {\n return globalThis.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [\n 'encrypt',\n 'decrypt',\n ]) as Promise<EnclaveKey>\n}\n\n/** A context with a FRESH DEK set, which is the whole point of a rewrap. */\nasync function makeContext(oldSlot: KeyringAuthenticator): Promise<SlotRewrapContext> {\n const newDeks = new Map<string, EnclaveKey>([\n ['invoices', await makeKey()],\n ['clients', await makeKey()],\n ])\n return { newKek: await makeKey(), newDeks, oldSlot }\n}\n\n/**\n * Run the shared `SlotRewrapCeremony` contract against one implementation.\n *\n * @param name - shown in the suite title, e.g. `'on-password'`.\n */\nexport function runCeremonyConformanceTests(name: string, fixture: CeremonyFixture): void {\n describe(`${name} — SlotRewrapCeremony conformance`, () => {\n it('1. returns options that preserve the slot id', async () => {\n const slot = await fixture.oldSlot()\n const out = await fixture.ceremony()(await makeContext(slot))\n // Hub validates this itself and throws on mismatch. Asserted here too\n // because a ceremony that gets it wrong should fail in ITS OWN suite,\n // not in a rotation weeks later.\n expect(out.id).toBe(slot.id)\n })\n\n it('2. returns options that preserve the method', async () => {\n const slot = await fixture.oldSlot()\n const out = await fixture.ceremony()(await makeContext(slot))\n expect(out.method).toBe(slot.method)\n expect(out.method).toBe(fixture.method)\n })\n\n it('3. returns options that preserve the EFFECTIVE wrap kind', async () => {\n const slot = await fixture.oldSlot()\n const out = await fixture.ceremony()(await makeContext(slot))\n // Compared on the EFFECTIVE value, not the literal field. `wrapKind` is\n // `?: 'kek'` on the wrap-KEK variant and a required `'deks'` on the\n // other, so an absent field legally means 'kek' and hub persists it as\n // such.\n //\n // The first version of this case asserted `out.wrapKind` directly. It\n // passed — because on-password, the only binding at the time, MUST set\n // the field explicitly. Wiring a second implementation is what exposed\n // it: on-webauthn omits it, correctly, and the kit called that a\n // failure. A conformance suite written against one implementation\n // encodes that implementation's incidental shape as the contract.\n //\n // A wrap-KEK slot silently becoming wrap-DEKs (or the reverse) really\n // does change what the slot can unlock — that is the property here, and\n // it survives the correction.\n expect(out.wrapKind ?? 'kek').toBe(slot.wrapKind ?? 'kek')\n })\n\n it('4. REFUSES a slot belonging to another method', async () => {\n const slot = await fixture.oldSlot()\n const other = await fixture.wrongMethodSlot()\n expect(other.method).not.toBe(fixture.method)\n // The fixture contract, asserted rather than trusted: if the refusal\n // case differs in more than the method, a passing result does not say\n // the METHOD guard works — some other guard may be doing the rejecting.\n expect(\n other.wrapKind,\n 'wrongMethodSlot must differ from oldSlot in `method` alone — a second difference lets another guard absorb the case',\n ).toBe(slot.wrapKind)\n await expect(fixture.ceremony()(await makeContext(other))).rejects.toThrow()\n })\n\n it('5. refuses without mutating the context it was handed', async () => {\n const other = await fixture.wrongMethodSlot()\n const ctx = await makeContext(other)\n const before = [...ctx.newDeks.keys()].sort()\n await expect(fixture.ceremony()(ctx)).rejects.toThrow()\n // A ceremony that half-applies before validating leaves the caller\n // holding a context it cannot safely retry with.\n expect([...ctx.newDeks.keys()].sort()).toEqual(before)\n })\n\n const freshness = fixture.unwrap\n ? '6. wraps the NEW dek set, not the old one'\n : '6. SKIPPED — no `unwrap` in the fixture, so freshness is UNVERIFIED here'\n\n it(freshness, async () => {\n const unwrap = fixture.unwrap\n if (!unwrap) {\n // Deliberately passes, loudly. The alternative — omitting the case —\n // makes an unverified property indistinguishable from a verified one.\n expect(unwrap).toBeUndefined()\n return\n }\n const slot = await fixture.oldSlot()\n const ctx = await makeContext(slot)\n const out = await fixture.ceremony()(ctx)\n const wrapped = await unwrap(out)\n expect([...wrapped.keys()].sort()).toEqual([...ctx.newDeks.keys()].sort())\n for (const [collection, expected] of ctx.newDeks) {\n const got = wrapped.get(collection)\n expect(got, `${collection} missing from the rewrapped set`).toBeDefined()\n // Compare the raw bytes: two AES keys are not `===`, and an\n // identity check would pass on any key at all.\n const a = new Uint8Array(await globalThis.crypto.subtle.exportKey('raw', expected))\n const b = new Uint8Array(await globalThis.crypto.subtle.exportKey('raw', got!))\n expect([...b], `${collection} was wrapped from the wrong key`).toEqual([...a])\n }\n })\n })\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noy-db/test-ceremony-conformance",
|
|
3
|
+
"version": "0.7.0-pre.0",
|
|
4
|
+
"description": "Parameterized contract tests for noy-db on-* slot ceremonies — the conformance suite every SlotRewrapCeremony must pass",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "vLannaAi <vicio@lanna.ai>",
|
|
7
|
+
"homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/test-ceremony-conformance#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vLannaAi/noy-db.git",
|
|
11
|
+
"directory": "packages/test-ceremony-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
|
+
"authenticator",
|
|
46
|
+
"keyring",
|
|
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
|
+
}
|