normalizeNodeIds ​

Contract 2.0.0 · package sha256 a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e · generated from configurator-runtime.d.ts by services/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.

Function · import { normalizeNodeIds } from '@holostep-configurators/runtime-client'; · stable · since 2.0.0 · section 22. Pure helpers (root of the package; run in Node and the fake)

Repairs old saves: mints missing ids, re-mints duplicates, remaps defaultNodeId, preset node ids, globalMaterialList[].globalMaterial and connections, and rewrites legacy nodeId:meshName connector ids, on every node type (preset node ids name GroupNodes). A stored id stays with its first occurrence in depth-first document order, and each later occurrence gets a new id from mint; every stored id is reserved first, and a mint result that is empty or already used is replaced by crypto.randomUUID(). A parent whose defaultNodeId names a repeated id that none of its direct children kept follows its first re-minted child with that id; every other reference to a repeated id (preset node ids, globalMaterialList, connections) keeps naming the first occurrence. Idempotent. The input type is loose on purpose: it is the document whose ids this function exists to repair, so no cast is needed. Every other field is copied unchanged. Sync, pure. The returned doc is your document from then on: keep it in your store and save from it, so the minted ids persist. A repair you don't keep is minted anew on the next load, and placement, connections and presets saved against it go stale (spec section 11.1).

Declaration ​

ts
export declare function normalizeNodeIds<D extends LegacyDocument>(doc: D, options?: {
  mint?: (node: LegacyNode) => string;
}): {
  doc: Omit<D, 'root'> & {
    root: ConfiguratorNode[];
  };
  repairs: ReadonlyArray<{
    from: string | null;
    to: string;
    parent: string | null;
  }>;
};

Example ​

Imports: values (has, validate, the Holo*Error classes, the pure helpers) from @holostep-configurators/runtime-client, types from @holostep-configurators/runtime-client/contract, /math and /testing names from those entries; boot from the runtime module the root's loadRuntimeAtFloor resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (examples/declaration/22-normalizeNodeIds.ts):

ts
import { normalizeNodeIds } from '@holostep-configurators/runtime-client';
import type { IConfigurator, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare function loadSavedDesign(id: string): Promise<IConfigurator>;
declare function adoptDesign(doc: IConfigurator): void;      // your store's document from now on
// no cast: the input type is loose
const { doc, repairs } = normalizeNodeIds(await loadSavedDesign('d-42'));
if (repairs.length) console.info(`repaired ${repairs.length} node ids`);
adoptDesign(doc);                                           // later saves keep the minted ids
// a load: the floor of history
await session.setSceneModel(doc, { origin: 'load', undo: 'rebase' });

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: normalizeNodeIds names no product
2. A precedent pass: DOM Document.getElementById (unique ids) (row X3)
3. Two apps, or labs pass: 4 apps (ledger: app-04c2774145, app-37356bf09f, app-4762caacdc, app-f9a32142f1)
4. The cheaper fix first decided per need in Appendix B: rows X3 at tier stable (a member only where no rule, data, asset or app code serves the need)