@hypequery/protocol-conformance 0.9.1 → 0.10.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/README.md +43 -27
- package/dist/adapters/generators.d.ts.map +1 -1
- package/dist/adapters/generators.js +14 -0
- package/dist/adapters/reference.d.ts +10 -1
- package/dist/adapters/reference.d.ts.map +1 -1
- package/dist/adapters/reference.js +52 -1
- package/dist/adapters/stdio.d.ts +3 -1
- package/dist/adapters/stdio.d.ts.map +1 -1
- package/dist/adapters/stdio.js +3 -0
- package/dist/bin/reference-adapter.js +2 -1
- package/dist/compare.js +10 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +4 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +74 -8
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/fixtures/cache-keys-v1/README.md +59 -0
- package/fixtures/cache-keys-v1/rejections.json +172 -0
- package/fixtures/cache-keys-v1/success.json +134 -0
- package/fixtures/deployment-bundles-v1/README.md +5 -57
- package/fixtures/deployment-releases-v1/README.md +6 -33
- package/fixtures/deployments-v1/README.md +5 -73
- package/fixtures/expressions-v1/README.md +4 -22
- package/fixtures/fuzz-seeds-v1/README.md +6 -26
- package/fixtures/identifiers-v1/README.md +4 -12
- package/fixtures/identifiers-v1/rejections.json +38 -0
- package/fixtures/identifiers-v1/success.json +18 -0
- package/fixtures/manifest.json +161 -39
- package/fixtures/query-diagnostics-v1/README.md +4 -45
- package/fixtures/query-events-v1/README.md +4 -56
- package/fixtures/query-implementations-v1/README.md +4 -30
- package/fixtures/query-schemas-v1/README.md +4 -22
- package/fixtures/sql-portability-v1/README.md +3 -12
- package/fixtures/tagged-values-v1/README.md +12 -1
- package/fixtures/tagged-values-v1/rejections.json +89 -0
- package/fixtures/tagged-values-v1/success.json +127 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -1,40 +1,56 @@
|
|
|
1
1
|
# @hypequery/protocol-conformance
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Hypequery security protocol. It drives any implementation of the protocol
|
|
5
|
-
against the language-neutral fixtures in `specs/security-protocol/fixtures/`
|
|
6
|
-
and checks the results against the pinned expectations.
|
|
3
|
+
`@hypequery/protocol-conformance` checks TypeScript, Python, or any other implementation of the Hypequery security protocol against the same language-neutral fixtures. It includes a command-line runner and the TypeScript reference adapter.
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
for the manifest schema, the adapter wire protocol, and the pass criteria.
|
|
5
|
+
The manifest, adapter wire format, and pass rules are defined in [RFC 0012](../../specs/security-protocol/rfc/0012-cross-language-conformance.md).
|
|
10
6
|
|
|
11
|
-
##
|
|
7
|
+
## Run the reference adapter
|
|
12
8
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
hypequery-protocol-
|
|
9
|
+
```bash
|
|
10
|
+
hypequery-protocol-conformance run -- \
|
|
11
|
+
hypequery-protocol-reference-adapter
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Test another language
|
|
15
|
+
|
|
16
|
+
Everything after `--` is the adapter command. The runner starts it directly, without a shell:
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
```bash
|
|
19
|
+
hypequery-protocol-conformance run \
|
|
20
|
+
--fixtures ./specs/security-protocol/fixtures \
|
|
21
|
+
-- python -m my_implementation.adapter
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Useful options include:
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
```bash
|
|
27
|
+
# Run selected fixture families and emit JSON.
|
|
28
|
+
hypequery-protocol-conformance run \
|
|
29
|
+
--families sql-portability-v1 \
|
|
30
|
+
--skip-fuzz \
|
|
31
|
+
--report json \
|
|
32
|
+
-- node ./adapter.mjs
|
|
24
33
|
|
|
25
|
-
#
|
|
34
|
+
# Show case counts by family.
|
|
26
35
|
hypequery-protocol-conformance list
|
|
27
36
|
```
|
|
28
37
|
|
|
29
|
-
Exit
|
|
30
|
-
|
|
38
|
+
Exit code `0` means every case passed, `1` means conformance failures, and `2` means the runner or adapter protocol could not be set up correctly.
|
|
39
|
+
|
|
40
|
+
## Adapter shape
|
|
41
|
+
|
|
42
|
+
An adapter reads newline-delimited JSON from stdin and writes newline-delimited JSON to stdout. It answers the initial `hello` with supported fixture families, then returns one `result` for every `case`.
|
|
43
|
+
|
|
44
|
+
The exported `createStdioAdapter` helper handles the loop. Your handler maps `(family, role, case)` to one of:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
{ ok: true, output? }
|
|
48
|
+
{ ok: false, code }
|
|
49
|
+
{ skipped: true, reason }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The package includes a pinned fixture snapshot, so a pinned package version is also a pinned conformance target. Pass `--fixtures` to test local or newer specifications.
|
|
31
53
|
|
|
32
|
-
##
|
|
54
|
+
## License
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
answers a `hello` with the families it supports, then one `result` per `case`.
|
|
36
|
-
The `createStdioAdapter` helper implements the loop; a handler maps
|
|
37
|
-
`(family, role, case)` to `{ ok: true, output? }`, `{ ok: false, code }`, or
|
|
38
|
-
`{ skipped: true, reason }`. The bundled fixture snapshot means a pinned
|
|
39
|
-
package version is a pinned conformance target; pass `--fixtures` to test
|
|
40
|
-
against newer or local specs.
|
|
56
|
+
Apache-2.0.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/adapters/generators.ts"],"names":[],"mappings":"AAIA,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAkBpC,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/adapters/generators.ts"],"names":[],"mappings":"AAIA,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAkBpC,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAuC1D;AAID,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAMxD;AAMD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAiCzD;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CA2BrD;AAgBD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAuB7D;AAkBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAyBpD;AAeD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAoB1D;AA2BD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAwDzD;AAkBD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAmBrD;AAaD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAgBtD"}
|
|
@@ -35,6 +35,20 @@ export function materializeTaggedValue(spec) {
|
|
|
35
35
|
throw new Error(`Unknown non-finite float: ${String(spec.value)}`);
|
|
36
36
|
case 'repeat-string':
|
|
37
37
|
return repeat(spec.utf8, spec.count);
|
|
38
|
+
case 'unsafe-accessor': {
|
|
39
|
+
// `$hypequery` served by a computed accessor instead of a data
|
|
40
|
+
// property. A validator must snapshot without invoking it and reject.
|
|
41
|
+
const value = {};
|
|
42
|
+
Object.defineProperty(value, '$hypequery', {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: () => ({
|
|
45
|
+
type: 'uuid',
|
|
46
|
+
version: 1,
|
|
47
|
+
value: '01890f3e-7b7b-7cc2-98c4-dc0c0c07398f',
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
38
52
|
default:
|
|
39
53
|
throw new Error(`Unknown tagged-value generator: ${String(spec.type)}`);
|
|
40
54
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { FixtureRole, HandlerResult } from '../types.js';
|
|
2
2
|
type Case = Record<string, unknown>;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* RFC 0012 hostile-object suite declaration for the TypeScript reference
|
|
5
|
+
* implementation. The cases live in @hypequery/protocol's
|
|
6
|
+
* `src/values/codec.test.ts`; each mechanism listed here is exercised there.
|
|
7
|
+
*/
|
|
8
|
+
export declare const REFERENCE_HOSTILE_OBJECT_SUITE: {
|
|
9
|
+
readonly count: 7;
|
|
10
|
+
readonly mechanisms: readonly ["getter", "toJSON", "proxy", "custom-prototype", "symbol-key", "sparse-array", "cycle"];
|
|
11
|
+
};
|
|
12
|
+
export declare const REFERENCE_FAMILIES: readonly ["cache-keys-v1", "tagged-values-v1", "identifiers-v1", "expressions-v1", "query-schemas-v1", "query-implementations-v1", "query-events-v1", "query-diagnostics-v1", "deployments-v1", "deployment-bundles-v1", "deployment-releases-v1"];
|
|
4
13
|
export declare function referenceHandle(family: string, role: FixtureRole, c: Case, section?: string): HandlerResult;
|
|
5
14
|
export {};
|
|
6
15
|
//# sourceMappingURL=reference.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/adapters/reference.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/adapters/reference.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAc9D,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0BpC;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;CAWjC,CAAC;AAEX,eAAO,MAAM,kBAAkB,oPAYrB,CAAC;AAEX,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,CAAC,EAAE,IAAI,EACP,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CAoCf"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// root export of @hypequery/protocol and returns a wire result. Every operation
|
|
3
3
|
// goes through the package's supported surface — this is exactly what a second
|
|
4
4
|
// implementation must reproduce.
|
|
5
|
-
import { decodeCanonicalValue, encodeCanonicalValue, hashCanonicalValue, parseProtocolIdentifier, parseProtocolQualifiedIdentifier, prepareProtocolDeploymentBundleManifest, prepareProtocolDeploymentContract, prepareProtocolDeploymentReleaseEnvelope, splitProtocolQualifiedIdentifier, validateCanonicalValue, validateProtocolDeploymentBundleManifest, validateProtocolDeploymentContract, validateProtocolDeploymentReleaseEnvelope, validateProtocolExpression, validateProtocolQueryDiagnostics, validateProtocolQueryEvent, validateProtocolQueryImplementation, validateProtocolSchema, validateProtocolSemanticQuery, validateProtocolSqlExpression, } from '@hypequery/protocol';
|
|
5
|
+
import { decodeCanonicalValue, deriveProtocolCacheKey, deriveProtocolCacheNamespaceToken, encodeCanonicalValue, hashCanonicalValue, parseProtocolIdentifier, parseProtocolQualifiedIdentifier, prepareProtocolDeploymentBundleManifest, prepareProtocolDeploymentContract, prepareProtocolDeploymentReleaseEnvelope, splitProtocolQualifiedIdentifier, validateCanonicalValue, validateProtocolDeploymentBundleManifest, validateProtocolDeploymentContract, validateProtocolDeploymentReleaseEnvelope, validateProtocolExpression, validateProtocolQueryDiagnostics, validateProtocolQueryEvent, validateProtocolQueryImplementation, validateProtocolSchema, validateProtocolSemanticQuery, validateProtocolSqlExpression, } from '@hypequery/protocol';
|
|
6
6
|
import { materializeBundle, materializeDeployment, materializeDiagnostics, materializeEvent, materializeExpression, materializeIdentifier, materializeImplementation, materializeRelease, materializeSchema, materializeTaggedValue, } from './generators.js';
|
|
7
7
|
const STABLE_CODE = /^HQ_[A-Z0-9_]+$/;
|
|
8
8
|
/** Turns a thrown protocol error into a wire rejection; rethrows anything else. */
|
|
@@ -25,7 +25,25 @@ const ACCEPT = { ok: true };
|
|
|
25
25
|
function validationInput(c, materialize) {
|
|
26
26
|
return c.generator ? materialize(c.generator) : c.value;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* RFC 0012 hostile-object suite declaration for the TypeScript reference
|
|
30
|
+
* implementation. The cases live in @hypequery/protocol's
|
|
31
|
+
* `src/values/codec.test.ts`; each mechanism listed here is exercised there.
|
|
32
|
+
*/
|
|
33
|
+
export const REFERENCE_HOSTILE_OBJECT_SUITE = {
|
|
34
|
+
count: 7,
|
|
35
|
+
mechanisms: [
|
|
36
|
+
'getter',
|
|
37
|
+
'toJSON',
|
|
38
|
+
'proxy',
|
|
39
|
+
'custom-prototype',
|
|
40
|
+
'symbol-key',
|
|
41
|
+
'sparse-array',
|
|
42
|
+
'cycle',
|
|
43
|
+
],
|
|
44
|
+
};
|
|
28
45
|
export const REFERENCE_FAMILIES = [
|
|
46
|
+
'cache-keys-v1',
|
|
29
47
|
'tagged-values-v1',
|
|
30
48
|
'identifiers-v1',
|
|
31
49
|
'expressions-v1',
|
|
@@ -68,10 +86,43 @@ export function referenceHandle(family, role, c, section) {
|
|
|
68
86
|
return handleBundle(role, c);
|
|
69
87
|
case 'deployment-releases-v1':
|
|
70
88
|
return handleRelease(role, c);
|
|
89
|
+
case 'cache-keys-v1':
|
|
90
|
+
return handleCacheKey(role, c);
|
|
71
91
|
default:
|
|
72
92
|
throw new Error(`reference adapter does not support family ${family}`);
|
|
73
93
|
}
|
|
74
94
|
}
|
|
95
|
+
function hexToBytes(hex) {
|
|
96
|
+
const out = new Uint8Array(hex.length / 2);
|
|
97
|
+
for (let index = 0; index < out.length; index += 1) {
|
|
98
|
+
out[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
function handleCacheKey(role, c) {
|
|
103
|
+
const generator = c.generator;
|
|
104
|
+
const preimage = generator?.type === 'repeat-string'
|
|
105
|
+
? (generator.utf8 ?? '').repeat(generator.count ?? 0)
|
|
106
|
+
: (c.preimageUtf8 ?? '');
|
|
107
|
+
const namespace = c.namespace;
|
|
108
|
+
return attempt(() => {
|
|
109
|
+
const key = deriveProtocolCacheKey({
|
|
110
|
+
secret: hexToBytes(c.secretHex),
|
|
111
|
+
namespace,
|
|
112
|
+
keyVersion: c.keyVersion,
|
|
113
|
+
preimage,
|
|
114
|
+
});
|
|
115
|
+
if (role !== 'success')
|
|
116
|
+
return { ok: true };
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
output: {
|
|
120
|
+
key,
|
|
121
|
+
namespaceToken: deriveProtocolCacheNamespaceToken(hexToBytes(c.secretHex), namespace.project, namespace.environment),
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
}
|
|
75
126
|
function handleTaggedValue(role, c) {
|
|
76
127
|
if (role === 'success') {
|
|
77
128
|
return attempt(() => ({
|
package/dist/adapters/stdio.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { FixtureRole, HandlerResult } from '../types.js';
|
|
1
|
+
import type { FixtureRole, HandlerResult, HostileObjectSuiteDeclaration } from '../types.js';
|
|
2
2
|
export interface StdioAdapterOptions {
|
|
3
3
|
readonly implementation?: string;
|
|
4
4
|
readonly version?: string;
|
|
5
5
|
readonly language?: string;
|
|
6
6
|
readonly families: readonly string[];
|
|
7
|
+
/** RFC 0012 language-specific hostile-object suite declaration. */
|
|
8
|
+
readonly hostileObjectSuite?: HostileObjectSuiteDeclaration;
|
|
7
9
|
readonly handle: (family: string, role: FixtureRole, fixtureCase: Record<string, unknown>, section: string | undefined) => HandlerResult | Promise<HandlerResult>;
|
|
8
10
|
/** Streams default to the process stdio; overridable for tests. */
|
|
9
11
|
readonly input?: NodeJS.ReadableStream;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/adapters/stdio.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/adapters/stdio.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAE7F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,mEAAmE;IACnE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,CACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,EAAE,MAAM,GAAG,SAAS,KACxB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgFhF"}
|
package/dist/adapters/stdio.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Reference adapter executable. Wraps @hypequery/protocol behind the RFC 0012
|
|
3
3
|
// stdio protocol so the runner can drive it like any other implementation.
|
|
4
4
|
import { createStdioAdapter } from '../adapters/stdio.js';
|
|
5
|
-
import { REFERENCE_FAMILIES, referenceHandle } from '../adapters/reference.js';
|
|
5
|
+
import { REFERENCE_FAMILIES, REFERENCE_HOSTILE_OBJECT_SUITE, referenceHandle, } from '../adapters/reference.js';
|
|
6
6
|
createStdioAdapter({
|
|
7
7
|
implementation: '@hypequery/protocol',
|
|
8
8
|
language: 'typescript',
|
|
9
9
|
families: [...REFERENCE_FAMILIES],
|
|
10
|
+
hostileObjectSuite: REFERENCE_HOSTILE_OBJECT_SUITE,
|
|
10
11
|
handle: referenceHandle,
|
|
11
12
|
})
|
|
12
13
|
.then((code) => process.exit(code))
|
package/dist/compare.js
CHANGED
|
@@ -121,5 +121,15 @@ function compareSuccessOutput(ec, o) {
|
|
|
121
121
|
return outcome(ec, 'fail', { expected: 'segments', actual: 'mismatch' });
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
if (ec.family === 'cache-keys-v1') {
|
|
125
|
+
// Reported as 'mismatch' rather than by value: a run against non-fixture
|
|
126
|
+
// inputs would otherwise print keys derived from a real secret.
|
|
127
|
+
if (o.key !== ec.case.key) {
|
|
128
|
+
return outcome(ec, 'fail', { expected: 'key', actual: 'mismatch' });
|
|
129
|
+
}
|
|
130
|
+
if (o.namespaceToken !== ec.case.namespaceToken) {
|
|
131
|
+
return outcome(ec, 'fail', { expected: 'namespaceToken', actual: 'mismatch' });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
124
134
|
return outcome(ec, 'pass');
|
|
125
135
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,6 @@ export type { JsonLoader } from './manifest.js';
|
|
|
6
6
|
export { compareCase } from './compare.js';
|
|
7
7
|
export { formatPrettyReport, formatJsonReport } from './report.js';
|
|
8
8
|
export { createStdioAdapter } from './adapters/stdio.js';
|
|
9
|
-
export { referenceHandle, REFERENCE_FAMILIES } from './adapters/reference.js';
|
|
9
|
+
export { referenceHandle, REFERENCE_FAMILIES, REFERENCE_HOSTILE_OBJECT_SUITE, } from './adapters/reference.js';
|
|
10
10
|
export * from './types.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,yBAAyB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,5 +5,5 @@ export { enumerateAllCases, enumerateFamilyCases, enumerateFuzzCases, resolveJso
|
|
|
5
5
|
export { compareCase } from './compare.js';
|
|
6
6
|
export { formatPrettyReport, formatJsonReport } from './report.js';
|
|
7
7
|
export { createStdioAdapter } from './adapters/stdio.js';
|
|
8
|
-
export { referenceHandle, REFERENCE_FAMILIES } from './adapters/reference.js';
|
|
8
|
+
export { referenceHandle, REFERENCE_FAMILIES, REFERENCE_HOSTILE_OBJECT_SUITE, } from './adapters/reference.js';
|
|
9
9
|
export * from './types.js';
|
package/dist/report.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAE5D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CA0B9D"}
|
package/dist/report.js
CHANGED
|
@@ -19,5 +19,9 @@ export function formatPrettyReport(summary) {
|
|
|
19
19
|
}
|
|
20
20
|
lines.push(`${summary.passed} passed, ${summary.failed} failed, ${summary.skipped} skipped`
|
|
21
21
|
+ (summary.notRun > 0 ? `, ${summary.notRun} not run (family not announced)` : ''));
|
|
22
|
+
const suite = adapter?.hostileObjectSuite;
|
|
23
|
+
if (suite) {
|
|
24
|
+
lines.push(`hostile-object suite: ${suite.count} cases (${suite.mechanisms.join(', ')})`);
|
|
25
|
+
}
|
|
22
26
|
return lines.join('\n');
|
|
23
27
|
}
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAQA,OAAO,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAQA,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAyJD,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6GxF"}
|
package/dist/runner.js
CHANGED
|
@@ -12,6 +12,56 @@ class AdapterExitError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
class AdapterTimeoutError extends Error {
|
|
14
14
|
}
|
|
15
|
+
function parseHostileObjectSuite(value) {
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
19
|
+
throw new Error('adapter hostileObjectSuite must be an object');
|
|
20
|
+
}
|
|
21
|
+
const suite = value;
|
|
22
|
+
if (!Number.isSafeInteger(suite.count) || suite.count < 1) {
|
|
23
|
+
throw new Error('adapter hostileObjectSuite count must be a positive safe integer');
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(suite.mechanisms)
|
|
26
|
+
|| suite.mechanisms.length === 0
|
|
27
|
+
|| !suite.mechanisms.every((mechanism) => typeof mechanism === 'string' && mechanism.length > 0)
|
|
28
|
+
|| new Set(suite.mechanisms).size !== suite.mechanisms.length) {
|
|
29
|
+
throw new Error('adapter hostileObjectSuite mechanisms must be unique non-empty strings');
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
count: suite.count,
|
|
33
|
+
mechanisms: [...suite.mechanisms],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function parseAdapterHello(message, hostModelFamilies) {
|
|
37
|
+
if (message.type !== 'hello'
|
|
38
|
+
|| message.protocol !== CONFORMANCE_PROTOCOL_VERSION
|
|
39
|
+
|| !Array.isArray(message.families)
|
|
40
|
+
|| !message.families.every((family) => typeof family === 'string' && family.length > 0)) {
|
|
41
|
+
throw new Error('adapter did not answer the handshake');
|
|
42
|
+
}
|
|
43
|
+
const families = message.families;
|
|
44
|
+
const hostileObjectSuite = parseHostileObjectSuite(message.hostileObjectSuite);
|
|
45
|
+
if (families.some((family) => hostModelFamilies.has(family)) && !hostileObjectSuite) {
|
|
46
|
+
throw new Error('adapter must declare hostileObjectSuite for host-model families');
|
|
47
|
+
}
|
|
48
|
+
for (const field of ['implementation', 'version', 'language']) {
|
|
49
|
+
if (message[field] !== undefined && typeof message[field] !== 'string') {
|
|
50
|
+
throw new Error(`adapter hello ${field} must be a string`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
type: 'hello',
|
|
55
|
+
protocol: CONFORMANCE_PROTOCOL_VERSION,
|
|
56
|
+
families: [...families],
|
|
57
|
+
...(typeof message.implementation === 'string'
|
|
58
|
+
? { implementation: message.implementation }
|
|
59
|
+
: {}),
|
|
60
|
+
...(typeof message.version === 'string' ? { version: message.version } : {}),
|
|
61
|
+
...(typeof message.language === 'string' ? { language: message.language } : {}),
|
|
62
|
+
...(hostileObjectSuite ? { hostileObjectSuite } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
15
65
|
/** A single adapter child process with a line-oriented message queue. */
|
|
16
66
|
class AdapterConnection {
|
|
17
67
|
child;
|
|
@@ -72,17 +122,14 @@ class AdapterConnection {
|
|
|
72
122
|
this.waiter = (message) => settle(() => resolve(message));
|
|
73
123
|
});
|
|
74
124
|
}
|
|
75
|
-
async handshake(timeoutMs) {
|
|
125
|
+
async handshake(timeoutMs, hostModelFamilies) {
|
|
76
126
|
this.write({
|
|
77
127
|
type: 'hello',
|
|
78
128
|
protocol: CONFORMANCE_PROTOCOL_VERSION,
|
|
79
129
|
manifestVersion: CONFORMANCE_MANIFEST_VERSION,
|
|
80
130
|
});
|
|
81
131
|
const message = await this.nextMessage(timeoutMs);
|
|
82
|
-
|
|
83
|
-
throw new Error('adapter did not answer the handshake');
|
|
84
|
-
}
|
|
85
|
-
return message;
|
|
132
|
+
return parseAdapterHello(message, hostModelFamilies);
|
|
86
133
|
}
|
|
87
134
|
end() {
|
|
88
135
|
this.write({ type: 'end' });
|
|
@@ -98,7 +145,16 @@ export async function runConformance(options) {
|
|
|
98
145
|
const fixturesDir = resolveFixturesDir(options.fixturesDir);
|
|
99
146
|
const manifest = loadManifest(fixturesDir);
|
|
100
147
|
const loadJson = createJsonLoader(fixturesDir);
|
|
101
|
-
|
|
148
|
+
const allCases = enumerateAllCases(manifest, loadJson);
|
|
149
|
+
const hostModelFamilies = new Set(allCases
|
|
150
|
+
.filter((c) => {
|
|
151
|
+
const generator = c.case.generator;
|
|
152
|
+
return typeof generator === 'object'
|
|
153
|
+
&& generator !== null
|
|
154
|
+
&& generator.type === 'unsafe-accessor';
|
|
155
|
+
})
|
|
156
|
+
.map((c) => c.family));
|
|
157
|
+
let cases = allCases;
|
|
102
158
|
if (options.onlyFuzz)
|
|
103
159
|
cases = cases.filter((c) => c.role === 'fuzz');
|
|
104
160
|
if (options.skipFuzz)
|
|
@@ -112,7 +168,14 @@ export async function runConformance(options) {
|
|
|
112
168
|
// per-case timeout so a busy machine never mistakes startup for a hang.
|
|
113
169
|
const handshakeTimeoutMs = Math.max(timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
114
170
|
let connection = new AdapterConnection(options.adapterCommand);
|
|
115
|
-
let hello
|
|
171
|
+
let hello;
|
|
172
|
+
try {
|
|
173
|
+
hello = await connection.handshake(handshakeTimeoutMs, hostModelFamilies);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
connection.kill();
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
116
179
|
let announced = new Set(hello.families);
|
|
117
180
|
let respawnBudget = 1;
|
|
118
181
|
const outcomes = [];
|
|
@@ -164,7 +227,7 @@ export async function runConformance(options) {
|
|
|
164
227
|
connection.kill();
|
|
165
228
|
connection = new AdapterConnection(options.adapterCommand);
|
|
166
229
|
try {
|
|
167
|
-
hello = await connection.handshake(handshakeTimeoutMs);
|
|
230
|
+
hello = await connection.handshake(handshakeTimeoutMs, hostModelFamilies);
|
|
168
231
|
}
|
|
169
232
|
catch {
|
|
170
233
|
// A replacement that cannot even complete a handshake ends the run
|
|
@@ -191,6 +254,9 @@ function summarize(outcomes, notRun, hello) {
|
|
|
191
254
|
version: hello.version,
|
|
192
255
|
language: hello.language,
|
|
193
256
|
families: hello.families,
|
|
257
|
+
...(hello.hostileObjectSuite
|
|
258
|
+
? { hostileObjectSuite: hello.hostileObjectSuite }
|
|
259
|
+
: {}),
|
|
194
260
|
},
|
|
195
261
|
outcomes,
|
|
196
262
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,18 @@ export interface EnumeratedCase {
|
|
|
33
33
|
readonly section?: string;
|
|
34
34
|
readonly case: Record<string, unknown>;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* RFC 0012 requires every implementation to declare a language-specific
|
|
38
|
+
* hostile-object suite covering the conversion mechanisms the shared
|
|
39
|
+
* `unsafe-accessor` generator cannot describe. The declaration travels in the
|
|
40
|
+
* adapter's `hello` message so it lands in the published run summary.
|
|
41
|
+
*/
|
|
42
|
+
export interface HostileObjectSuiteDeclaration {
|
|
43
|
+
/** Number of cases in the implementation's own suite. */
|
|
44
|
+
readonly count: number;
|
|
45
|
+
/** Conversion mechanisms covered, e.g. `getter`, `toJSON`, `__str__`. */
|
|
46
|
+
readonly mechanisms: readonly string[];
|
|
47
|
+
}
|
|
36
48
|
export interface AdapterHello {
|
|
37
49
|
readonly type: 'hello';
|
|
38
50
|
readonly protocol: number;
|
|
@@ -40,6 +52,7 @@ export interface AdapterHello {
|
|
|
40
52
|
readonly version?: string;
|
|
41
53
|
readonly language?: string;
|
|
42
54
|
readonly families: readonly string[];
|
|
55
|
+
readonly hostileObjectSuite?: HostileObjectSuiteDeclaration;
|
|
43
56
|
}
|
|
44
57
|
export type HandlerResult = {
|
|
45
58
|
readonly ok: true;
|
|
@@ -78,6 +91,7 @@ export interface RunSummary {
|
|
|
78
91
|
readonly version?: string;
|
|
79
92
|
readonly language?: string;
|
|
80
93
|
readonly families: readonly string[];
|
|
94
|
+
readonly hostileObjectSuite?: HostileObjectSuiteDeclaration;
|
|
81
95
|
};
|
|
82
96
|
readonly outcomes: readonly CaseOutcome[];
|
|
83
97
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,WAAW,GACX,UAAU,GACV,UAAU,GACV,cAAc,GACd,MAAM,CAAC;AAEX,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,gCAAgC,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC7C;AAED,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Cache key version 1 fixtures
|
|
2
|
+
|
|
3
|
+
These language-neutral fixtures accompany accepted RFC 0013 and are normative
|
|
4
|
+
for cache key version 1.
|
|
5
|
+
|
|
6
|
+
## Success manifest
|
|
7
|
+
|
|
8
|
+
Each entry in `success.json` contains:
|
|
9
|
+
|
|
10
|
+
- `id`: stable fixture identifier;
|
|
11
|
+
- `secretHex`: the cache-key secret as lowercase hex;
|
|
12
|
+
- `namespace`: the `{project, environment}` pair;
|
|
13
|
+
- `keyVersion`: the integer key version;
|
|
14
|
+
- `preimageUtf8`: the exact canonical preimage as UTF-8 text;
|
|
15
|
+
- `namespaceToken`: the expected opaque namespace prefix;
|
|
16
|
+
- `key`: the expected complete store key.
|
|
17
|
+
|
|
18
|
+
Both `namespaceToken` and `key` are exact. An implementation that produces a
|
|
19
|
+
different string for the same inputs is non-conformant, not merely different.
|
|
20
|
+
|
|
21
|
+
## Rejection manifest
|
|
22
|
+
|
|
23
|
+
Entries in `rejections.json` carry the same input fields plus the required
|
|
24
|
+
stable `error` code. An entry may replace `preimageUtf8` with a `generator`:
|
|
25
|
+
|
|
26
|
+
- `repeat-string`: concatenates `count` copies of the UTF-8 string `utf8`.
|
|
27
|
+
|
|
28
|
+
`secretHex` is an empty string for the missing-secret case.
|
|
29
|
+
The `precedence-*` cases deliberately violate several constraints at once and
|
|
30
|
+
pin the RFC's required first failure.
|
|
31
|
+
|
|
32
|
+
## How these expectations were produced
|
|
33
|
+
|
|
34
|
+
The expected keys were generated with an implementation independent of the
|
|
35
|
+
TypeScript reference: Node's built-in `crypto.createHmac` rather than the
|
|
36
|
+
`@noble/hashes` primitives `@hypequery/protocol` uses. Agreement between the
|
|
37
|
+
two is therefore cross-validation rather than a restatement of one
|
|
38
|
+
implementation's behaviour.
|
|
39
|
+
|
|
40
|
+
## What the corpus is designed to prove
|
|
41
|
+
|
|
42
|
+
Beyond exact derivation, the success cases demonstrate the separation
|
|
43
|
+
properties the scheme exists for:
|
|
44
|
+
|
|
45
|
+
- `namespace-separates-environment` and `namespace-separates-project` — an
|
|
46
|
+
identical preimage under a different namespace yields a different key, so a
|
|
47
|
+
shared store cannot serve one environment's entry to another;
|
|
48
|
+
- `rotation-changes-key` — incrementing `keyVersion` changes the key, making
|
|
49
|
+
rotation a flush rather than a silent reuse;
|
|
50
|
+
- `secret-separates-key` — a different secret yields a different key even for
|
|
51
|
+
an identical namespace and preimage;
|
|
52
|
+
- `deployment-target-punctuation` and `deployment-target-leading-digit` — the
|
|
53
|
+
namespace accepts the RFC 0008 target grammar used by deployment releases;
|
|
54
|
+
- `sensitive-preimage-not-recoverable` — a preimage containing an email
|
|
55
|
+
address and a tenant identifier produces a key containing neither.
|
|
56
|
+
|
|
57
|
+
The last of these is the whole point of the RFC: the previous scheme used the
|
|
58
|
+
readable preimage as the store key, so those values appeared in `SCAN` output,
|
|
59
|
+
eviction logs, and metric labels.
|