@needle-tools/addressing 0.3.2 → 0.3.3
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 +70 -70
- package/dist/address.js +7 -2
- package/dist/anchor.d.ts +12 -0
- package/dist/anchor.js +11 -8
- package/dist/gltf.js +6 -4
- package/dist/id.d.ts +30 -11
- package/dist/id.js +39 -22
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/resolve.d.ts +62 -6
- package/dist/resolve.js +141 -21
- package/package.json +1 -1
- package/src/address.ts +111 -106
- package/src/anchor.ts +71 -68
- package/src/gltf.ts +91 -89
- package/src/hash.ts +58 -58
- package/src/id.ts +61 -45
- package/src/index.ts +34 -29
- package/src/resolve.ts +315 -172
- package/src/uuid.ts +48 -48
package/src/gltf.ts
CHANGED
|
@@ -1,89 +1,91 @@
|
|
|
1
|
-
import { computeElementId } from "./id.js";
|
|
2
|
-
|
|
3
|
-
/** The glTF `extras` key carrying the persistent id. */
|
|
4
|
-
export const NEEDLE_ID_KEY = "needle-id";
|
|
5
|
-
|
|
6
|
-
/** All root-collection element types that receive ids, with their glTF collection key. */
|
|
7
|
-
export const STAMPED_COLLECTIONS = [
|
|
8
|
-
["node", "nodes"],
|
|
9
|
-
["scene", "scenes"],
|
|
10
|
-
["animation", "animations"],
|
|
11
|
-
["camera", "cameras"],
|
|
12
|
-
["material", "materials"],
|
|
13
|
-
["mesh", "meshes"],
|
|
14
|
-
["skin", "skins"],
|
|
15
|
-
["texture", "textures"],
|
|
16
|
-
["image", "images"],
|
|
17
|
-
["sampler", "samplers"],
|
|
18
|
-
] as const satisfies ReadonlyArray<readonly [string, string]>;
|
|
19
|
-
|
|
20
|
-
export type StampedElementType = (typeof STAMPED_COLLECTIONS)[number][0];
|
|
21
|
-
|
|
22
|
-
/** Structural (duck-typed) view of a glTF root element — no glTF library dependency. */
|
|
23
|
-
export type GltfElement = {
|
|
24
|
-
name?: string;
|
|
25
|
-
extras?: Record<string, unknown>;
|
|
26
|
-
[key: string]: unknown;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/** Structural view of a glTF JSON root. Collections are looked up by key. */
|
|
30
|
-
export type GltfJson = {
|
|
31
|
-
[collection: string]: unknown;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type EnsureIdsOptions = {
|
|
35
|
-
/**
|
|
36
|
-
* Optional salt scoping unanchored ids to an external identity (e.g. an
|
|
37
|
-
* asset/content id). By default no salt is used — ids derive from the element alone
|
|
38
|
-
* (type, name, occurrence-of-name), so any tool mints the same ids with no
|
|
39
|
-
* coordination. With a salt, only writers that know it agree — safe because
|
|
40
|
-
* existing ids are never overwritten (whoever stamps first wins).
|
|
41
|
-
*/
|
|
42
|
-
baseHash?: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type EnsureIdsResult = {
|
|
46
|
-
/** the salt the ids were derived with, when one was provided */
|
|
47
|
-
baseHash?: string;
|
|
48
|
-
/** how many elements received a new id (0 → the file was already fully stamped) */
|
|
49
|
-
assigned: number;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Idempotently inject `extras["needle-id"]` into every element of the stamped
|
|
54
|
-
* collections. Existing ids are never overwritten, so already-stamped files pass
|
|
55
|
-
* through unchanged and every id writer agrees with whoever stamped first.
|
|
56
|
-
*
|
|
57
|
-
* Mutates `json` in place.
|
|
58
|
-
*/
|
|
59
|
-
export function ensureIds(json: GltfJson, options?: EnsureIdsOptions): EnsureIdsResult {
|
|
60
|
-
const baseHash = options?.baseHash;
|
|
61
|
-
let assigned = 0;
|
|
62
|
-
for (const [type, collection] of STAMPED_COLLECTIONS) {
|
|
63
|
-
const elements = json[collection];
|
|
64
|
-
if (!Array.isArray(elements)) continue;
|
|
65
|
-
// occurrence-of-name: the id input is the element's position among same-named
|
|
66
|
-
// elements, NOT its collection index — uniquely named elements keep their ids
|
|
67
|
-
// across reorders and asset updates
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
1
|
+
import { computeElementId, idNameOf } from "./id.js";
|
|
2
|
+
|
|
3
|
+
/** The glTF `extras` key carrying the persistent id. */
|
|
4
|
+
export const NEEDLE_ID_KEY = "needle-id";
|
|
5
|
+
|
|
6
|
+
/** All root-collection element types that receive ids, with their glTF collection key. */
|
|
7
|
+
export const STAMPED_COLLECTIONS = [
|
|
8
|
+
["node", "nodes"],
|
|
9
|
+
["scene", "scenes"],
|
|
10
|
+
["animation", "animations"],
|
|
11
|
+
["camera", "cameras"],
|
|
12
|
+
["material", "materials"],
|
|
13
|
+
["mesh", "meshes"],
|
|
14
|
+
["skin", "skins"],
|
|
15
|
+
["texture", "textures"],
|
|
16
|
+
["image", "images"],
|
|
17
|
+
["sampler", "samplers"],
|
|
18
|
+
] as const satisfies ReadonlyArray<readonly [string, string]>;
|
|
19
|
+
|
|
20
|
+
export type StampedElementType = (typeof STAMPED_COLLECTIONS)[number][0];
|
|
21
|
+
|
|
22
|
+
/** Structural (duck-typed) view of a glTF root element — no glTF library dependency. */
|
|
23
|
+
export type GltfElement = {
|
|
24
|
+
name?: string;
|
|
25
|
+
extras?: Record<string, unknown>;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Structural view of a glTF JSON root. Collections are looked up by key. */
|
|
30
|
+
export type GltfJson = {
|
|
31
|
+
[collection: string]: unknown;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type EnsureIdsOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* Optional salt scoping unanchored ids to an external identity (e.g. an
|
|
37
|
+
* asset/content id). By default no salt is used — ids derive from the element alone
|
|
38
|
+
* (type, name, occurrence-of-name), so any tool mints the same ids with no
|
|
39
|
+
* coordination. With a salt, only writers that know it agree — safe because
|
|
40
|
+
* existing ids are never overwritten (whoever stamps first wins).
|
|
41
|
+
*/
|
|
42
|
+
baseHash?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type EnsureIdsResult = {
|
|
46
|
+
/** the salt the ids were derived with, when one was provided */
|
|
47
|
+
baseHash?: string;
|
|
48
|
+
/** how many elements received a new id (0 → the file was already fully stamped) */
|
|
49
|
+
assigned: number;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Idempotently inject `extras["needle-id"]` into every element of the stamped
|
|
54
|
+
* collections. Existing ids are never overwritten, so already-stamped files pass
|
|
55
|
+
* through unchanged and every id writer agrees with whoever stamped first.
|
|
56
|
+
*
|
|
57
|
+
* Mutates `json` in place.
|
|
58
|
+
*/
|
|
59
|
+
export function ensureIds(json: GltfJson, options?: EnsureIdsOptions): EnsureIdsResult {
|
|
60
|
+
const baseHash = options?.baseHash;
|
|
61
|
+
let assigned = 0;
|
|
62
|
+
for (const [type, collection] of STAMPED_COLLECTIONS) {
|
|
63
|
+
const elements = json[collection];
|
|
64
|
+
if (!Array.isArray(elements)) continue;
|
|
65
|
+
// occurrence-of-name: the id input is the element's position among same-named
|
|
66
|
+
// elements, NOT its collection index — uniquely named elements keep their ids
|
|
67
|
+
// across reorders and asset updates. Grouping is by the DERIVATION name
|
|
68
|
+
// (anchor marker stripped, normalized), matching occurrenceOfName.
|
|
69
|
+
const occurrences = new Map<string, number>();
|
|
70
|
+
for (let index = 0; index < elements.length; index++) {
|
|
71
|
+
const element = elements[index] as GltfElement;
|
|
72
|
+
if (element == null || typeof element !== "object") continue;
|
|
73
|
+
const name = typeof element.name === "string" ? element.name : "";
|
|
74
|
+
const group = idNameOf(name);
|
|
75
|
+
const occurrence = occurrences.get(group) ?? 0;
|
|
76
|
+
occurrences.set(group, occurrence + 1);
|
|
77
|
+
element.extras ??= {};
|
|
78
|
+
if (element.extras[NEEDLE_ID_KEY] === undefined) {
|
|
79
|
+
element.extras[NEEDLE_ID_KEY] = computeElementId(type, name, occurrence, baseHash);
|
|
80
|
+
assigned++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { baseHash, assigned };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Read an element's needle-id without computing anything. */
|
|
88
|
+
export function getElementId(element: GltfElement | null | undefined): string | null {
|
|
89
|
+
const id = element?.extras?.[NEEDLE_ID_KEY];
|
|
90
|
+
return typeof id === "string" ? id : null;
|
|
91
|
+
}
|
package/src/hash.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vendored SHA-1 so the package has zero runtime dependencies — it backs UUIDv5
|
|
3
|
-
* (RFC 4122). Correctness is pinned by golden tests against the `uuid` npm package.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** SHA-1 digest bytes of `data`. */
|
|
7
|
-
export function sha1(data: Uint8Array): Uint8Array {
|
|
8
|
-
const padded = padMessage(data);
|
|
9
|
-
const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
|
|
10
|
-
|
|
11
|
-
let h0 = 0x67452301, h1 = 0xefcdab89, h2 = 0x98badcfe, h3 = 0x10325476, h4 = 0xc3d2e1f0;
|
|
12
|
-
const w = new Uint32Array(80);
|
|
13
|
-
for (let off = 0; off < padded.length; off += 64) {
|
|
14
|
-
for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + i * 4);
|
|
15
|
-
for (let i = 16; i < 80; i++) {
|
|
16
|
-
const n = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
|
|
17
|
-
w[i] = (n << 1) | (n >>> 31);
|
|
18
|
-
}
|
|
19
|
-
let a = h0, b = h1, c = h2, d = h3, e = h4;
|
|
20
|
-
for (let i = 0; i < 80; i++) {
|
|
21
|
-
let f: number, k: number;
|
|
22
|
-
if (i < 20) { f = (b & c) | (~b & d); k = 0x5a827999; }
|
|
23
|
-
else if (i < 40) { f = b ^ c ^ d; k = 0x6ed9eba1; }
|
|
24
|
-
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8f1bbcdc; }
|
|
25
|
-
else { f = b ^ c ^ d; k = 0xca62c1d6; }
|
|
26
|
-
const t = ((((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0);
|
|
27
|
-
e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;
|
|
28
|
-
}
|
|
29
|
-
h0 = (h0 + a) | 0; h1 = (h1 + b) | 0; h2 = (h2 + c) | 0; h3 = (h3 + d) | 0; h4 = (h4 + e) | 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const out = new Uint8Array(20);
|
|
33
|
-
const odv = new DataView(out.buffer);
|
|
34
|
-
odv.setUint32(0, h0 >>> 0);
|
|
35
|
-
odv.setUint32(4, h1 >>> 0);
|
|
36
|
-
odv.setUint32(8, h2 >>> 0);
|
|
37
|
-
odv.setUint32(12, h3 >>> 0);
|
|
38
|
-
odv.setUint32(16, h4 >>> 0);
|
|
39
|
-
return out;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function toHex(bytes: Uint8Array): string {
|
|
43
|
-
let s = "";
|
|
44
|
-
for (let i = 0; i < bytes.length; i++) s += bytes[i].toString(16).padStart(2, "0");
|
|
45
|
-
return s;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** append 0x80, zero-pad to 56 mod 64, append big-endian 64-bit bit-length */
|
|
49
|
-
function padMessage(data: Uint8Array): Uint8Array {
|
|
50
|
-
const bitLen = data.length * 8;
|
|
51
|
-
const padded = new Uint8Array((((data.length + 8) >> 6) + 1) * 64);
|
|
52
|
-
padded.set(data);
|
|
53
|
-
padded[data.length] = 0x80;
|
|
54
|
-
const dv = new DataView(padded.buffer);
|
|
55
|
-
dv.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000));
|
|
56
|
-
dv.setUint32(padded.length - 4, bitLen >>> 0);
|
|
57
|
-
return padded;
|
|
58
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Vendored SHA-1 so the package has zero runtime dependencies — it backs UUIDv5
|
|
3
|
+
* (RFC 4122). Correctness is pinned by golden tests against the `uuid` npm package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** SHA-1 digest bytes of `data`. */
|
|
7
|
+
export function sha1(data: Uint8Array): Uint8Array {
|
|
8
|
+
const padded = padMessage(data);
|
|
9
|
+
const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
|
|
10
|
+
|
|
11
|
+
let h0 = 0x67452301, h1 = 0xefcdab89, h2 = 0x98badcfe, h3 = 0x10325476, h4 = 0xc3d2e1f0;
|
|
12
|
+
const w = new Uint32Array(80);
|
|
13
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
14
|
+
for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + i * 4);
|
|
15
|
+
for (let i = 16; i < 80; i++) {
|
|
16
|
+
const n = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
|
|
17
|
+
w[i] = (n << 1) | (n >>> 31);
|
|
18
|
+
}
|
|
19
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4;
|
|
20
|
+
for (let i = 0; i < 80; i++) {
|
|
21
|
+
let f: number, k: number;
|
|
22
|
+
if (i < 20) { f = (b & c) | (~b & d); k = 0x5a827999; }
|
|
23
|
+
else if (i < 40) { f = b ^ c ^ d; k = 0x6ed9eba1; }
|
|
24
|
+
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8f1bbcdc; }
|
|
25
|
+
else { f = b ^ c ^ d; k = 0xca62c1d6; }
|
|
26
|
+
const t = ((((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0);
|
|
27
|
+
e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t;
|
|
28
|
+
}
|
|
29
|
+
h0 = (h0 + a) | 0; h1 = (h1 + b) | 0; h2 = (h2 + c) | 0; h3 = (h3 + d) | 0; h4 = (h4 + e) | 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const out = new Uint8Array(20);
|
|
33
|
+
const odv = new DataView(out.buffer);
|
|
34
|
+
odv.setUint32(0, h0 >>> 0);
|
|
35
|
+
odv.setUint32(4, h1 >>> 0);
|
|
36
|
+
odv.setUint32(8, h2 >>> 0);
|
|
37
|
+
odv.setUint32(12, h3 >>> 0);
|
|
38
|
+
odv.setUint32(16, h4 >>> 0);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function toHex(bytes: Uint8Array): string {
|
|
43
|
+
let s = "";
|
|
44
|
+
for (let i = 0; i < bytes.length; i++) s += bytes[i].toString(16).padStart(2, "0");
|
|
45
|
+
return s;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** append 0x80, zero-pad to 56 mod 64, append big-endian 64-bit bit-length */
|
|
49
|
+
function padMessage(data: Uint8Array): Uint8Array {
|
|
50
|
+
const bitLen = data.length * 8;
|
|
51
|
+
const padded = new Uint8Array((((data.length + 8) >> 6) + 1) * 64);
|
|
52
|
+
padded.set(data);
|
|
53
|
+
padded[data.length] = 0x80;
|
|
54
|
+
const dv = new DataView(padded.buffer);
|
|
55
|
+
dv.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000));
|
|
56
|
+
dv.setUint32(padded.length - 4, bitLen >>> 0);
|
|
57
|
+
return padded;
|
|
58
|
+
}
|
package/src/id.ts
CHANGED
|
@@ -1,45 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { uuidv5 } from "./uuid.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
import { normalizeName, parseAnchor } from "./anchor.js";
|
|
2
|
+
import { uuidv5 } from "./uuid.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The name an id is derived FROM: the anchor marker removed (if any) and space/underscore
|
|
6
|
+
* runs collapsed.
|
|
7
|
+
*
|
|
8
|
+
* The marker is stripped because **ids are minted as if no anchor existed** — an anchor is
|
|
9
|
+
* a LABEL, not an identity, and the two must be able to change independently. Both halves
|
|
10
|
+
* of that are load-bearing:
|
|
11
|
+
* - stripping the marker means ADDING or REMOVING `#needle` leaves the id untouched (under
|
|
12
|
+
* the previous rule, anchoring an object CHANGED its id — so the very act recommended for
|
|
13
|
+
* making something stable orphaned every edit already addressed to it);
|
|
14
|
+
* - {@link occurrenceOfName} groups on this same value, so anchoring ONE of two `Door`s
|
|
15
|
+
* does not shift the OTHER one's occurrence.
|
|
16
|
+
*
|
|
17
|
+
* Note what is decoupled: the id is independent of the anchor MARKER, not of the NAME.
|
|
18
|
+
* Renaming `Door #needle` → `Entrance #needle` still changes a DERIVED id; a STAMPED id is
|
|
19
|
+
* unaffected, since existing ids are never overwritten.
|
|
20
|
+
*/
|
|
21
|
+
export function idNameOf(name: string | null | undefined): string {
|
|
22
|
+
if (typeof name !== "string") return "";
|
|
23
|
+
// an anchor's value is already marker-stripped and normalized
|
|
24
|
+
return parseAnchor(name)?.value ?? normalizeName(name);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The deterministic needle-id of a glTF element:
|
|
29
|
+
* `uuidv5("<type>-<idName>-<occurrence>[-<baseHash>]")`, where `idName` is the name with
|
|
30
|
+
* any anchor marker removed ({@link idNameOf}) and `occurrence` is the element's position
|
|
31
|
+
* among same-named elements of its type. Uniquely named elements are therefore fully
|
|
32
|
+
* reorder-stable across file versions; duplicates shift only when reordered within their
|
|
33
|
+
* own name group; unnamed elements are positional among the unnamed.
|
|
34
|
+
*
|
|
35
|
+
* `baseHash` is an optional consumer-supplied salt (e.g. an asset/content id) that scopes
|
|
36
|
+
* ids to an external identity. Omitted by default, so any tool stamping the same element
|
|
37
|
+
* mints the same id with no coordination.
|
|
38
|
+
*
|
|
39
|
+
* Ids are persisted in `extras["needle-id"]` and never overwritten once present —
|
|
40
|
+
* whoever stamps first wins; this rule only governs never-stamped elements.
|
|
41
|
+
*/
|
|
42
|
+
export function computeElementId(type: string, name: string | null | undefined, occurrence: number, baseHash?: string): string {
|
|
43
|
+
return uuidv5(`${type}-${idNameOf(name)}-${occurrence}${baseHash ? `-${baseHash}` : ""}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The occurrence of the element at `index` among same-named elements of its collection —
|
|
48
|
+
* the positional input of {@link computeElementId}. Exposed so consumers computing ids
|
|
49
|
+
* for individual elements (instead of stamping whole files) use the identical rule.
|
|
50
|
+
*
|
|
51
|
+
* Grouping is by {@link idNameOf}, so an anchored element counts in the same group as its
|
|
52
|
+
* unanchored namesakes.
|
|
53
|
+
*/
|
|
54
|
+
export function occurrenceOfName(elements: ReadonlyArray<{ name?: unknown } | null | undefined>, index: number): number {
|
|
55
|
+
const name = idNameOf(elements[index]?.name as string | null | undefined);
|
|
56
|
+
let occurrence = 0;
|
|
57
|
+
for (let i = 0; i < index; i++) {
|
|
58
|
+
if (idNameOf(elements[i]?.name as string | null | undefined) === name) occurrence++;
|
|
59
|
+
}
|
|
60
|
+
return occurrence;
|
|
61
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
-
export { ANCHOR_MARKER, ANCHOR_MARKERS, parseAnchor, parseAnchorValue, type ParsedAnchor } from "./anchor.js";
|
|
2
|
-
export { computeElementId, occurrenceOfName } from "./id.js";
|
|
3
|
-
export {
|
|
4
|
-
ensureIds,
|
|
5
|
-
getElementId,
|
|
6
|
-
NEEDLE_ID_KEY,
|
|
7
|
-
STAMPED_COLLECTIONS,
|
|
8
|
-
type EnsureIdsOptions,
|
|
9
|
-
type EnsureIdsResult,
|
|
10
|
-
type GltfElement,
|
|
11
|
-
type GltfJson,
|
|
12
|
-
type StampedElementType,
|
|
13
|
-
} from "./gltf.js";
|
|
14
|
-
export {
|
|
15
|
-
collectionKeyForType,
|
|
16
|
-
createGltfElementAddress,
|
|
17
|
-
createObjectAddress,
|
|
18
|
-
findPath,
|
|
19
|
-
type GraphNode,
|
|
20
|
-
type NeedleAddress,
|
|
21
|
-
} from "./address.js";
|
|
22
|
-
export {
|
|
23
|
-
reconcileInGraph,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
type
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
export { ANCHOR_MARKER, ANCHOR_MARKERS, parseAnchor, parseAnchorValue, type ParsedAnchor } from "./anchor.js";
|
|
2
|
+
export { computeElementId, occurrenceOfName } from "./id.js";
|
|
3
|
+
export {
|
|
4
|
+
ensureIds,
|
|
5
|
+
getElementId,
|
|
6
|
+
NEEDLE_ID_KEY,
|
|
7
|
+
STAMPED_COLLECTIONS,
|
|
8
|
+
type EnsureIdsOptions,
|
|
9
|
+
type EnsureIdsResult,
|
|
10
|
+
type GltfElement,
|
|
11
|
+
type GltfJson,
|
|
12
|
+
type StampedElementType,
|
|
13
|
+
} from "./gltf.js";
|
|
14
|
+
export {
|
|
15
|
+
collectionKeyForType,
|
|
16
|
+
createGltfElementAddress,
|
|
17
|
+
createObjectAddress,
|
|
18
|
+
findPath,
|
|
19
|
+
type GraphNode,
|
|
20
|
+
type NeedleAddress,
|
|
21
|
+
} from "./address.js";
|
|
22
|
+
export {
|
|
23
|
+
reconcileInGraph,
|
|
24
|
+
resolveInCollection,
|
|
25
|
+
resolveInGltf,
|
|
26
|
+
resolveInGraph,
|
|
27
|
+
type AddressableElement,
|
|
28
|
+
type GltfResolveResult,
|
|
29
|
+
type ReconcileCandidate,
|
|
30
|
+
type ResolutionIssue,
|
|
31
|
+
type ResolutionRung,
|
|
32
|
+
type ResolveOptions,
|
|
33
|
+
} from "./resolve.js";
|
|
34
|
+
export { randomUuid, uuidv5, UUID_NAMESPACE_URL } from "./uuid.js";
|