{"version":3,"file":"schema-reflect.cjs","sources":["../../src/core/schema-reflect.ts"],"sourcesContent":["// -----------------------------------------------------------------------------\n// Schema-native identity. The decoder assigns every instance a stable integer\n// `refId` (own, non-enumerable `Symbol.for(\"$refId\")` property) and exposes a\n// field-name → field-index map on `constructor[Symbol.metadata]`. These two\n// integers — refId and field index — are the SAME identity the wire protocol\n// and any C / C# port use, so callers key their runtime state on them rather\n// than on JS object identity (WeakMap) or field-name strings. The instance\n// object and the field-name string never reach a hot path; they're resolved to\n// `(refId, fieldId)` once at the API boundary.\n//\n// Reflection only — no prediction state lives here. Both `input/` and\n// `predict/` read it, which is why it sits under `core/`.\n// -----------------------------------------------------------------------------\n\n// `$refId`/`$values` are imported (not re-declared) so they stay identical to the\n// decoder's symbols; schema shares them via the process-wide Symbol Registry\n// (`Symbol.for`), so the import is one value even across duplicate installs.\n// `Symbol.metadata` is the TC39 decorator-metadata symbol, not a schema export.\nimport { $refId, $values } from \"@colyseus/schema\";\nimport type { MapSchema, ArraySchema, SetSchema } from \"@colyseus/schema\";\n\n/** Keys of T whose value is a Colyseus collection (Map/Array/Set schema). */\nexport type CollectionKeys<T> = {\n    [K in keyof T]-?: T[K] extends MapSchema<any> | ArraySchema<any> | SetSchema<any> ? K : never;\n}[keyof T] & string;\n\n/** Element type of a Colyseus collection. */\nexport type ChildOf<C> =\n    C extends MapSchema<infer V> ? V :\n    C extends ArraySchema<infer V> ? V :\n    C extends SetSchema<infer V> ? V :\n    never;\n\nconst $METADATA: symbol = (Symbol as { metadata?: symbol }).metadata ?? Symbol.for(\"Symbol.metadata\");\n/** Schema's own SoA: a dense array of a decoded instance's field values, indexed\n *  by field index. Reading/writing it bypasses the per-field accessor and the\n *  megamorphic dynamic-key path — reckon's scratch refill + extract use it. */\nexport const $VALUES = $values;\n\n/** Stable integer id the decoder assigned this instance, or undefined if it\n *  hasn't been decoded yet (attach/track called too early — see the Predictor\n *  file header). */\nexport function refIdOf(instance: object): number | undefined {\n    return (instance as Record<symbol, number | undefined>)[$refId];\n}\n\n/** The schema metadata object: maps `name -> index` (number) and\n *  `index -> { name, index, type }` (MetadataField). Undefined for non-schema\n *  objects (e.g. plain test fixtures). */\nexport type SchemaMetadata = Record<string | number, unknown>;\nexport function metadataOf(instance: object): SchemaMetadata | undefined {\n    return (instance.constructor as unknown as Record<symbol, SchemaMetadata | undefined>)[$METADATA];\n}\n\n/**\n * Field's declaration index from the schema's metadata, or -1 if unknown.\n * `metadata[name]` is the index. Only used on the COLD attach path (to stamp a\n * slot's SLOT_FIELD and seed reckon sim fields) — the hot `value()` read keys\n * its slot map by field NAME, so it never resolves an index per frame.\n */\nexport function fieldIndexOf(instance: object, field: string): number {\n    const idx = metadataOf(instance)?.[field];\n    return typeof idx === \"number\" ? idx : -1;\n}\n\n\n/** PRIMITIVE (scalar) field names in declaration order, read from the schema\n *  metadata. Empty for non-schema objects. Field indices are dense from 0, so\n *  walk until the first gap.\n *\n *  Only number / string / boolean (and the other primitive encodings — int8,\n *  float32, …) are returned: in the metadata a primitive's `type` is a STRING\n *  (\"number\", \"string\", …) whereas a collection (Map/Array/Set) or a nested\n *  Schema carries an OBJECT `type`. The reckon snapshot uses this to clone the\n *  SCALAR state a `step` reads — including string/enum discriminators (`kind`)\n *  and booleans (`grounded`), which steps routinely branch on — while skipping\n *  reference-typed fields. Those are structural, not forward-simulated, and\n *  shallow-copying their reference into a scratch that `step` mutates could\n *  corrupt the live tree; for the rare step that needs one, pass an explicit\n *  `snapshot`. (Schema fields live behind getters over `$values`, so a plain\n *  `{...instance}` spread copies nothing — hence the metadata walk.) */\nexport function scalarFieldNamesOf(instance: object): string[] {\n    const meta = metadataOf(instance);\n    if (!meta) return [];\n    const names: string[] = [];\n    for (let i = 0; ; i++) {\n        const f = meta[i] as { name?: string; type?: unknown } | undefined;\n        if (!f || typeof f.name !== \"string\") break;\n        if (typeof f.type === \"string\") names.push(f.name); // skip collections / child schemas (object type)\n    }\n    return names;\n}\n\n/** Scalar type strings that are NOT numbers on the decoded instance (\"string\",\n *  \"boolean\", and the bigint encodings, which decode to BigInt). Everything\n *  else — \"number\", the sized ints/floats, custom numeric encodings — is\n *  numeric and eligible for pose smoothing. */\nconst NON_NUMERIC_SCALAR: Record<string, true> = {\n    string: true, boolean: true, bigint64: true, biguint64: true,\n};\n\n/**\n * One metadata walk returning BOTH scalar sets a world binding needs:\n * `fields` — every primitive field, in declaration order (the adopt set,\n * same rule as {@link scalarFieldNamesOf}); `numeric` — the subset whose\n * decoded value is a number (the pose/smoothing set). Both empty for\n * non-schema objects.\n */\nexport function scalarFieldsOf(instance: object): { fields: string[]; numeric: string[] } {\n    const meta = metadataOf(instance);\n    const fields: string[] = [];\n    const numeric: string[] = [];\n    if (meta) {\n        for (let i = 0; ; i++) {\n            const f = meta[i] as { name?: string; type?: unknown } | undefined;\n            if (!f || typeof f.name !== \"string\") break;\n            if (typeof f.type !== \"string\") continue; // skip collections / child schemas\n            fields.push(f.name);\n            if (NON_NUMERIC_SCALAR[f.type] === undefined) numeric.push(f.name);\n        }\n    }\n    return { fields, numeric };\n}\n\n// -----------------------------------------------------------------------------\n// Wire quantizers — \"what value would the wire deliver for this float64?\"\n// Backs the reconciler's wire-precision-aware reconcile: a prediction is\n// indistinguishable from the decoded truth iff `quantizer(predicted) === truth`,\n// so lossy wire types (float32, auto `number`) stop injecting rounding noise\n// into the rollback restore point (the noise flips knife-edge sim branches).\n// -----------------------------------------------------------------------------\n\nconst identityQuantizer = (v: number) => v;\n\n/**\n * Mirror of the codec's dynamic `number` encoding (`encode.number` in\n * @colyseus/schema): NaN encodes as 0, ±Infinity as ±MAX_SAFE_INTEGER,\n * non-int32 values ride float32 when the ABSOLUTE precision loss is < 1e-4\n * (else float64 — exact), and int32-range integers ride exact int encodings.\n * Must track the codec's rule exactly — including the `(v | 0)` integer test\n * and the abs-of-abs loss check — or the compare misclassifies.\n */\nfunction quantizeAutoNumber(v: number): number {\n    if (Number.isNaN(v)) return 0;\n    if (!Number.isFinite(v)) return v > 0 ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;\n    if (v !== (v | 0)) {\n        if (Math.abs(v) <= 3.4028235e+38) {\n            const f = Math.fround(v);\n            if (Math.abs(Math.abs(f) - Math.abs(v)) < 1e-4) return f;\n        }\n    }\n    return v;\n}\n\n/**\n * The wire quantizer for one declared field: maps a predicted float64 to the\n * EXACT value the wire would deliver for it. `\"float32\"` → `Math.fround`;\n * `\"number\"` → the codec's dynamic rule ({@link quantizeAutoNumber}); every\n * type the wire round-trips exactly (ints, float64, boolean) — and any type\n * this doesn't model (e.g. `quantized`) — gets the identity, which degrades to\n * a bit-exact compare: conservative, never worse than always-adopting.\n * Identity for non-schema instances (no metadata — plain test fixtures).\n */\nexport function wireQuantizerOf(instance: object, field: string): (v: number) => number {\n    const meta = metadataOf(instance);\n    const idx = meta?.[field];\n    const decl = typeof idx === \"number\" ? (meta![idx] as { type?: unknown } | undefined) : undefined;\n    switch (decl?.type) {\n        case \"float32\": return Math.fround;\n        case \"number\": return quantizeAutoNumber;\n        default: return identityQuantizer;\n    }\n}\n\n/**\n * Build a snapshot fn that clones `fieldNames` from a live instance into a fresh\n * plain object. Used by reckon's GENERIC advance path — schema versions whose\n * decoded instances expose values as own properties / accessors rather than a\n * dense `$values` SoA (e.g. esbuild's class-field transform in the real browser,\n * where the `$values` fast path is unavailable and this is the path production\n * actually runs).\n *\n * Why not a plain `for` loop? A single `o[names[i]] = src[names[i]]` store site\n * sees every field name across one call, so V8 demotes it to\n * `KeyedStoreIC_Megamorphic` — the profiler put ~33% of reckon time there.\n * Unrolling gives each field its OWN store site; with a fixed field layout each\n * site sees exactly one key and stays MONOMORPHIC (no eval; ~2.4× faster\n * snapshot in isolation, ~+19% on the full reckon read). Names are captured as\n * locals so the `!== undefined` guard is a predictable branch. Field sets wider\n * than the unroll fall back to the megamorphic loop (rare — most schemas have\n * < 16 numeric+scalar fields).\n */\nconst SNAPSHOT_UNROLL_WIDTH = 16;\nexport function makeUnrolledSnapshot(fieldNames: readonly string[]): (e: any) => any {\n    if (fieldNames.length > SNAPSHOT_UNROLL_WIDTH) {\n        const names = fieldNames;\n        return (e: Record<string, unknown>) => {\n            const o: Record<string, unknown> = {};\n            for (let i = 0; i < names.length; i++) o[names[i]] = e[names[i]];\n            return o;\n        };\n    }\n    const n0 = fieldNames[0], n1 = fieldNames[1], n2 = fieldNames[2], n3 = fieldNames[3],\n        n4 = fieldNames[4], n5 = fieldNames[5], n6 = fieldNames[6], n7 = fieldNames[7],\n        n8 = fieldNames[8], n9 = fieldNames[9], n10 = fieldNames[10], n11 = fieldNames[11],\n        n12 = fieldNames[12], n13 = fieldNames[13], n14 = fieldNames[14], n15 = fieldNames[15];\n    return (e: Record<string, unknown>) => {\n        const o: Record<string, unknown> = {};\n        if (n0 !== undefined) o[n0] = e[n0]; if (n1 !== undefined) o[n1] = e[n1];\n        if (n2 !== undefined) o[n2] = e[n2]; if (n3 !== undefined) o[n3] = e[n3];\n        if (n4 !== undefined) o[n4] = e[n4]; if (n5 !== undefined) o[n5] = e[n5];\n        if (n6 !== undefined) o[n6] = e[n6]; if (n7 !== undefined) o[n7] = e[n7];\n        if (n8 !== undefined) o[n8] = e[n8]; if (n9 !== undefined) o[n9] = e[n9];\n        if (n10 !== undefined) o[n10] = e[n10]; if (n11 !== undefined) o[n11] = e[n11];\n        if (n12 !== undefined) o[n12] = e[n12]; if (n13 !== undefined) o[n13] = e[n13];\n        if (n14 !== undefined) o[n14] = e[n14]; if (n15 !== undefined) o[n15] = e[n15];\n        return o;\n    };\n}\n"],"names":["$values","$refId"],"mappings":"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"}