@series-inc/rundot-syncplay 5.23.0-beta.7

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.
Files changed (264) hide show
  1. package/README.md +610 -0
  2. package/dist/animation.d.ts +119 -0
  3. package/dist/animation.js +488 -0
  4. package/dist/asset-cooking.d.ts +82 -0
  5. package/dist/asset-cooking.js +364 -0
  6. package/dist/authority-room.d.ts +217 -0
  7. package/dist/authority-room.js +701 -0
  8. package/dist/bot-documents.d.ts +115 -0
  9. package/dist/bot-documents.js +549 -0
  10. package/dist/bot-primitives.d.ts +152 -0
  11. package/dist/bot-primitives.js +472 -0
  12. package/dist/browser.d.ts +57 -0
  13. package/dist/browser.js +53 -0
  14. package/dist/canonical.d.ts +12 -0
  15. package/dist/canonical.js +288 -0
  16. package/dist/certification.d.ts +455 -0
  17. package/dist/certification.js +1855 -0
  18. package/dist/chaos-certification.d.ts +56 -0
  19. package/dist/chaos-certification.js +1397 -0
  20. package/dist/cjs/animation.js +495 -0
  21. package/dist/cjs/asset-cooking.js +372 -0
  22. package/dist/cjs/authority-room.js +704 -0
  23. package/dist/cjs/bot-documents.js +556 -0
  24. package/dist/cjs/bot-primitives.js +482 -0
  25. package/dist/cjs/browser.js +96 -0
  26. package/dist/cjs/canonical.js +299 -0
  27. package/dist/cjs/certification.js +1860 -0
  28. package/dist/cjs/chaos-certification.js +1402 -0
  29. package/dist/cjs/collections.js +36 -0
  30. package/dist/cjs/collision.js +96 -0
  31. package/dist/cjs/commands.js +987 -0
  32. package/dist/cjs/config-bundle.js +491 -0
  33. package/dist/cjs/creator.js +30 -0
  34. package/dist/cjs/deterministic-primitives.js +134 -0
  35. package/dist/cjs/deterministic-session.js +1410 -0
  36. package/dist/cjs/ecs-lite.js +155 -0
  37. package/dist/cjs/effects-adapter.js +97 -0
  38. package/dist/cjs/engine-complete.js +2297 -0
  39. package/dist/cjs/engine-core-proof.js +70 -0
  40. package/dist/cjs/engine-parity-matrix.js +874 -0
  41. package/dist/cjs/engine-parity.js +3183 -0
  42. package/dist/cjs/engine-runtime.js +3141 -0
  43. package/dist/cjs/errors.js +22 -0
  44. package/dist/cjs/events.js +247 -0
  45. package/dist/cjs/examples/counter-duel.js +157 -0
  46. package/dist/cjs/examples/fixed-arena.js +368 -0
  47. package/dist/cjs/index.js +229 -0
  48. package/dist/cjs/input-authority.js +150 -0
  49. package/dist/cjs/input-button.js +256 -0
  50. package/dist/cjs/input-timeline.js +418 -0
  51. package/dist/cjs/instant-replay.js +66 -0
  52. package/dist/cjs/lag-compensation3d.js +837 -0
  53. package/dist/cjs/local-authority-server-runner.js +1121 -0
  54. package/dist/cjs/local-authority-wire.js +1497 -0
  55. package/dist/cjs/local-authority.js +817 -0
  56. package/dist/cjs/local-ws-adapter.js +150 -0
  57. package/dist/cjs/match-log.js +61 -0
  58. package/dist/cjs/math.js +157 -0
  59. package/dist/cjs/mock-network-adapter.js +26 -0
  60. package/dist/cjs/movement.js +703 -0
  61. package/dist/cjs/movement3d.js +1246 -0
  62. package/dist/cjs/multiclient-authority.js +308 -0
  63. package/dist/cjs/multiplayer-service.js +434 -0
  64. package/dist/cjs/navigation.js +347 -0
  65. package/dist/cjs/navmesh.js +1437 -0
  66. package/dist/cjs/networked-client.js +691 -0
  67. package/dist/cjs/networked-input-authority.js +443 -0
  68. package/dist/cjs/node.js +47 -0
  69. package/dist/cjs/number-lab.js +78 -0
  70. package/dist/cjs/offline-session.js +370 -0
  71. package/dist/cjs/package.json +1 -0
  72. package/dist/cjs/pause-resume.js +43 -0
  73. package/dist/cjs/physics-cert.js +426 -0
  74. package/dist/cjs/physics2d.js +751 -0
  75. package/dist/cjs/physics3d.js +1996 -0
  76. package/dist/cjs/prediction-culling.js +203 -0
  77. package/dist/cjs/protocol-lab.js +1299 -0
  78. package/dist/cjs/r3f-render-adapter.js +209 -0
  79. package/dist/cjs/random.js +59 -0
  80. package/dist/cjs/replay-bundle.js +1598 -0
  81. package/dist/cjs/replay-inspect.js +236 -0
  82. package/dist/cjs/replay-scoring.js +68 -0
  83. package/dist/cjs/replay.js +356 -0
  84. package/dist/cjs/reports.js +181 -0
  85. package/dist/cjs/rollback-history.js +251 -0
  86. package/dist/cjs/runtime-guards.js +111 -0
  87. package/dist/cjs/sample-lab.js +145 -0
  88. package/dist/cjs/sample-scenes.js +752 -0
  89. package/dist/cjs/schema-artifacts.js +359 -0
  90. package/dist/cjs/schema-codegen.js +544 -0
  91. package/dist/cjs/schema-dsl-binary.js +655 -0
  92. package/dist/cjs/schema.js +319 -0
  93. package/dist/cjs/sdk-offline.js +99 -0
  94. package/dist/cjs/session-promotion.js +74 -0
  95. package/dist/cjs/session-snapshot.js +265 -0
  96. package/dist/cjs/session-wire.js +216 -0
  97. package/dist/cjs/signals.js +103 -0
  98. package/dist/cjs/sparse-set.js +169 -0
  99. package/dist/cjs/synctest.js +136 -0
  100. package/dist/cjs/system-lifecycle.js +286 -0
  101. package/dist/cjs/timers.js +61 -0
  102. package/dist/cjs/tools/static-checker.js +1468 -0
  103. package/dist/cjs/types.js +2 -0
  104. package/dist/cjs/venus-mp-budget.js +125 -0
  105. package/dist/cjs/ws-frame.js +255 -0
  106. package/dist/cli.d.ts +2 -0
  107. package/dist/cli.js +11379 -0
  108. package/dist/collections.d.ts +9 -0
  109. package/dist/collections.js +30 -0
  110. package/dist/collision.d.ts +25 -0
  111. package/dist/collision.js +85 -0
  112. package/dist/commands.d.ts +137 -0
  113. package/dist/commands.js +977 -0
  114. package/dist/config-bundle.d.ts +89 -0
  115. package/dist/config-bundle.js +483 -0
  116. package/dist/creator.d.ts +20 -0
  117. package/dist/creator.js +11 -0
  118. package/dist/deterministic-primitives.d.ts +59 -0
  119. package/dist/deterministic-primitives.js +126 -0
  120. package/dist/deterministic-session.d.ts +197 -0
  121. package/dist/deterministic-session.js +1401 -0
  122. package/dist/ecs-lite.d.ts +26 -0
  123. package/dist/ecs-lite.js +152 -0
  124. package/dist/effects-adapter.d.ts +21 -0
  125. package/dist/effects-adapter.js +94 -0
  126. package/dist/engine-complete.d.ts +45 -0
  127. package/dist/engine-complete.js +2292 -0
  128. package/dist/engine-core-proof.d.ts +26 -0
  129. package/dist/engine-core-proof.js +66 -0
  130. package/dist/engine-parity-matrix.d.ts +300 -0
  131. package/dist/engine-parity-matrix.js +869 -0
  132. package/dist/engine-parity.d.ts +74 -0
  133. package/dist/engine-parity.js +3178 -0
  134. package/dist/engine-runtime.d.ts +351 -0
  135. package/dist/engine-runtime.js +3123 -0
  136. package/dist/errors.d.ts +11 -0
  137. package/dist/errors.js +17 -0
  138. package/dist/events.d.ts +45 -0
  139. package/dist/events.js +242 -0
  140. package/dist/examples/counter-duel.d.ts +43 -0
  141. package/dist/examples/counter-duel.js +150 -0
  142. package/dist/examples/fixed-arena.d.ts +110 -0
  143. package/dist/examples/fixed-arena.js +356 -0
  144. package/dist/index.d.ts +101 -0
  145. package/dist/index.js +66 -0
  146. package/dist/input-authority.d.ts +82 -0
  147. package/dist/input-authority.js +147 -0
  148. package/dist/input-button.d.ts +36 -0
  149. package/dist/input-button.js +249 -0
  150. package/dist/input-timeline.d.ts +65 -0
  151. package/dist/input-timeline.js +412 -0
  152. package/dist/instant-replay.d.ts +59 -0
  153. package/dist/instant-replay.js +62 -0
  154. package/dist/lag-compensation3d.d.ts +146 -0
  155. package/dist/lag-compensation3d.js +827 -0
  156. package/dist/local-authority-server-runner.d.ts +79 -0
  157. package/dist/local-authority-server-runner.js +1118 -0
  158. package/dist/local-authority-wire.d.ts +142 -0
  159. package/dist/local-authority-wire.js +1477 -0
  160. package/dist/local-authority.d.ts +160 -0
  161. package/dist/local-authority.js +810 -0
  162. package/dist/local-ws-adapter.d.ts +14 -0
  163. package/dist/local-ws-adapter.js +146 -0
  164. package/dist/match-log.d.ts +74 -0
  165. package/dist/match-log.js +57 -0
  166. package/dist/math.d.ts +41 -0
  167. package/dist/math.js +154 -0
  168. package/dist/mock-network-adapter.d.ts +13 -0
  169. package/dist/mock-network-adapter.js +23 -0
  170. package/dist/movement.d.ts +155 -0
  171. package/dist/movement.js +694 -0
  172. package/dist/movement3d.d.ts +195 -0
  173. package/dist/movement3d.js +1238 -0
  174. package/dist/multiclient-authority.d.ts +30 -0
  175. package/dist/multiclient-authority.js +305 -0
  176. package/dist/multiplayer-service.d.ts +153 -0
  177. package/dist/multiplayer-service.js +429 -0
  178. package/dist/navigation.d.ts +91 -0
  179. package/dist/navigation.js +338 -0
  180. package/dist/navmesh.d.ts +236 -0
  181. package/dist/navmesh.js +1423 -0
  182. package/dist/networked-client.d.ts +124 -0
  183. package/dist/networked-client.js +688 -0
  184. package/dist/networked-input-authority.d.ts +51 -0
  185. package/dist/networked-input-authority.js +439 -0
  186. package/dist/node.d.ts +22 -0
  187. package/dist/node.js +19 -0
  188. package/dist/number-lab.d.ts +15 -0
  189. package/dist/number-lab.js +75 -0
  190. package/dist/offline-session.d.ts +2 -0
  191. package/dist/offline-session.js +367 -0
  192. package/dist/pause-resume.d.ts +48 -0
  193. package/dist/pause-resume.js +40 -0
  194. package/dist/physics-cert.d.ts +40 -0
  195. package/dist/physics-cert.js +389 -0
  196. package/dist/physics2d.d.ts +142 -0
  197. package/dist/physics2d.js +741 -0
  198. package/dist/physics3d.d.ts +285 -0
  199. package/dist/physics3d.js +1979 -0
  200. package/dist/prediction-culling.d.ts +34 -0
  201. package/dist/prediction-culling.js +200 -0
  202. package/dist/protocol-lab.d.ts +115 -0
  203. package/dist/protocol-lab.js +1295 -0
  204. package/dist/r3f-render-adapter.d.ts +61 -0
  205. package/dist/r3f-render-adapter.js +203 -0
  206. package/dist/random.d.ts +13 -0
  207. package/dist/random.js +55 -0
  208. package/dist/replay-bundle.d.ts +150 -0
  209. package/dist/replay-bundle.js +1586 -0
  210. package/dist/replay-inspect.d.ts +60 -0
  211. package/dist/replay-inspect.js +228 -0
  212. package/dist/replay-scoring.d.ts +28 -0
  213. package/dist/replay-scoring.js +65 -0
  214. package/dist/replay.d.ts +17 -0
  215. package/dist/replay.js +351 -0
  216. package/dist/reports.d.ts +176 -0
  217. package/dist/reports.js +174 -0
  218. package/dist/rollback-history.d.ts +46 -0
  219. package/dist/rollback-history.js +247 -0
  220. package/dist/runtime-guards.d.ts +1 -0
  221. package/dist/runtime-guards.js +108 -0
  222. package/dist/sample-lab.d.ts +32 -0
  223. package/dist/sample-lab.js +134 -0
  224. package/dist/sample-scenes.d.ts +86 -0
  225. package/dist/sample-scenes.js +745 -0
  226. package/dist/schema-artifacts.d.ts +49 -0
  227. package/dist/schema-artifacts.js +356 -0
  228. package/dist/schema-codegen.d.ts +21 -0
  229. package/dist/schema-codegen.js +538 -0
  230. package/dist/schema-dsl-binary.d.ts +75 -0
  231. package/dist/schema-dsl-binary.js +648 -0
  232. package/dist/schema.d.ts +87 -0
  233. package/dist/schema.js +315 -0
  234. package/dist/sdk-offline.d.ts +34 -0
  235. package/dist/sdk-offline.js +96 -0
  236. package/dist/session-promotion.d.ts +26 -0
  237. package/dist/session-promotion.js +70 -0
  238. package/dist/session-snapshot.d.ts +95 -0
  239. package/dist/session-snapshot.js +255 -0
  240. package/dist/session-wire.d.ts +89 -0
  241. package/dist/session-wire.js +211 -0
  242. package/dist/signals.d.ts +31 -0
  243. package/dist/signals.js +99 -0
  244. package/dist/sparse-set.d.ts +34 -0
  245. package/dist/sparse-set.js +165 -0
  246. package/dist/synctest.d.ts +31 -0
  247. package/dist/synctest.js +133 -0
  248. package/dist/system-lifecycle.d.ts +72 -0
  249. package/dist/system-lifecycle.js +281 -0
  250. package/dist/timers.d.ts +17 -0
  251. package/dist/timers.js +53 -0
  252. package/dist/tools/index.d.ts +4 -0
  253. package/dist/tools/index.js +4 -0
  254. package/dist/tools/static-checker.d.ts +31 -0
  255. package/dist/tools/static-checker.js +1462 -0
  256. package/dist/tools/vite-plugin.d.ts +69 -0
  257. package/dist/tools/vite-plugin.js +203 -0
  258. package/dist/types.d.ts +148 -0
  259. package/dist/types.js +1 -0
  260. package/dist/venus-mp-budget.d.ts +29 -0
  261. package/dist/venus-mp-budget.js +122 -0
  262. package/dist/ws-frame.d.ts +49 -0
  263. package/dist/ws-frame.js +247 -0
  264. package/package.json +169 -0
