@kubun/id 0.10.0 → 0.12.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/lib/attachment.d.ts +2 -2
- package/lib/attachment.js +96 -1
- package/lib/base.d.ts +20 -15
- package/lib/base.js +61 -1
- package/lib/bytes.d.ts +1 -1
- package/lib/bytes.js +24 -1
- package/lib/cluster-model.d.ts +2 -2
- package/lib/cluster-model.js +26 -1
- package/lib/decode.js +34 -1
- package/lib/document.d.ts +2 -2
- package/lib/document.js +81 -1
- package/lib/index.js +8 -1
- package/lib/model.d.ts +4 -4
- package/lib/model.js +111 -1
- package/lib/plugin-node.d.ts +2 -2
- package/lib/plugin-node.js +84 -1
- package/lib/utils.js +19 -1
- package/package.json +11 -11
package/lib/attachment.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class AttachmentID extends KubunID<
|
|
1
|
+
import { ATTACHMENT_BINARY, KubunID } from './base.js';
|
|
2
|
+
export declare class AttachmentID extends KubunID<typeof ATTACHMENT_BINARY> {
|
|
3
3
|
#private;
|
|
4
4
|
static create(value: Uint8Array, mimeType?: string): AttachmentID;
|
|
5
5
|
static fromBytes(input: Uint8Array): AttachmentID;
|
package/lib/attachment.js
CHANGED
|
@@ -1 +1,96 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { fromUTF, toUTF } from '@sozai/codec';
|
|
2
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
3
|
+
import { ATTACHMENT_BINARY, KubunID, NAMESPACE_CODE } from './base.js';
|
|
4
|
+
import { concat } from './bytes.js';
|
|
5
|
+
import { decodeVarint, digestBytes, encodeVarint } from './utils.js';
|
|
6
|
+
const DEFAULT_MIME_TYPE = 'application/octet-stream';
|
|
7
|
+
export class AttachmentID extends KubunID {
|
|
8
|
+
static create(value, mimeType) {
|
|
9
|
+
return new AttachmentID(digestBytes(value), value.byteLength, mimeType);
|
|
10
|
+
}
|
|
11
|
+
static fromBytes(input) {
|
|
12
|
+
let offset = 0;
|
|
13
|
+
// Check namespace = is it a KubunID?
|
|
14
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
15
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
16
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
17
|
+
}
|
|
18
|
+
offset += namespaceLength;
|
|
19
|
+
// Check type matches
|
|
20
|
+
const [type, typeLength] = decodeVarint(input, offset);
|
|
21
|
+
if (type !== ATTACHMENT_BINARY) {
|
|
22
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
23
|
+
}
|
|
24
|
+
offset += typeLength;
|
|
25
|
+
// Decode digest
|
|
26
|
+
const [digestLength, digestVarintLength] = decodeVarint(input, offset);
|
|
27
|
+
offset += digestVarintLength;
|
|
28
|
+
const digest = input.slice(offset, offset + digestLength);
|
|
29
|
+
offset += digestLength;
|
|
30
|
+
// Extract content length
|
|
31
|
+
const [contentLength, contentVarintLength] = decodeVarint(input, offset);
|
|
32
|
+
offset += contentVarintLength;
|
|
33
|
+
// Check if MIME type is provided
|
|
34
|
+
const [mimeTypeBytesLength, mimeTypeVarintLength] = decodeVarint(input, offset);
|
|
35
|
+
if (mimeTypeBytesLength === 0) {
|
|
36
|
+
// No MIME type encoded, use default
|
|
37
|
+
return new AttachmentID(digest, contentLength);
|
|
38
|
+
}
|
|
39
|
+
// Decode MIME type value
|
|
40
|
+
offset += mimeTypeVarintLength;
|
|
41
|
+
const mimeTypeBytes = input.slice(offset, offset + mimeTypeBytesLength);
|
|
42
|
+
return new AttachmentID(digest, contentLength, toUTF(mimeTypeBytes));
|
|
43
|
+
}
|
|
44
|
+
static fromString(input) {
|
|
45
|
+
return AttachmentID.fromBytes(base36.decode(input));
|
|
46
|
+
}
|
|
47
|
+
static from(input) {
|
|
48
|
+
if (input instanceof AttachmentID) {
|
|
49
|
+
return input;
|
|
50
|
+
}
|
|
51
|
+
if (input instanceof Uint8Array) {
|
|
52
|
+
return AttachmentID.fromBytes(input);
|
|
53
|
+
}
|
|
54
|
+
if (typeof input === 'string') {
|
|
55
|
+
return AttachmentID.fromString(input);
|
|
56
|
+
}
|
|
57
|
+
throw new Error('Unsupported input type');
|
|
58
|
+
}
|
|
59
|
+
#contentLength;
|
|
60
|
+
#digest;
|
|
61
|
+
#mimeType;
|
|
62
|
+
constructor(digest, contentLength, mimeType = DEFAULT_MIME_TYPE){
|
|
63
|
+
let data;
|
|
64
|
+
const baseData = concat([
|
|
65
|
+
encodeVarint(digest.byteLength),
|
|
66
|
+
digest,
|
|
67
|
+
encodeVarint(contentLength)
|
|
68
|
+
]);
|
|
69
|
+
if (mimeType === DEFAULT_MIME_TYPE) {
|
|
70
|
+
data = concat([
|
|
71
|
+
baseData,
|
|
72
|
+
encodeVarint(0)
|
|
73
|
+
]);
|
|
74
|
+
} else {
|
|
75
|
+
const typeBytes = fromUTF(mimeType);
|
|
76
|
+
data = concat([
|
|
77
|
+
baseData,
|
|
78
|
+
encodeVarint(typeBytes.byteLength),
|
|
79
|
+
typeBytes
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
super(ATTACHMENT_BINARY, data);
|
|
83
|
+
this.#contentLength = contentLength;
|
|
84
|
+
this.#digest = digest;
|
|
85
|
+
this.#mimeType = mimeType;
|
|
86
|
+
}
|
|
87
|
+
get contentLength() {
|
|
88
|
+
return this.#contentLength;
|
|
89
|
+
}
|
|
90
|
+
get digest() {
|
|
91
|
+
return this.#digest;
|
|
92
|
+
}
|
|
93
|
+
get mimeType() {
|
|
94
|
+
return this.#mimeType;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/lib/base.d.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
export declare const NAMESPACE_CODE = 181;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export declare
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Immutable references
|
|
4
|
+
*/
|
|
5
|
+
export declare const ATTACHMENT_BINARY = 1;
|
|
6
|
+
export type AttachmentType = typeof ATTACHMENT_BINARY;
|
|
7
|
+
export declare const MODEL_SCALAR = 10;
|
|
8
|
+
export declare const MODEL_SHAPE = 11;
|
|
9
|
+
export declare const MODEL_DOCUMENT = 20;
|
|
10
|
+
export declare const MODEL_CLUSTER = 21;
|
|
11
|
+
export type ModelType = typeof MODEL_SCALAR | typeof MODEL_SHAPE | typeof MODEL_DOCUMENT | typeof MODEL_CLUSTER;
|
|
12
|
+
/**
|
|
13
|
+
* Mutable references
|
|
14
|
+
*/
|
|
15
|
+
export declare const DOCUMENT_MERGE = 30;
|
|
16
|
+
export type DocumentType = typeof DOCUMENT_MERGE;
|
|
17
|
+
/**
|
|
18
|
+
* External references
|
|
19
|
+
*/
|
|
20
|
+
export declare const EXTERNAL_PLUGIN_NODE = 40;
|
|
21
|
+
export type ExternalType = typeof EXTERNAL_PLUGIN_NODE;
|
|
17
22
|
export type KubunType = AttachmentType | ModelType | DocumentType | ExternalType;
|
|
18
23
|
export declare class KubunID<T extends KubunType = KubunType> {
|
|
19
24
|
#private;
|
package/lib/base.js
CHANGED
|
@@ -1 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
2
|
+
import { concat, equals } from './bytes.js';
|
|
3
|
+
import { encodeVarint } from './utils.js';
|
|
4
|
+
export const NAMESPACE_CODE = 0xb5;
|
|
5
|
+
const NAMESPACE_BYTES = encodeVarint(NAMESPACE_CODE);
|
|
6
|
+
/**
|
|
7
|
+
* Immutable references
|
|
8
|
+
*/ export const ATTACHMENT_BINARY = 1;
|
|
9
|
+
// DAG-only
|
|
10
|
+
export const MODEL_SCALAR = 10;
|
|
11
|
+
export const MODEL_SHAPE = 11;
|
|
12
|
+
// Circular
|
|
13
|
+
export const MODEL_DOCUMENT = 20;
|
|
14
|
+
export const MODEL_CLUSTER = 21;
|
|
15
|
+
/**
|
|
16
|
+
* Mutable references
|
|
17
|
+
*/ export const DOCUMENT_MERGE = 30;
|
|
18
|
+
/**
|
|
19
|
+
* External references
|
|
20
|
+
*/ export const EXTERNAL_PLUGIN_NODE = 40;
|
|
21
|
+
export class KubunID {
|
|
22
|
+
#type;
|
|
23
|
+
#dataBytes;
|
|
24
|
+
#idBytes;
|
|
25
|
+
#string;
|
|
26
|
+
constructor(type, dataBytes){
|
|
27
|
+
this.#type = type;
|
|
28
|
+
this.#dataBytes = dataBytes;
|
|
29
|
+
}
|
|
30
|
+
get type() {
|
|
31
|
+
return this.#type;
|
|
32
|
+
}
|
|
33
|
+
get data() {
|
|
34
|
+
return this.#dataBytes;
|
|
35
|
+
}
|
|
36
|
+
get bytes() {
|
|
37
|
+
if (this.#idBytes == null) {
|
|
38
|
+
this.#idBytes = this.toBytes();
|
|
39
|
+
}
|
|
40
|
+
return this.#idBytes;
|
|
41
|
+
}
|
|
42
|
+
equals(other) {
|
|
43
|
+
return other instanceof KubunID && other.type === this.type && equals(other.data, this.data);
|
|
44
|
+
}
|
|
45
|
+
toBytes() {
|
|
46
|
+
return concat([
|
|
47
|
+
NAMESPACE_BYTES,
|
|
48
|
+
encodeVarint(this.#type),
|
|
49
|
+
this.#dataBytes
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
toString() {
|
|
53
|
+
if (this.#string == null) {
|
|
54
|
+
this.#string = base36.encode(this.bytes);
|
|
55
|
+
}
|
|
56
|
+
return this.#string;
|
|
57
|
+
}
|
|
58
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
59
|
+
return `KubunID(${this.toString()})`;
|
|
60
|
+
}
|
|
61
|
+
}
|
package/lib/bytes.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function concat(arrays: Uint8Array
|
|
1
|
+
export declare function concat(arrays: Array<Uint8Array>, providedLength?: number): Uint8Array;
|
|
2
2
|
export declare function equals(a: Uint8Array, b: Uint8Array): boolean;
|
package/lib/bytes.js
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
export function concat(
|
|
1
|
+
export function concat(arrays, providedLength) {
|
|
2
|
+
const length = providedLength ?? arrays.reduce((acc, curr)=>acc + curr.length, 0);
|
|
3
|
+
const output = new Uint8Array(length);
|
|
4
|
+
let offset = 0;
|
|
5
|
+
for (const arr of arrays){
|
|
6
|
+
output.set(arr, offset);
|
|
7
|
+
offset += arr.length;
|
|
8
|
+
}
|
|
9
|
+
return output;
|
|
10
|
+
}
|
|
11
|
+
export function equals(a, b) {
|
|
12
|
+
if (a === b) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (a.byteLength !== b.byteLength) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
for(let i = 0; i < a.byteLength; i++){
|
|
19
|
+
if (a[i] !== b[i]) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
package/lib/cluster-model.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { KubunID,
|
|
2
|
-
export declare class ClusterModelID extends KubunID<
|
|
1
|
+
import { KubunID, MODEL_CLUSTER } from './base.js';
|
|
2
|
+
export declare class ClusterModelID extends KubunID<typeof MODEL_CLUSTER> {
|
|
3
3
|
static fromDigest(digestBytes: Uint8Array): ClusterModelID;
|
|
4
4
|
static fromBytes(input: Uint8Array): ClusterModelID;
|
|
5
5
|
static fromString(input: string): ClusterModelID;
|
package/lib/cluster-model.js
CHANGED
|
@@ -1 +1,26 @@
|
|
|
1
|
-
import{base36
|
|
1
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
2
|
+
import { KubunID, MODEL_CLUSTER, NAMESPACE_CODE } from './base.js';
|
|
3
|
+
import { decodeVarint } from './utils.js';
|
|
4
|
+
export class ClusterModelID extends KubunID {
|
|
5
|
+
static fromDigest(digestBytes) {
|
|
6
|
+
return new ClusterModelID(digestBytes);
|
|
7
|
+
}
|
|
8
|
+
static fromBytes(input) {
|
|
9
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
10
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
11
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
12
|
+
}
|
|
13
|
+
const [type, typeLength] = decodeVarint(input, namespaceLength);
|
|
14
|
+
if (type !== MODEL_CLUSTER) {
|
|
15
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
16
|
+
}
|
|
17
|
+
const digestBytes = input.slice(namespaceLength + typeLength);
|
|
18
|
+
return new ClusterModelID(digestBytes);
|
|
19
|
+
}
|
|
20
|
+
static fromString(input) {
|
|
21
|
+
return ClusterModelID.fromBytes(base36.decode(input));
|
|
22
|
+
}
|
|
23
|
+
constructor(digestBytes){
|
|
24
|
+
super(MODEL_CLUSTER, digestBytes);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/lib/decode.js
CHANGED
|
@@ -1 +1,34 @@
|
|
|
1
|
-
import{base36
|
|
1
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
2
|
+
import { AttachmentID } from './attachment.js';
|
|
3
|
+
import { ATTACHMENT_BINARY, DOCUMENT_MERGE, EXTERNAL_PLUGIN_NODE, MODEL_CLUSTER, MODEL_DOCUMENT, MODEL_SCALAR, MODEL_SHAPE, NAMESPACE_CODE } from './base.js';
|
|
4
|
+
import { ClusterModelID } from './cluster-model.js';
|
|
5
|
+
import { DocumentID } from './document.js';
|
|
6
|
+
import { DocumentModelID, ScalarModelID, ShapeModelID } from './model.js';
|
|
7
|
+
import { PluginNodeID } from './plugin-node.js';
|
|
8
|
+
import { decodeVarint } from './utils.js';
|
|
9
|
+
export function decodeID(input) {
|
|
10
|
+
const bytes = typeof input === 'string' ? base36.decode(input) : input;
|
|
11
|
+
const [namespace, namespaceLength] = decodeVarint(bytes);
|
|
12
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
13
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
14
|
+
}
|
|
15
|
+
const [type] = decodeVarint(bytes, namespaceLength);
|
|
16
|
+
switch(type){
|
|
17
|
+
case ATTACHMENT_BINARY:
|
|
18
|
+
return AttachmentID.fromBytes(bytes);
|
|
19
|
+
case MODEL_SCALAR:
|
|
20
|
+
return ScalarModelID.fromBytes(bytes);
|
|
21
|
+
case MODEL_SHAPE:
|
|
22
|
+
return ShapeModelID.fromBytes(bytes);
|
|
23
|
+
case MODEL_DOCUMENT:
|
|
24
|
+
return DocumentModelID.fromBytes(bytes);
|
|
25
|
+
case MODEL_CLUSTER:
|
|
26
|
+
return ClusterModelID.fromBytes(bytes);
|
|
27
|
+
case DOCUMENT_MERGE:
|
|
28
|
+
return DocumentID.fromBytes(bytes);
|
|
29
|
+
case EXTERNAL_PLUGIN_NODE:
|
|
30
|
+
return PluginNodeID.fromBytes(bytes);
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Invalid KubunID: unknown type ${type}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/lib/document.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DOCUMENT_MERGE, KubunID } from './base.js';
|
|
2
2
|
import { DocumentModelID } from './model.js';
|
|
3
|
-
export declare class DocumentID extends KubunID<
|
|
3
|
+
export declare class DocumentID extends KubunID<typeof DOCUMENT_MERGE> {
|
|
4
4
|
#private;
|
|
5
5
|
static create(model: DocumentModelID | Uint8Array | string, owner: string | Uint8Array, unique: Uint8Array): DocumentID;
|
|
6
6
|
static fromBytes(input: Uint8Array): DocumentID;
|
package/lib/document.js
CHANGED
|
@@ -1 +1,81 @@
|
|
|
1
|
-
import{fromUTF
|
|
1
|
+
import { fromUTF } from '@sozai/codec';
|
|
2
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
3
|
+
import { DOCUMENT_MERGE, KubunID, NAMESPACE_CODE } from './base.js';
|
|
4
|
+
import { concat } from './bytes.js';
|
|
5
|
+
import { DocumentModelID } from './model.js';
|
|
6
|
+
import { decodeVarint, digestBytes, encodeVarint } from './utils.js';
|
|
7
|
+
export class DocumentID extends KubunID {
|
|
8
|
+
static create(model, owner, unique) {
|
|
9
|
+
const contentBytes = concat([
|
|
10
|
+
typeof owner === 'string' ? fromUTF(owner) : owner,
|
|
11
|
+
unique
|
|
12
|
+
]);
|
|
13
|
+
return new DocumentID(DocumentModelID.from(model), digestBytes(contentBytes));
|
|
14
|
+
}
|
|
15
|
+
static fromBytes(input) {
|
|
16
|
+
let offset = 0;
|
|
17
|
+
// Check namespace = is it a KubunID?
|
|
18
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
19
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
20
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
21
|
+
}
|
|
22
|
+
offset += namespaceLength;
|
|
23
|
+
// Check type matches
|
|
24
|
+
const [type, typeLength] = decodeVarint(input, offset);
|
|
25
|
+
if (type !== DOCUMENT_MERGE) {
|
|
26
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
27
|
+
}
|
|
28
|
+
offset += typeLength;
|
|
29
|
+
// Decode model ID: digest bytes followed by index
|
|
30
|
+
const [modelDataByteLength, modelDataVarintLength] = decodeVarint(input, offset);
|
|
31
|
+
offset += modelDataVarintLength;
|
|
32
|
+
const modelDigest = input.slice(offset, offset + modelDataByteLength);
|
|
33
|
+
offset += modelDataByteLength;
|
|
34
|
+
const [modelIndex, modelIndexVarintLength] = decodeVarint(input, offset);
|
|
35
|
+
offset += modelIndexVarintLength;
|
|
36
|
+
const modelID = new DocumentModelID(modelDigest, modelIndex);
|
|
37
|
+
// Extract content digest bytes
|
|
38
|
+
const [contentByteLength, contentVarintLength] = decodeVarint(input, offset);
|
|
39
|
+
offset += contentVarintLength;
|
|
40
|
+
const contentBytes = input.slice(offset, offset + contentByteLength);
|
|
41
|
+
return new DocumentID(modelID, contentBytes);
|
|
42
|
+
}
|
|
43
|
+
static fromString(input) {
|
|
44
|
+
return DocumentID.fromBytes(base36.decode(input));
|
|
45
|
+
}
|
|
46
|
+
static from(input) {
|
|
47
|
+
if (input instanceof DocumentID) {
|
|
48
|
+
return input;
|
|
49
|
+
}
|
|
50
|
+
if (input instanceof Uint8Array) {
|
|
51
|
+
return DocumentID.fromBytes(input);
|
|
52
|
+
}
|
|
53
|
+
if (typeof input === 'string') {
|
|
54
|
+
return DocumentID.fromString(input);
|
|
55
|
+
}
|
|
56
|
+
throw new Error('Unsupported input type');
|
|
57
|
+
}
|
|
58
|
+
#model;
|
|
59
|
+
#content;
|
|
60
|
+
constructor(model, contentDigestBytes){
|
|
61
|
+
if (model.isLocal) {
|
|
62
|
+
throw new Error('Invalid DocumentID: model cannot be local');
|
|
63
|
+
}
|
|
64
|
+
const data = concat([
|
|
65
|
+
encodeVarint(model.data.byteLength),
|
|
66
|
+
model.data,
|
|
67
|
+
encodeVarint(model.index),
|
|
68
|
+
encodeVarint(contentDigestBytes.byteLength),
|
|
69
|
+
contentDigestBytes
|
|
70
|
+
]);
|
|
71
|
+
super(DOCUMENT_MERGE, data);
|
|
72
|
+
this.#model = model;
|
|
73
|
+
this.#content = contentDigestBytes;
|
|
74
|
+
}
|
|
75
|
+
get model() {
|
|
76
|
+
return this.#model;
|
|
77
|
+
}
|
|
78
|
+
get content() {
|
|
79
|
+
return this.#content;
|
|
80
|
+
}
|
|
81
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
export{AttachmentID}from
|
|
1
|
+
export { AttachmentID } from './attachment.js';
|
|
2
|
+
export { KubunID } from './base.js';
|
|
3
|
+
export { ClusterModelID } from './cluster-model.js';
|
|
4
|
+
export { decodeID } from './decode.js';
|
|
5
|
+
export { DocumentID } from './document.js';
|
|
6
|
+
export { DocumentModelID, ScalarModelID, ShapeModelID } from './model.js';
|
|
7
|
+
export { PluginNodeID } from './plugin-node.js';
|
|
8
|
+
export { digestBytes, digestJSON } from './utils.js';
|
package/lib/model.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { KubunID,
|
|
2
|
-
export declare class ScalarModelID extends KubunID<
|
|
1
|
+
import { KubunID, MODEL_DOCUMENT, MODEL_SCALAR, MODEL_SHAPE } from './base.js';
|
|
2
|
+
export declare class ScalarModelID extends KubunID<typeof MODEL_SCALAR> {
|
|
3
3
|
static create(value: unknown): ScalarModelID;
|
|
4
4
|
static fromBytes(input: Uint8Array): ScalarModelID;
|
|
5
5
|
static fromString(input: string): ScalarModelID;
|
|
6
6
|
constructor(digestBytes: Uint8Array);
|
|
7
7
|
}
|
|
8
|
-
export declare class ShapeModelID extends KubunID<
|
|
8
|
+
export declare class ShapeModelID extends KubunID<typeof MODEL_SHAPE> {
|
|
9
9
|
static create(value: unknown): ShapeModelID;
|
|
10
10
|
static fromBytes(input: Uint8Array): ShapeModelID;
|
|
11
11
|
static fromString(input: string): ShapeModelID;
|
|
12
12
|
constructor(digestBytes: Uint8Array);
|
|
13
13
|
}
|
|
14
|
-
export declare class DocumentModelID extends KubunID<
|
|
14
|
+
export declare class DocumentModelID extends KubunID<typeof MODEL_DOCUMENT> {
|
|
15
15
|
#private;
|
|
16
16
|
static fromBytes(input: Uint8Array): DocumentModelID;
|
|
17
17
|
static fromString(input: string): DocumentModelID;
|
package/lib/model.js
CHANGED
|
@@ -1 +1,111 @@
|
|
|
1
|
-
import{base36
|
|
1
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
2
|
+
import { KubunID, MODEL_DOCUMENT, MODEL_SCALAR, MODEL_SHAPE, NAMESPACE_CODE } from './base.js';
|
|
3
|
+
import { concat } from './bytes.js';
|
|
4
|
+
import { decodeVarint, digestJSON, encodeVarint } from './utils.js';
|
|
5
|
+
export class ScalarModelID extends KubunID {
|
|
6
|
+
static create(value) {
|
|
7
|
+
return new ScalarModelID(digestJSON(value));
|
|
8
|
+
}
|
|
9
|
+
static fromBytes(input) {
|
|
10
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
11
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
12
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
13
|
+
}
|
|
14
|
+
const [type, typeLength] = decodeVarint(input, namespaceLength);
|
|
15
|
+
if (type !== MODEL_SCALAR) {
|
|
16
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
17
|
+
}
|
|
18
|
+
const digestBytes = input.slice(namespaceLength + typeLength);
|
|
19
|
+
return new ScalarModelID(digestBytes);
|
|
20
|
+
}
|
|
21
|
+
static fromString(input) {
|
|
22
|
+
return ScalarModelID.fromBytes(base36.decode(input));
|
|
23
|
+
}
|
|
24
|
+
constructor(digestBytes){
|
|
25
|
+
super(MODEL_SCALAR, digestBytes);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class ShapeModelID extends KubunID {
|
|
29
|
+
static create(value) {
|
|
30
|
+
return new ShapeModelID(digestJSON(value));
|
|
31
|
+
}
|
|
32
|
+
static fromBytes(input) {
|
|
33
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
34
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
35
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
36
|
+
}
|
|
37
|
+
const [type, typeLength] = decodeVarint(input, namespaceLength);
|
|
38
|
+
if (type !== MODEL_SHAPE) {
|
|
39
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
40
|
+
}
|
|
41
|
+
const digestBytes = input.slice(namespaceLength + typeLength);
|
|
42
|
+
return new ShapeModelID(digestBytes);
|
|
43
|
+
}
|
|
44
|
+
static fromString(input) {
|
|
45
|
+
return ShapeModelID.fromBytes(base36.decode(input));
|
|
46
|
+
}
|
|
47
|
+
constructor(digestBytes){
|
|
48
|
+
super(MODEL_SHAPE, digestBytes);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const MAX_INDEX = 127;
|
|
52
|
+
export class DocumentModelID extends KubunID {
|
|
53
|
+
static fromBytes(input) {
|
|
54
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
55
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
56
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
57
|
+
}
|
|
58
|
+
const [type, typeLength] = decodeVarint(input, namespaceLength);
|
|
59
|
+
if (type !== MODEL_DOCUMENT) {
|
|
60
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
61
|
+
}
|
|
62
|
+
const digestBytes = input.slice(namespaceLength + typeLength, input.byteLength - 1);
|
|
63
|
+
const [index] = decodeVarint(input, input.byteLength - 1);
|
|
64
|
+
return new DocumentModelID(digestBytes, index);
|
|
65
|
+
}
|
|
66
|
+
static fromString(input) {
|
|
67
|
+
return DocumentModelID.fromBytes(base36.decode(input));
|
|
68
|
+
}
|
|
69
|
+
static from(input) {
|
|
70
|
+
if (input instanceof DocumentModelID) {
|
|
71
|
+
return input;
|
|
72
|
+
}
|
|
73
|
+
if (input instanceof Uint8Array) {
|
|
74
|
+
return DocumentModelID.fromBytes(input);
|
|
75
|
+
}
|
|
76
|
+
if (typeof input === 'string') {
|
|
77
|
+
return DocumentModelID.fromString(input);
|
|
78
|
+
}
|
|
79
|
+
throw new Error('Unsupported input type');
|
|
80
|
+
}
|
|
81
|
+
static local(index) {
|
|
82
|
+
return new DocumentModelID(new Uint8Array(0), index);
|
|
83
|
+
}
|
|
84
|
+
#index;
|
|
85
|
+
#isLocal;
|
|
86
|
+
constructor(digestBytes, index){
|
|
87
|
+
if (index > MAX_INDEX) {
|
|
88
|
+
// index should be encoded in a single byte
|
|
89
|
+
throw new RangeError(`Index ${index} exceeds maximum of ${MAX_INDEX}`);
|
|
90
|
+
}
|
|
91
|
+
super(MODEL_DOCUMENT, digestBytes);
|
|
92
|
+
this.#index = index;
|
|
93
|
+
this.#isLocal = digestBytes.length === 0;
|
|
94
|
+
}
|
|
95
|
+
get index() {
|
|
96
|
+
return this.#index;
|
|
97
|
+
}
|
|
98
|
+
get isLocal() {
|
|
99
|
+
return this.#isLocal;
|
|
100
|
+
}
|
|
101
|
+
toBytes() {
|
|
102
|
+
return concat([
|
|
103
|
+
super.toBytes(),
|
|
104
|
+
encodeVarint(this.#index)
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
toGlobal(digestBytesOrModelID) {
|
|
108
|
+
const digestBytes = digestBytesOrModelID instanceof DocumentModelID ? digestBytesOrModelID.data : digestBytesOrModelID;
|
|
109
|
+
return new DocumentModelID(digestBytes, this.#index);
|
|
110
|
+
}
|
|
111
|
+
}
|
package/lib/plugin-node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class PluginNodeID extends KubunID<
|
|
1
|
+
import { EXTERNAL_PLUGIN_NODE, KubunID } from './base.js';
|
|
2
|
+
export declare class PluginNodeID extends KubunID<typeof EXTERNAL_PLUGIN_NODE> {
|
|
3
3
|
#private;
|
|
4
4
|
static create(plugin: string, typeName: string, localID: string): PluginNodeID;
|
|
5
5
|
static fromBytes(input: Uint8Array): PluginNodeID;
|
package/lib/plugin-node.js
CHANGED
|
@@ -1 +1,84 @@
|
|
|
1
|
-
import{fromUTF
|
|
1
|
+
import { fromUTF, toUTF } from '@sozai/codec';
|
|
2
|
+
import { base36 } from 'multiformats/bases/base36';
|
|
3
|
+
import { EXTERNAL_PLUGIN_NODE, KubunID, NAMESPACE_CODE } from './base.js';
|
|
4
|
+
import { concat } from './bytes.js';
|
|
5
|
+
import { decodeVarint, encodeVarint } from './utils.js';
|
|
6
|
+
export class PluginNodeID extends KubunID {
|
|
7
|
+
static create(plugin, typeName, localID) {
|
|
8
|
+
return new PluginNodeID(plugin, typeName, localID);
|
|
9
|
+
}
|
|
10
|
+
static fromBytes(input) {
|
|
11
|
+
let offset = 0;
|
|
12
|
+
// Check namespace
|
|
13
|
+
const [namespace, namespaceLength] = decodeVarint(input);
|
|
14
|
+
if (namespace !== NAMESPACE_CODE) {
|
|
15
|
+
throw new Error('Invalid KubunID: namespace mismatch');
|
|
16
|
+
}
|
|
17
|
+
offset += namespaceLength;
|
|
18
|
+
// Check type matches
|
|
19
|
+
const [type, typeLength] = decodeVarint(input, offset);
|
|
20
|
+
if (type !== EXTERNAL_PLUGIN_NODE) {
|
|
21
|
+
throw new Error('Invalid KubunID: type mismatch');
|
|
22
|
+
}
|
|
23
|
+
offset += typeLength;
|
|
24
|
+
// Decode plugin
|
|
25
|
+
const [pluginLength, pluginVarintLength] = decodeVarint(input, offset);
|
|
26
|
+
offset += pluginVarintLength;
|
|
27
|
+
const plugin = pluginLength === 0 ? '' : toUTF(input.slice(offset, offset + pluginLength));
|
|
28
|
+
offset += pluginLength;
|
|
29
|
+
// Decode typeName
|
|
30
|
+
const [typeNameLength, typeNameVarintLength] = decodeVarint(input, offset);
|
|
31
|
+
offset += typeNameVarintLength;
|
|
32
|
+
const typeName = typeNameLength === 0 ? '' : toUTF(input.slice(offset, offset + typeNameLength));
|
|
33
|
+
offset += typeNameLength;
|
|
34
|
+
// Decode localID
|
|
35
|
+
const [localIDLength, localIDVarintLength] = decodeVarint(input, offset);
|
|
36
|
+
offset += localIDVarintLength;
|
|
37
|
+
const localID = localIDLength === 0 ? '' : toUTF(input.slice(offset, offset + localIDLength));
|
|
38
|
+
return new PluginNodeID(plugin, typeName, localID);
|
|
39
|
+
}
|
|
40
|
+
static fromString(input) {
|
|
41
|
+
return PluginNodeID.fromBytes(base36.decode(input));
|
|
42
|
+
}
|
|
43
|
+
static from(input) {
|
|
44
|
+
if (input instanceof PluginNodeID) {
|
|
45
|
+
return input;
|
|
46
|
+
}
|
|
47
|
+
if (input instanceof Uint8Array) {
|
|
48
|
+
return PluginNodeID.fromBytes(input);
|
|
49
|
+
}
|
|
50
|
+
if (typeof input === 'string') {
|
|
51
|
+
return PluginNodeID.fromString(input);
|
|
52
|
+
}
|
|
53
|
+
throw new Error('Unsupported input type');
|
|
54
|
+
}
|
|
55
|
+
#plugin;
|
|
56
|
+
#typeName;
|
|
57
|
+
#localID;
|
|
58
|
+
constructor(plugin, typeName, localID){
|
|
59
|
+
const pluginBytes = fromUTF(plugin);
|
|
60
|
+
const typeNameBytes = fromUTF(typeName);
|
|
61
|
+
const localIDBytes = fromUTF(localID);
|
|
62
|
+
const data = concat([
|
|
63
|
+
encodeVarint(pluginBytes.byteLength),
|
|
64
|
+
pluginBytes,
|
|
65
|
+
encodeVarint(typeNameBytes.byteLength),
|
|
66
|
+
typeNameBytes,
|
|
67
|
+
encodeVarint(localIDBytes.byteLength),
|
|
68
|
+
localIDBytes
|
|
69
|
+
]);
|
|
70
|
+
super(EXTERNAL_PLUGIN_NODE, data);
|
|
71
|
+
this.#plugin = plugin;
|
|
72
|
+
this.#typeName = typeName;
|
|
73
|
+
this.#localID = localID;
|
|
74
|
+
}
|
|
75
|
+
get plugin() {
|
|
76
|
+
return this.#plugin;
|
|
77
|
+
}
|
|
78
|
+
get typeName() {
|
|
79
|
+
return this.#typeName;
|
|
80
|
+
}
|
|
81
|
+
get localID() {
|
|
82
|
+
return this.#localID;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import { blake3 } from '@noble/hashes/blake3.js';
|
|
2
|
+
import { canonicalStringify, fromUTF } from '@sozai/codec';
|
|
3
|
+
import varint from 'varint';
|
|
4
|
+
export function digestBytes(bytes) {
|
|
5
|
+
return blake3(bytes);
|
|
6
|
+
}
|
|
7
|
+
export function digestJSON(value) {
|
|
8
|
+
return digestBytes(fromUTF(canonicalStringify(value)));
|
|
9
|
+
}
|
|
10
|
+
export function decodeVarint(bytes, offset) {
|
|
11
|
+
const value = varint.decode(bytes, offset);
|
|
12
|
+
return [
|
|
13
|
+
value,
|
|
14
|
+
varint.decode.bytes ?? 0
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
export function encodeVarint(value) {
|
|
18
|
+
return varint.encode(value, new Uint8Array(varint.encodingLength(value)));
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/id",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"license": "see LICENSE.md",
|
|
3
|
+
"version": "0.12.0",
|
|
5
4
|
"keywords": [],
|
|
5
|
+
"license": "see LICENSE.md",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"type": "module",
|
|
7
|
-
"main": "lib/index.js",
|
|
8
|
-
"types": "lib/index.d.ts",
|
|
9
8
|
"exports": {
|
|
10
9
|
".": "./lib/index.js"
|
|
11
10
|
},
|
|
11
|
+
"main": "lib/index.js",
|
|
12
|
+
"types": "lib/index.d.ts",
|
|
12
13
|
"files": [
|
|
13
14
|
"lib/*",
|
|
14
15
|
"LICENSE.md"
|
|
15
16
|
],
|
|
16
|
-
"sideEffects": false,
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@enkaku/codec": "^0.16.0",
|
|
19
18
|
"@noble/hashes": "^2.2.0",
|
|
20
|
-
"
|
|
19
|
+
"@sozai/codec": "^0.4.0",
|
|
20
|
+
"multiformats": "^14.0.5",
|
|
21
21
|
"varint": "^6.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/varint": "^6.0.3"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
+
"build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
|
|
27
28
|
"build:clean": "del lib",
|
|
28
|
-
"build:js": "swc src -d ./lib --config-file ../../swc.json --strip-leading-paths",
|
|
29
|
+
"build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
29
30
|
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
30
31
|
"build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
|
|
31
|
-
"
|
|
32
|
+
"test": "pnpm run test:types && pnpm run test:unit",
|
|
32
33
|
"test:types": "tsc --noEmit",
|
|
33
|
-
"test:unit": "vitest run"
|
|
34
|
-
"test": "pnpm run test:types && pnpm run test:unit"
|
|
34
|
+
"test:unit": "vitest run"
|
|
35
35
|
}
|
|
36
36
|
}
|