@opus-magnum/rubedo 1.0.0 → 1.0.2
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 +13 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/v1/RubedoForge.d.ts +2 -2
- package/dist/v1/RubedoForge.js +1 -1
- package/dist/v1/contract-test.js +7 -7
- package/dist/v1/index.d.ts +3 -0
- package/dist/v1/index.js +3 -0
- package/dist/v1/types.d.ts +27 -0
- package/dist/v1/types.js +4 -0
- package/package.json +25 -3
- package/src/index.ts +1 -3
- package/src/v1/RubedoForge.ts +3 -3
- package/src/v1/contract-test.ts +13 -12
- package/src/v1/index.ts +3 -0
- package/src/v1/types.ts +36 -0
- package/tsconfig.build.json +0 -13
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @opus-magnum/rubedo
|
|
2
|
+
|
|
3
|
+
**RUBEDO v1** — детерминированный движок структурного фингерпринтинга (canonical JSON + SHA-256), выделенный из sealed-ядра Opus Magnum.
|
|
4
|
+
|
|
5
|
+
## Гарантии v1
|
|
6
|
+
- Независимость от порядка ключей в объектах (canonicalizeJson → stableStringify)
|
|
7
|
+
- Независимость от порядка элементов synopsis (сортировка по `id`)
|
|
8
|
+
- Детерминированный `fingerprint` (SHA-256) и `artifact_id` (`RUBEDO::v1::<prefix>`)
|
|
9
|
+
- Контракт-инварианты через `contract-test`
|
|
10
|
+
|
|
11
|
+
## Установка
|
|
12
|
+
```bash
|
|
13
|
+
npm i @opus-magnum/rubedo
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/v1/RubedoForge.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IRubedoForge, RubedoArtifact,
|
|
1
|
+
import type { IRubedoForge, RubedoArtifact, RubedoForgeInput } from "./types.js";
|
|
2
2
|
export declare class RubedoForge implements IRubedoForge {
|
|
3
|
-
forge(input:
|
|
3
|
+
forge(input: RubedoForgeInput): RubedoArtifact;
|
|
4
4
|
}
|
package/dist/v1/RubedoForge.js
CHANGED
package/dist/v1/contract-test.js
CHANGED
|
@@ -2,23 +2,23 @@ import { RUBEDO_CONTRACT_V1 } from "./contract.js";
|
|
|
2
2
|
import { RubedoForge } from "./RubedoForge.js";
|
|
3
3
|
function assertEq(a, b, msg) {
|
|
4
4
|
if (a !== b)
|
|
5
|
-
throw new Error(msg
|
|
5
|
+
throw new Error(`${msg}: ${String(a)} !== ${String(b)}`);
|
|
6
6
|
}
|
|
7
7
|
console.log(`[RUBEDO_CONTRACT] version=${RUBEDO_CONTRACT_V1.version}`);
|
|
8
|
-
const
|
|
9
|
-
const fpOf = (synopsis) =>
|
|
10
|
-
//
|
|
8
|
+
const forge = new RubedoForge();
|
|
9
|
+
const fpOf = (synopsis) => forge.forge({ synopsis }).fingerprint;
|
|
10
|
+
// key order independence (value object must be stable-stringified)
|
|
11
11
|
const s1 = [{ id: "key:a", kind: "KEY", value: { a: 1, b: 2, c: { y: 9, z: 0 } } }];
|
|
12
12
|
const s2 = [{ id: "key:a", kind: "KEY", value: { b: 2, a: 1, c: { z: 0, y: 9 } } }];
|
|
13
13
|
assertEq(fpOf(s1), fpOf(s2), "KEY_ORDER_INDEPENDENCE");
|
|
14
|
-
//
|
|
14
|
+
// synopsis order independence (forge must sort synopsis)
|
|
15
15
|
const t1 = [
|
|
16
16
|
{ id: "k:1", kind: "KEY", value: 1 },
|
|
17
|
-
{ id: "k:2", kind: "KEY", value: 2 }
|
|
17
|
+
{ id: "k:2", kind: "KEY", value: 2 },
|
|
18
18
|
];
|
|
19
19
|
const t2 = [
|
|
20
20
|
{ id: "k:2", kind: "KEY", value: 2 },
|
|
21
|
-
{ id: "k:1", kind: "KEY", value: 1 }
|
|
21
|
+
{ id: "k:1", kind: "KEY", value: 1 },
|
|
22
22
|
];
|
|
23
23
|
assertEq(fpOf(t1), fpOf(t2), "SYNOPSIS_ORDER_INDEPENDENCE");
|
|
24
24
|
console.log("[PASS] RUBEDO package contract invariants satisfied");
|
package/dist/v1/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RUBEDO v1 — package public contract types (self-contained)
|
|
3
|
+
*/
|
|
4
|
+
export type RubedoValue = null | boolean | number | string | RubedoValue[] | {
|
|
5
|
+
[key: string]: RubedoValue;
|
|
6
|
+
};
|
|
7
|
+
export type RubedoSynopsisItem = {
|
|
8
|
+
id: string;
|
|
9
|
+
kind: string;
|
|
10
|
+
value: RubedoValue;
|
|
11
|
+
};
|
|
12
|
+
export type RubedoForgeInput = {
|
|
13
|
+
synopsis: RubedoSynopsisItem[];
|
|
14
|
+
};
|
|
15
|
+
export interface RubedoArtifact {
|
|
16
|
+
artifact_id: string;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
payload: {
|
|
19
|
+
synopsis: RubedoSynopsisItem[];
|
|
20
|
+
};
|
|
21
|
+
stats: {
|
|
22
|
+
count: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface IRubedoForge {
|
|
26
|
+
forge(input: RubedoForgeInput): RubedoArtifact;
|
|
27
|
+
}
|
package/dist/v1/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,21 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opus-magnum/rubedo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "RUBEDO v1 — deterministic structural fingerprint engine (sealed core of Opus Magnum)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"rubedo",
|
|
7
|
+
"fingerprint",
|
|
8
|
+
"hash",
|
|
9
|
+
"deterministic",
|
|
10
|
+
"canonical-json",
|
|
11
|
+
"sha256"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
4
14
|
"type": "module",
|
|
5
15
|
"main": "dist/index.js",
|
|
6
16
|
"types": "dist/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
7
22
|
"exports": {
|
|
8
23
|
".": {
|
|
9
24
|
"types": "./dist/index.d.ts",
|
|
10
25
|
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./v1": {
|
|
28
|
+
"types": "./dist/v1/index.d.ts",
|
|
29
|
+
"default": "./dist/v1/index.js"
|
|
11
30
|
}
|
|
12
31
|
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
13
35
|
"scripts": {
|
|
14
36
|
"build": "npx tsc -p tsconfig.build.json",
|
|
15
37
|
"test:contract": "npm run -s build >/dev/null && node dist/v1/contract-test.js"
|
|
16
38
|
},
|
|
17
39
|
"devDependencies": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
40
|
+
"@types/node": "^22.19.3",
|
|
41
|
+
"typescript": "^5.9.3"
|
|
20
42
|
}
|
|
21
43
|
}
|
package/src/index.ts
CHANGED
package/src/v1/RubedoForge.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import type { IRubedoForge, RubedoArtifact,
|
|
3
|
-
import { stableStringify } from "./canonical-json";
|
|
2
|
+
import type { IRubedoForge, RubedoArtifact, RubedoForgeInput, RubedoSynopsisItem } from "./types.js";
|
|
3
|
+
import { stableStringify } from "./canonical-json.js";
|
|
4
4
|
|
|
5
5
|
function sha256Hex(s: string): string {
|
|
6
6
|
return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
|
|
@@ -8,7 +8,7 @@ function sha256Hex(s: string): string {
|
|
|
8
8
|
|
|
9
9
|
export class RubedoForge implements IRubedoForge {
|
|
10
10
|
|
|
11
|
-
forge(input:
|
|
11
|
+
forge(input: RubedoForgeInput): RubedoArtifact {
|
|
12
12
|
const synopsis = [...input.synopsis].slice().sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
13
13
|
const payload = { synopsis };
|
|
14
14
|
|
package/src/v1/contract-test.ts
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import { RUBEDO_CONTRACT_V1 } from "./contract.js";
|
|
2
2
|
import { RubedoForge } from "./RubedoForge.js";
|
|
3
|
+
import type { RubedoSynopsisItem } from "./types.js";
|
|
3
4
|
|
|
4
|
-
function assertEq(a:
|
|
5
|
-
if (a !== b) throw new Error(msg
|
|
5
|
+
function assertEq(a: string, b: string, msg: string) {
|
|
6
|
+
if (a !== b) throw new Error(`${msg}: ${String(a)} !== ${String(b)}`);
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
console.log(`[RUBEDO_CONTRACT] version=${RUBEDO_CONTRACT_V1.version}`);
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
const fpOf = (synopsis:
|
|
11
|
+
const forge = new RubedoForge();
|
|
12
|
+
const fpOf = (synopsis: RubedoSynopsisItem[]) => forge.forge({ synopsis }).fingerprint;
|
|
12
13
|
|
|
13
|
-
//
|
|
14
|
-
const s1 = [{ id: "key:a", kind: "KEY", value: { a: 1, b: 2, c: { y: 9, z: 0 } } }];
|
|
15
|
-
const s2 = [{ id: "key:a", kind: "KEY", value: { b: 2, a: 1, c: { z: 0, y: 9 } } }];
|
|
14
|
+
// key order independence (value object must be stable-stringified)
|
|
15
|
+
const s1: RubedoSynopsisItem[] = [{ id: "key:a", kind: "KEY", value: { a: 1, b: 2, c: { y: 9, z: 0 } } }];
|
|
16
|
+
const s2: RubedoSynopsisItem[] = [{ id: "key:a", kind: "KEY", value: { b: 2, a: 1, c: { z: 0, y: 9 } } }];
|
|
16
17
|
assertEq(fpOf(s1), fpOf(s2), "KEY_ORDER_INDEPENDENCE");
|
|
17
18
|
|
|
18
|
-
//
|
|
19
|
-
const t1 = [
|
|
19
|
+
// synopsis order independence (forge must sort synopsis)
|
|
20
|
+
const t1: RubedoSynopsisItem[] = [
|
|
20
21
|
{ id: "k:1", kind: "KEY", value: 1 },
|
|
21
|
-
{ id: "k:2", kind: "KEY", value: 2 }
|
|
22
|
+
{ id: "k:2", kind: "KEY", value: 2 },
|
|
22
23
|
];
|
|
23
|
-
const t2 = [
|
|
24
|
+
const t2: RubedoSynopsisItem[] = [
|
|
24
25
|
{ id: "k:2", kind: "KEY", value: 2 },
|
|
25
|
-
{ id: "k:1", kind: "KEY", value: 1 }
|
|
26
|
+
{ id: "k:1", kind: "KEY", value: 1 },
|
|
26
27
|
];
|
|
27
28
|
assertEq(fpOf(t1), fpOf(t2), "SYNOPSIS_ORDER_INDEPENDENCE");
|
|
28
29
|
|
package/src/v1/index.ts
ADDED
package/src/v1/types.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RUBEDO v1 — package public contract types (self-contained)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type RubedoValue =
|
|
6
|
+
| null
|
|
7
|
+
| boolean
|
|
8
|
+
| number
|
|
9
|
+
| string
|
|
10
|
+
| RubedoValue[]
|
|
11
|
+
| { [key: string]: RubedoValue };
|
|
12
|
+
|
|
13
|
+
export type RubedoSynopsisItem = {
|
|
14
|
+
id: string;
|
|
15
|
+
kind: string;
|
|
16
|
+
value: RubedoValue;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type RubedoForgeInput = {
|
|
20
|
+
synopsis: RubedoSynopsisItem[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface RubedoArtifact {
|
|
24
|
+
artifact_id: string;
|
|
25
|
+
fingerprint: string;
|
|
26
|
+
payload: {
|
|
27
|
+
synopsis: RubedoSynopsisItem[];
|
|
28
|
+
};
|
|
29
|
+
stats: {
|
|
30
|
+
count: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IRubedoForge {
|
|
35
|
+
forge(input: RubedoForgeInput): RubedoArtifact;
|
|
36
|
+
}
|
package/tsconfig.build.json
DELETED