@@ -0,0 +1,544 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSchemaTypes = generateSchemaTypes;
4
+ exports.createDeterministicDescriptorBinarySchema = createDeterministicDescriptorBinarySchema;
5
+ exports.generateSchemaBinarySource = generateSchemaBinarySource;
6
+ exports.proveSchemaBinaryRoundTrip = proveSchemaBinaryRoundTrip;
7
+ const schema_js_1 = require("./schema.js");
8
+ const canonical_js_1 = require("./canonical.js");
9
+ const schema_dsl_binary_js_1 = require("./schema-dsl-binary.js");
10
+ const typeScriptIdentifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
11
+ function generateSchemaTypes(descriptor, options = {}) {
12
+ const validated = (0, schema_js_1.defineSyncplayGame)(descriptor);
13
+ const rootName = schemaRootName(options.rootName, validated.deterministicVersion);
14
+ const lines = [];
15
+ lines.push(`export const ${rootName}SchemaIdentity = ${JSON.stringify(validated.replayIdentity, null, 2)} as const;`);
16
+ lines.push('');
17
+ lines.push(`export interface ${rootName}Input {`);
18
+ for (const field of validated.input.fields) {
19
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
20
+ }
21
+ lines.push('}');
22
+ lines.push('');
23
+ lines.push(`export function create${rootName}DefaultInput(): ${rootName}Input {`);
24
+ lines.push(' return {');
25
+ for (const field of validated.input.fields) {
26
+ lines.push(` ${propertyName(field.name)}: ${literalForDefaultValue(field)},`);
27
+ }
28
+ lines.push(' };');
29
+ lines.push('}');
30
+ lines.push('');
31
+ lines.push(`export function serialize${rootName}Input(input: ${rootName}Input): string {`);
32
+ lines.push(` return stableStringify${rootName}(input);`);
33
+ lines.push('}');
34
+ lines.push('');
35
+ lines.push(`export function deserialize${rootName}Input(source: string): ${rootName}Input {`);
36
+ lines.push(` return validate${rootName}Input(JSON.parse(source) as unknown);`);
37
+ lines.push('}');
38
+ lines.push('');
39
+ lines.push(`export interface ${rootName}State {`);
40
+ for (const field of validated.state.fields) {
41
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
42
+ }
43
+ lines.push('}');
44
+ lines.push('');
45
+ lines.push(`export function serialize${rootName}State(state: ${rootName}State): string {`);
46
+ lines.push(` return stableStringify${rootName}(state);`);
47
+ lines.push('}');
48
+ lines.push('');
49
+ lines.push(`export function deserialize${rootName}State(source: string): ${rootName}State {`);
50
+ lines.push(` return validate${rootName}State(JSON.parse(source) as unknown);`);
51
+ lines.push('}');
52
+ for (const channel of validated.commandChannels ?? []) {
53
+ const commandName = `${rootName}${toPascalCase(channel.id)}Command`;
54
+ lines.push('');
55
+ lines.push(`export interface ${commandName} {`);
56
+ for (const field of channel.fields) {
57
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
58
+ }
59
+ lines.push('}');
60
+ lines.push('');
61
+ lines.push(`export function serialize${commandName}(command: ${commandName}): string {`);
62
+ lines.push(` return stableStringify${rootName}(command);`);
63
+ lines.push('}');
64
+ lines.push('');
65
+ lines.push(`export function deserialize${commandName}(source: string): ${commandName} {`);
66
+ lines.push(` return validate${commandName}(JSON.parse(source) as unknown);`);
67
+ lines.push('}');
68
+ }
69
+ lines.push('');
70
+ lines.push(`export interface ${rootName}ReplayMetadata {`);
71
+ lines.push(' readonly deterministicVersion: string;');
72
+ lines.push(' readonly engineIdentityHash: string;');
73
+ lines.push(' readonly artifactId: string;');
74
+ lines.push(' readonly inputSchemaId: string;');
75
+ lines.push(' readonly stateSchemaId: string;');
76
+ lines.push(' readonly commandSchemaId: string;');
77
+ lines.push(' readonly assetSchemaId: string;');
78
+ lines.push(' readonly configSchemaId: string;');
79
+ lines.push(' readonly randomSeed: number;');
80
+ lines.push(' readonly checksumCadence: number;');
81
+ lines.push('}');
82
+ lines.push('');
83
+ lines.push(`export function serialize${rootName}ReplayMetadata(metadata: ${rootName}ReplayMetadata): string {`);
84
+ lines.push(` return stableStringify${rootName}(metadata);`);
85
+ lines.push('}');
86
+ lines.push('');
87
+ lines.push(`export function deserialize${rootName}ReplayMetadata(source: string): ${rootName}ReplayMetadata {`);
88
+ lines.push(` return validate${rootName}ReplayMetadata(JSON.parse(source) as unknown);`);
89
+ lines.push('}');
90
+ lines.push('');
91
+ lines.push(`export interface ${rootName}AssetManifest {`);
92
+ lines.push(' readonly assetDbHash: string;');
93
+ lines.push(' readonly lookupHash: string;');
94
+ lines.push(' readonly schemaHash: string;');
95
+ lines.push(' readonly dependencyHash: string;');
96
+ lines.push('}');
97
+ lines.push('');
98
+ lines.push(`export function serialize${rootName}AssetManifest(manifest: ${rootName}AssetManifest): string {`);
99
+ lines.push(` return stableStringify${rootName}(manifest);`);
100
+ lines.push('}');
101
+ lines.push('');
102
+ lines.push(`export function deserialize${rootName}AssetManifest(source: string): ${rootName}AssetManifest {`);
103
+ lines.push(` return validate${rootName}AssetManifest(JSON.parse(source) as unknown);`);
104
+ lines.push('}');
105
+ const eventTypes = [];
106
+ for (const event of validated.events ?? []) {
107
+ const eventName = `${rootName}${toPascalCase(event.id.split('.').at(-1) ?? event.id)}Event`;
108
+ const payloadName = `${eventName}Payload`;
109
+ eventTypes.push(eventName);
110
+ lines.push('');
111
+ lines.push(`export interface ${payloadName} {`);
112
+ for (const field of event.payloadFields) {
113
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
114
+ }
115
+ lines.push('}');
116
+ lines.push('');
117
+ lines.push(`export interface ${eventName} {`);
118
+ lines.push(` readonly id: '${event.id}';`);
119
+ lines.push(` readonly rollback: '${event.rollback}';`);
120
+ lines.push(` readonly payload: ${payloadName};`);
121
+ lines.push('}');
122
+ }
123
+ if (eventTypes.length > 0) {
124
+ lines.push('');
125
+ lines.push(`export type ${rootName}Event = ${eventTypes.join(' | ')};`);
126
+ }
127
+ else {
128
+ lines.push('');
129
+ lines.push(`export type ${rootName}Event = never;`);
130
+ }
131
+ for (const asset of validated.assets ?? []) {
132
+ lines.push('');
133
+ lines.push(`export interface ${rootName}${toPascalCase(asset.id)}Asset {`);
134
+ for (const field of asset.fields) {
135
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
136
+ }
137
+ lines.push('}');
138
+ }
139
+ if (validated.config !== undefined) {
140
+ lines.push('');
141
+ lines.push(`export interface ${rootName}Config {`);
142
+ for (const field of validated.config.fields) {
143
+ lines.push(` readonly ${propertyName(field.name)}: ${fieldToType(field)};`);
144
+ }
145
+ lines.push('}');
146
+ }
147
+ lines.push('');
148
+ lines.push(`function stableStringify${rootName}(value: unknown): string {`);
149
+ lines.push(` return JSON.stringify(stableCanonicalize${rootName}(value));`);
150
+ lines.push('}');
151
+ emitObjectValidator(lines, rootName, `${rootName}Input`, `${rootName}Input`, validated.input.fields, validated.maxPlayers);
152
+ emitObjectValidator(lines, rootName, `${rootName}State`, `${rootName}State`, validated.state.fields, validated.maxPlayers);
153
+ for (const channel of validated.commandChannels ?? []) {
154
+ const commandName = `${rootName}${toPascalCase(channel.id)}Command`;
155
+ emitObjectValidator(lines, rootName, commandName, commandName, channel.fields, validated.maxPlayers);
156
+ }
157
+ emitFixedObjectValidator(lines, rootName, `${rootName}ReplayMetadata`, `${rootName}ReplayMetadata`, [
158
+ { name: 'deterministicVersion', kind: 'string' },
159
+ { name: 'engineIdentityHash', kind: 'string' },
160
+ { name: 'artifactId', kind: 'string' },
161
+ { name: 'inputSchemaId', kind: 'string' },
162
+ { name: 'stateSchemaId', kind: 'string' },
163
+ { name: 'commandSchemaId', kind: 'string' },
164
+ { name: 'assetSchemaId', kind: 'string' },
165
+ { name: 'configSchemaId', kind: 'string' },
166
+ { name: 'randomSeed', kind: 'integer' },
167
+ { name: 'checksumCadence', kind: 'integer' },
168
+ ]);
169
+ emitFixedObjectValidator(lines, rootName, `${rootName}AssetManifest`, `${rootName}AssetManifest`, [
170
+ { name: 'assetDbHash', kind: 'string' },
171
+ { name: 'lookupHash', kind: 'string' },
172
+ { name: 'schemaHash', kind: 'string' },
173
+ { name: 'dependencyHash', kind: 'string' },
174
+ ]);
175
+ lines.push('');
176
+ lines.push(`function stableCanonicalize${rootName}(value: unknown): unknown {`);
177
+ lines.push(' if (value === null || typeof value !== "object") {');
178
+ lines.push(' return value;');
179
+ lines.push(' }');
180
+ lines.push(' if (Array.isArray(value)) {');
181
+ lines.push(` return value.map((item) => stableCanonicalize${rootName}(item));`);
182
+ lines.push(' }');
183
+ lines.push(' const record = value as Record<string, unknown>;');
184
+ lines.push(' const output: Record<string, unknown> = {};');
185
+ lines.push(` for (const key of Object.keys(record).sort(compare${rootName}Strings)) {`);
186
+ lines.push(` output[key] = stableCanonicalize${rootName}(record[key]);`);
187
+ lines.push(' }');
188
+ lines.push(' return output;');
189
+ lines.push('}');
190
+ lines.push('');
191
+ lines.push(`function assert${rootName}Record(value: unknown, label: string): Record<string, unknown> {`);
192
+ lines.push(' if (value === null || typeof value !== "object" || Array.isArray(value)) {');
193
+ lines.push(' throw new Error(`${label} must be an object`);');
194
+ lines.push(' }');
195
+ lines.push(' return value as Record<string, unknown>;');
196
+ lines.push('}');
197
+ lines.push('');
198
+ lines.push(`function assert${rootName}ExactKeys(record: Record<string, unknown>, label: string, expectedKeys: readonly string[]): void {`);
199
+ lines.push(` const actual = Object.keys(record).sort(compare${rootName}Strings);`);
200
+ lines.push(` const expected = [...expectedKeys].sort(compare${rootName}Strings);`);
201
+ lines.push(' if (actual.length !== expected.length) {');
202
+ lines.push(' throw new Error(`${label} has ${actual.length} fields, expected ${expected.length}`);');
203
+ lines.push(' }');
204
+ lines.push(' for (let index = 0; index < expected.length; index += 1) {');
205
+ lines.push(' if (actual[index] !== expected[index]) {');
206
+ lines.push(' throw new Error(`${label} field mismatch at ${index}: expected ${expected[index]}, actual ${actual[index]}`);');
207
+ lines.push(' }');
208
+ lines.push(' }');
209
+ lines.push('}');
210
+ lines.push('');
211
+ lines.push(`function read${rootName}Boolean(value: unknown, label: string): boolean {`);
212
+ lines.push(' if (typeof value !== "boolean") {');
213
+ lines.push(' throw new Error(`${label} must be boolean`);');
214
+ lines.push(' }');
215
+ lines.push(' return value;');
216
+ lines.push('}');
217
+ lines.push('');
218
+ lines.push(`function read${rootName}String(value: unknown, label: string): string {`);
219
+ lines.push(' if (typeof value !== "string") {');
220
+ lines.push(' throw new Error(`${label} must be string`);');
221
+ lines.push(' }');
222
+ lines.push(' return value;');
223
+ lines.push('}');
224
+ lines.push('');
225
+ lines.push(`function read${rootName}Integer(value: unknown, label: string): number {`);
226
+ lines.push(' if (typeof value !== "number" || !Number.isInteger(value)) {');
227
+ lines.push(' throw new Error(`${label} must be integer`);');
228
+ lines.push(' }');
229
+ lines.push(' return value;');
230
+ lines.push('}');
231
+ lines.push('');
232
+ lines.push(`function assert${rootName}IntegerRange(value: number, label: string, min: number | undefined, max: number | undefined): number {`);
233
+ lines.push(' if (min !== undefined && value < min) {');
234
+ lines.push(' throw new Error(`${label} must be >= ${min}`);');
235
+ lines.push(' }');
236
+ lines.push(' if (max !== undefined && value > max) {');
237
+ lines.push(' throw new Error(`${label} must be <= ${max}`);');
238
+ lines.push(' }');
239
+ lines.push(' return value;');
240
+ lines.push('}');
241
+ lines.push('');
242
+ lines.push(`function read${rootName}Array<T>(value: unknown, label: string, readItem: (item: unknown, itemLabel: string) => T): readonly T[] {`);
243
+ lines.push(' if (!Array.isArray(value)) {');
244
+ lines.push(' throw new Error(`${label} must be array`);');
245
+ lines.push(' }');
246
+ lines.push(' return value.map((item, index) => readItem(item, `${label}[${index}]`));');
247
+ lines.push('}');
248
+ lines.push('');
249
+ lines.push(`function compare${rootName}Strings(left: string, right: string): number {`);
250
+ lines.push(' if (left < right) return -1;');
251
+ lines.push(' if (left > right) return 1;');
252
+ lines.push(' return 0;');
253
+ lines.push('}');
254
+ return `${lines.join('\n')}\n`;
255
+ }
256
+ function createDeterministicDescriptorBinarySchema(descriptor, options = {}) {
257
+ const validated = (0, schema_js_1.defineSyncplayGame)(descriptor);
258
+ const rootName = schemaRootName(options.rootName, validated.deterministicVersion);
259
+ const declarations = [
260
+ descriptorDeclaration('input', `${rootName}Input`, validated.input.fields),
261
+ descriptorDeclaration('component', `${rootName}State`, validated.state.fields),
262
+ ...(validated.commandChannels ?? []).map((channel) => commandDeclaration(rootName, channel)),
263
+ ...(validated.events ?? []).map((event) => eventDeclaration(rootName, event)),
264
+ ...(validated.assets ?? []).map((asset) => assetDeclaration(rootName, asset)),
265
+ ...(validated.config === undefined ? [] : [configDeclaration(rootName, validated.config)]),
266
+ ].sort(compareDslDeclarations);
267
+ const body = {
268
+ imports: [],
269
+ pragmas: {
270
+ artifact: validated.artifactId,
271
+ maxPlayers: String(validated.maxPlayers),
272
+ tickRate: String(validated.tickRate),
273
+ },
274
+ declarations,
275
+ };
276
+ return {
277
+ ...body,
278
+ schemaHash: (0, canonical_js_1.defaultChecksum)(body),
279
+ };
280
+ }
281
+ function generateSchemaBinarySource(descriptor, options = {}) {
282
+ const validated = (0, schema_js_1.defineSyncplayGame)(descriptor);
283
+ const rootName = schemaRootName(options.rootName, validated.deterministicVersion);
284
+ const importFrom = options.importFrom ?? '@series-inc/rundot-syncplay/browser';
285
+ const schema = createDeterministicDescriptorBinarySchema(validated, { rootName });
286
+ const declarationNames = schema.declarations.map((declaration) => declaration.name);
287
+ const lines = [];
288
+ lines.push('import {');
289
+ lines.push(' createDeterministicBinarySerializer,');
290
+ lines.push(' type DeterministicDslSchema,');
291
+ lines.push(' type DeterministicDslValue,');
292
+ lines.push(`} from ${JSON.stringify(importFrom)};`);
293
+ lines.push('');
294
+ lines.push(`export const ${rootName}BinarySchema = ${JSON.stringify(schema, null, 2)} as const satisfies DeterministicDslSchema;`);
295
+ lines.push('');
296
+ lines.push(`export const ${rootName}BinarySchemaHash = ${JSON.stringify(schema.schemaHash)} as const;`);
297
+ lines.push(`export const ${rootName}BinaryArtifactId = ${JSON.stringify(validated.artifactId)} as const;`);
298
+ lines.push(`export type ${rootName}BinaryDeclarationName = ${declarationNames.map((name) => JSON.stringify(name)).join(' | ')};`);
299
+ lines.push('');
300
+ lines.push(`const ${rootName}BinarySerializer = createDeterministicBinarySerializer(${rootName}BinarySchema);`);
301
+ lines.push('');
302
+ lines.push(`export function encode${rootName}Binary(declarationName: ${rootName}BinaryDeclarationName, value: Readonly<Record<string, DeterministicDslValue>>): Uint8Array {`);
303
+ lines.push(` return ${rootName}BinarySerializer.encode(declarationName, value);`);
304
+ lines.push('}');
305
+ lines.push('');
306
+ lines.push(`export function decode${rootName}Binary(declarationName: ${rootName}BinaryDeclarationName, bytes: Uint8Array): Readonly<Record<string, DeterministicDslValue>> {`);
307
+ lines.push(` return ${rootName}BinarySerializer.decode(declarationName, bytes);`);
308
+ lines.push('}');
309
+ return `${lines.join('\n')}\n`;
310
+ }
311
+ function proveSchemaBinaryRoundTrip(descriptor, options = {}) {
312
+ const schema = createDeterministicDescriptorBinarySchema(descriptor, options);
313
+ const serializer = (0, schema_dsl_binary_js_1.createDeterministicBinarySerializer)(schema);
314
+ const decoded = [];
315
+ let encodedBytes = 0;
316
+ for (const declaration of schema.declarations) {
317
+ const value = sampleBinaryValueForDeclaration(declaration);
318
+ const bytes = serializer.encode(declaration.name, value);
319
+ encodedBytes += bytes.byteLength;
320
+ decoded.push(serializer.decode(declaration.name, bytes));
321
+ }
322
+ return {
323
+ result: 'pass',
324
+ schemaHash: schema.schemaHash,
325
+ declarationCount: schema.declarations.length,
326
+ fieldCount: schema.declarations.reduce((sum, declaration) => sum + declaration.fields.length, 0),
327
+ encodedBytes,
328
+ roundTripChecksum: (0, canonical_js_1.defaultChecksum)(decoded),
329
+ };
330
+ }
331
+ function emitObjectValidator(lines, rootName, functionName, typeName, fields, maxPlayers) {
332
+ emitFixedObjectValidator(lines, rootName, functionName, typeName, fields.map((field) => ({
333
+ name: field.name,
334
+ kind: field.kind,
335
+ collection: field.collectionOrdering !== undefined,
336
+ min: field.kind === 'player-slot' ? field.min ?? 0 : field.min,
337
+ max: field.kind === 'player-slot' ? field.max ?? maxPlayers - 1 : field.max,
338
+ })));
339
+ }
340
+ function emitFixedObjectValidator(lines, rootName, functionName, typeName, fields) {
341
+ lines.push('');
342
+ lines.push(`function validate${functionName}(value: unknown): ${typeName} {`);
343
+ lines.push(` const record = assert${rootName}Record(value, ${JSON.stringify(typeName)});`);
344
+ lines.push(` assert${rootName}ExactKeys(record, ${JSON.stringify(typeName)}, [${fields.map((field) => JSON.stringify(field.name)).join(', ')}]);`);
345
+ lines.push(' return {');
346
+ for (const field of fields) {
347
+ lines.push(` ${propertyName(field.name)}: ${field.collection === true ? collectionReadExpression(rootName, typeName, field) : scalarReadExpression(rootName, typeName, field)},`);
348
+ }
349
+ lines.push(` } as ${typeName};`);
350
+ lines.push('}');
351
+ }
352
+ function scalarReadExpression(rootName, typeName, field) {
353
+ const reader = generatedValidatorReaderName(rootName, field.kind);
354
+ const label = JSON.stringify(`${typeName}.${field.name}`);
355
+ const readCall = `${reader}(record[${JSON.stringify(field.name)}], ${label})`;
356
+ return hasGeneratedRangeConstraint(field)
357
+ ? `${generatedIntegerRangeAssertName(rootName)}(${readCall}, ${label}, ${numberLiteral(field.min)}, ${numberLiteral(field.max)})`
358
+ : readCall;
359
+ }
360
+ function collectionReadExpression(rootName, typeName, field) {
361
+ const reader = generatedValidatorReaderName(rootName, field.kind);
362
+ if (!hasGeneratedRangeConstraint(field)) {
363
+ return `read${rootName}Array(record[${JSON.stringify(field.name)}], ${JSON.stringify(`${typeName}.${field.name}`)}, ${reader})`;
364
+ }
365
+ const rangeAssert = generatedIntegerRangeAssertName(rootName);
366
+ return `read${rootName}Array(record[${JSON.stringify(field.name)}], ${JSON.stringify(`${typeName}.${field.name}`)}, (item, itemLabel) => ${rangeAssert}(${reader}(item, itemLabel), itemLabel, ${numberLiteral(field.min)}, ${numberLiteral(field.max)}))`;
367
+ }
368
+ function hasGeneratedRangeConstraint(field) {
369
+ return (field.min !== undefined || field.max !== undefined)
370
+ && (field.kind === 'integer' || field.kind === 'fixed-q16' || field.kind === 'bitfield' || field.kind === 'player-slot');
371
+ }
372
+ function numberLiteral(value) {
373
+ return value === undefined ? 'undefined' : String(value);
374
+ }
375
+ function validatorSuffix(kind) {
376
+ switch (kind) {
377
+ case 'boolean':
378
+ case 'button':
379
+ return 'Boolean';
380
+ case 'string':
381
+ case 'asset-ref':
382
+ return 'String';
383
+ case 'integer':
384
+ case 'fixed-q16':
385
+ case 'bitfield':
386
+ case 'player-slot':
387
+ return 'Integer';
388
+ default:
389
+ return 'Never';
390
+ }
391
+ }
392
+ function generatedValidatorReaderName(rootName, kind) {
393
+ return generatedTypeScriptIdentifier(`read${rootName}${validatorSuffix(kind)}`, 'validator reader name');
394
+ }
395
+ function generatedIntegerRangeAssertName(rootName) {
396
+ return generatedTypeScriptIdentifier(`assert${rootName}IntegerRange`, 'integer range assertion name');
397
+ }
398
+ function literalForDefaultValue(field) {
399
+ if (typeof field.defaultValue === 'string') {
400
+ return JSON.stringify(field.defaultValue);
401
+ }
402
+ return String(field.defaultValue);
403
+ }
404
+ function fieldToType(field) {
405
+ const baseType = scalarFieldToType(field);
406
+ return field.collectionOrdering === undefined ? baseType : `readonly ${baseType}[]`;
407
+ }
408
+ function scalarFieldToType(field) {
409
+ switch (field.kind) {
410
+ case 'boolean':
411
+ case 'button':
412
+ return 'boolean';
413
+ case 'integer':
414
+ case 'fixed-q16':
415
+ case 'bitfield':
416
+ case 'player-slot':
417
+ return 'number';
418
+ case 'string':
419
+ case 'asset-ref':
420
+ return 'string';
421
+ default:
422
+ return 'never';
423
+ }
424
+ }
425
+ function descriptorDeclaration(kind, name, fields) {
426
+ return {
427
+ kind,
428
+ name,
429
+ fields: fields.map(descriptorBinaryField).sort(compareDslFields),
430
+ modifiers: [],
431
+ };
432
+ }
433
+ function commandDeclaration(rootName, channel) {
434
+ return descriptorDeclaration('command', `${rootName}${toPascalCase(channel.id)}Command`, channel.fields);
435
+ }
436
+ function eventDeclaration(rootName, event) {
437
+ return descriptorDeclaration('event', `${rootName}${toPascalCase(event.id.split('.').at(-1) ?? event.id)}Event`, event.payloadFields);
438
+ }
439
+ function assetDeclaration(rootName, asset) {
440
+ return descriptorDeclaration('struct', `${rootName}${toPascalCase(asset.id)}Asset`, asset.fields);
441
+ }
442
+ function configDeclaration(rootName, config) {
443
+ return descriptorDeclaration('global', `${rootName}${toPascalCase(config.id)}Config`, config.fields);
444
+ }
445
+ function descriptorBinaryField(field) {
446
+ const scalar = descriptorBinaryScalarKind(field);
447
+ return {
448
+ name: safeBinaryFieldName(field.name),
449
+ kind: field.collectionOrdering === undefined ? scalar : `${scalar}[]`,
450
+ };
451
+ }
452
+ function descriptorBinaryScalarKind(field) {
453
+ switch (field.kind) {
454
+ case 'boolean':
455
+ return 'bool';
456
+ case 'button':
457
+ return 'button';
458
+ case 'integer':
459
+ case 'bitfield':
460
+ case 'player-slot':
461
+ return 'int';
462
+ case 'fixed-q16':
463
+ return 'fixed';
464
+ case 'string':
465
+ return 'string';
466
+ case 'asset-ref':
467
+ return 'asset-ref';
468
+ default:
469
+ return 'int';
470
+ }
471
+ }
472
+ function safeBinaryFieldName(name) {
473
+ return toCamelCase(name);
474
+ }
475
+ function sampleBinaryValueForDeclaration(declaration) {
476
+ const value = {};
477
+ for (const field of declaration.fields) {
478
+ value[field.name] = sampleBinaryValueForField(field);
479
+ }
480
+ return value;
481
+ }
482
+ function sampleBinaryValueForField(field) {
483
+ if (field.kind.endsWith('[]')) {
484
+ return [sampleBinaryScalarValue(field.kind.slice(0, -2))];
485
+ }
486
+ return sampleBinaryScalarValue(field.kind);
487
+ }
488
+ function sampleBinaryScalarValue(kind) {
489
+ if (kind === 'bool' || kind === 'button') {
490
+ return true;
491
+ }
492
+ if (kind === 'string' || kind === 'asset-ref' || kind.startsWith('asset-ref:')) {
493
+ return 'sample.ref';
494
+ }
495
+ if (kind === 'fixed') {
496
+ return 1.5;
497
+ }
498
+ return 1;
499
+ }
500
+ function compareDslDeclarations(left, right) {
501
+ return compareStrings(left.kind, right.kind) || compareStrings(left.name, right.name);
502
+ }
503
+ function compareDslFields(left, right) {
504
+ return compareStrings(left.name, right.name);
505
+ }
506
+ function compareStrings(left, right) {
507
+ if (left < right)
508
+ return -1;
509
+ if (left > right)
510
+ return 1;
511
+ return 0;
512
+ }
513
+ function toPascalCase(value) {
514
+ const words = value.split(/[^a-zA-Z0-9]+/).filter((word) => word.length > 0);
515
+ const name = words.map((word) => `${word[0].toUpperCase()}${word.slice(1)}`).join('');
516
+ return name.length === 0 ? 'DeterministicGame' : name;
517
+ }
518
+ function schemaRootName(rootName, deterministicVersion) {
519
+ const candidate = rootName ?? generatedRootName(deterministicVersion);
520
+ return generatedTypeScriptIdentifier(candidate, 'Schema rootName');
521
+ }
522
+ function generatedRootName(deterministicVersion) {
523
+ const candidate = toPascalCase(deterministicVersion.split('@')[0]);
524
+ return /^[A-Za-z_$]/.test(candidate) ? candidate : `Game${candidate}`;
525
+ }
526
+ function toCamelCase(value) {
527
+ const pascal = toPascalCase(value);
528
+ if (pascal.length === 0) {
529
+ return 'field';
530
+ }
531
+ return `${pascal[0].toLowerCase()}${pascal.slice(1)}`;
532
+ }
533
+ function propertyName(value) {
534
+ return typeScriptIdentifierPattern.test(value) ? value : JSON.stringify(value);
535
+ }
536
+ function isTypeScriptIdentifier(value) {
537
+ return typeScriptIdentifierPattern.test(value);
538
+ }
539
+ function generatedTypeScriptIdentifier(value, label) {
540
+ if (!isTypeScriptIdentifier(value)) {
541
+ throw new Error(`${label} must be a TypeScript identifier, received ${JSON.stringify(value)}`);
542
+ }
543
+ return value;
544
+ }