quantum-forge 2.6.5 → 2.7.1
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/QUANTUM_FORGE.md +23 -11
- package/README.md +46 -26
- package/dist/lib/quantum.d.ts +10 -4
- package/dist/lib/quantum.js +19 -4
- package/dist/lib/quantum.js.map +1 -1
- package/dist/lib/vite-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin.js.map +1 -1
- package/dist/quantum-forge-qubit/quantum-forge-web-esm.wasm +0 -0
- package/dist/quantum-forge-web-0.3.0.tgz +0 -0
- package/dist/quantum-forge-web-esm.wasm +0 -0
- package/package.json +8 -4
- package/quantum-forge-sw.js +3 -3
- package/scripts/cli.mjs +137 -0
- package/scripts/prepare.mjs +16 -0
package/QUANTUM_FORGE.md
CHANGED
|
@@ -16,10 +16,10 @@ const fs = require('fs');
|
|
|
16
16
|
try {
|
|
17
17
|
const p = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
|
18
18
|
const deps = { ...p.dependencies, ...p.devDependencies };
|
|
19
|
-
const core = deps['quantum-forge'] || deps['
|
|
20
|
-
const engine = deps['quantum-forge-engine'] || deps['
|
|
21
|
-
const corePkg = deps['quantum-forge'] ? 'quantum-forge' : '
|
|
22
|
-
const enginePkg = deps['quantum-forge-engine'] ? 'quantum-forge-engine' : '
|
|
19
|
+
const core = deps['quantum-forge'] || deps['quantum-forge'] || 'not installed';
|
|
20
|
+
const engine = deps['quantum-forge-engine'] || deps['quantum-forge-engine'] || 'not installed';
|
|
21
|
+
const corePkg = deps['quantum-forge'] ? 'quantum-forge' : 'quantum-forge';
|
|
22
|
+
const enginePkg = deps['quantum-forge-engine'] ? 'quantum-forge-engine' : 'quantum-forge-engine';
|
|
23
23
|
console.log('Core: ' + corePkg + '@' + core);
|
|
24
24
|
if (engine !== 'not installed') console.log('Engine: ' + enginePkg + '@' + engine);
|
|
25
25
|
console.log('Type: ' + (p.type || 'commonjs'));
|
|
@@ -42,6 +42,18 @@ useQuantumForgeBuild("qubit"); // before ensureLoaded()
|
|
|
42
42
|
await ensureLoaded();
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
### Node and headless
|
|
46
|
+
|
|
47
|
+
No Vite plugin is needed in Node (tests, servers, agents). The loader finds the WASM inside the installed package, and `useQuantumForgeBuild("qubit")` selects the variant the same way as in a page. Use `setWasmBasePath(fileUrl)` only for WASM files kept outside the package. Node 22 or newer.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { useQuantumForgeBuild, ensureLoaded, getModule, getVersion } from "quantum-forge/quantum";
|
|
51
|
+
useQuantumForgeBuild("qubit");
|
|
52
|
+
await ensureLoaded();
|
|
53
|
+
console.log(getVersion()); // the package version
|
|
54
|
+
const m = getModule();
|
|
55
|
+
```
|
|
56
|
+
|
|
45
57
|
### Vite Plugin
|
|
46
58
|
|
|
47
59
|
```typescript
|
|
@@ -132,7 +144,7 @@ Start with dimension 2 unless your design needs more. Shipped package supports u
|
|
|
132
144
|
|
|
133
145
|
## Gates
|
|
134
146
|
|
|
135
|
-
All gates are called via `getModule()`. Every gate accepts optional `predicates` for conditional execution (creates entanglement).
|
|
147
|
+
All gates are called via `getModule()`. Every gate accepts optional `predicates` for conditional execution (creates entanglement). Omit `fraction` for the discrete gate. Passing a number, `1` included, selects the continuous rotation instead: at `1` it lands on the same state as the discrete gate, but by the slower rotation path. Write `undefined` when you mean the discrete gate.
|
|
136
148
|
|
|
137
149
|
| Gate | Method | Dim | Description |
|
|
138
150
|
|------|--------|-----|-------------|
|
|
@@ -140,19 +152,19 @@ All gates are called via `getModule()`. Every gate accepts optional `predicates`
|
|
|
140
152
|
| Shift / X | `m.shift(prop, fraction?, preds?)` | Any | Inverse of cycle. Same as cycle for dim=2 |
|
|
141
153
|
| Hadamard | `m.hadamard(prop, fraction?, preds?)` | Any | Equal superposition. The main "make it quantum" gate |
|
|
142
154
|
| Inv. Hadamard | `m.inverse_hadamard(prop, preds?)` | Any | Reverse of hadamard |
|
|
143
|
-
| Clock / Z | `m.clock(prop, fraction
|
|
155
|
+
| Clock / Z | `m.clock(prop, fraction?, preds?)` | Any | Phase rotation. Invisible until interaction |
|
|
144
156
|
| Y | `m.y(prop, fraction?, preds?)` | **2 only** | Pauli Y. Throws if dimension != 2 |
|
|
145
157
|
| iSwap | `m.i_swap(p1, p2, fraction, preds?)` | Any | Anti-correlated entanglement |
|
|
146
158
|
| Swap | `m.swap(p1, p2, preds?)` | Any | Direct state exchange, no entanglement |
|
|
147
159
|
| Phase Rotate | `m.phase_rotate(preds, angle)` | Any | Phase rotation conditioned on predicates (required) |
|
|
148
160
|
|
|
149
|
-
**Fractional gates**: `
|
|
161
|
+
**Fractional gates**: `0.5` is the square root of the gate, `0.1` barely moves the state, `1` completes the rotation. Apply a small fraction every frame for a gradual effect.
|
|
150
162
|
|
|
151
163
|
### Predicates (Conditional Gates)
|
|
152
164
|
|
|
153
165
|
Every gate accepts predicates that condition it on other properties' states. **This is how entanglement works** — a gate that depends on another property's state correlates them.
|
|
154
166
|
|
|
155
|
-
**Gotcha:** predicates are the third argument
|
|
167
|
+
**Gotcha:** predicates are the third argument, so the discrete gate with predicates is `m.shift(target, undefined, [control.is(1)])`. Passing `1` runs the fractional path instead (same state, slower).
|
|
156
168
|
|
|
157
169
|
```typescript
|
|
158
170
|
// CNOT: flip target only when control is |1⟩
|
|
@@ -211,7 +223,7 @@ createLinkedPair(id1: string, id2: string): void {
|
|
|
211
223
|
const m = this.getModule();
|
|
212
224
|
m.cycle(prop1);
|
|
213
225
|
m.hadamard(prop1);
|
|
214
|
-
m.shift(prop2,
|
|
226
|
+
m.shift(prop2, undefined, [prop1.is(1)]); // CNOT
|
|
215
227
|
this.setProperty(id1, prop1);
|
|
216
228
|
this.setProperty(id2, prop2);
|
|
217
229
|
}
|
|
@@ -250,7 +262,7 @@ this.getModule().i_swap(propA, propB, 0.5);
|
|
|
250
262
|
A control property determines whether a target enters superposition:
|
|
251
263
|
|
|
252
264
|
```typescript
|
|
253
|
-
m.hadamard(target,
|
|
265
|
+
m.hadamard(target, undefined, [control.is(1)]);
|
|
254
266
|
```
|
|
255
267
|
|
|
256
268
|
### Choosing Between Mechanisms
|
|
@@ -499,7 +511,7 @@ input.destroy();
|
|
|
499
511
|
|
|
500
512
|
| Export | Key APIs |
|
|
501
513
|
|--------|----------|
|
|
502
|
-
| `./quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
|
|
514
|
+
| `./quantum` | `ensureLoaded`, `getModule`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild`, `setWasmBasePath` |
|
|
503
515
|
| `./logging` | `Logger` |
|
|
504
516
|
| `./vite-plugin` | `quantumForgeVitePlugin` |
|
|
505
517
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Real quantum mechanics for game developers. Superposition, entanglement, and interference powered by a compiled C++ quantum simulator running via WebAssembly.
|
|
4
4
|
|
|
5
|
-
This is the core package
|
|
5
|
+
This is the core package: WASM loader, quantum property manager, and Vite plugin. For the full game framework (engine, rendering, input, audio, and more), install [`quantum-forge-engine`](https://www.npmjs.com/package/quantum-forge-engine) instead. It depends on this package, so one install gets both.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -29,48 +29,51 @@ export default defineConfig({
|
|
|
29
29
|
### 2. Use Quantum Mechanics
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
import { ensureLoaded, QuantumPropertyManager } from "quantum-forge/quantum";
|
|
32
|
+
import { ensureLoaded, getModule, QuantumPropertyManager } from "quantum-forge/quantum";
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
await ensureLoaded();
|
|
34
|
+
await ensureLoaded(); // once, before any quantum call
|
|
36
35
|
|
|
37
|
-
// Create a property manager
|
|
38
36
|
const qpm = new QuantumPropertyManager({ dimension: 2 });
|
|
37
|
+
const m = getModule(); // gates and measurement live here, not on the manager
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
qpm.hadamard(qubit);
|
|
39
|
+
const qubit = qpm.acquireProperty(); // |0⟩, from the pool
|
|
40
|
+
m.hadamard(qubit); // equal superposition
|
|
43
41
|
|
|
44
|
-
// Read
|
|
45
|
-
|
|
42
|
+
// Read without collapsing
|
|
43
|
+
m.probabilities([qubit]);
|
|
44
|
+
// [{ probability: 0.5, qudit_values: [0] }, { probability: 0.5, qudit_values: [1] }]
|
|
46
45
|
|
|
47
46
|
// Measure (collapses to 0 or 1)
|
|
48
|
-
const [value] =
|
|
47
|
+
const [value] = m.measure_properties([qubit]);
|
|
49
48
|
|
|
50
|
-
//
|
|
49
|
+
// Return the handle to the pool, reset to |0⟩
|
|
51
50
|
qpm.releaseProperty(qubit, value);
|
|
52
51
|
```
|
|
53
52
|
|
|
53
|
+
`QuantumPropertyManager` owns handles: `acquireProperty`, `releaseProperty`, `setProperty` / `getProperty` / `deleteProperty` by id, and `getModule()`. Games usually extend it and wrap the module calls in game-specific methods.
|
|
54
|
+
|
|
54
55
|
### 3. Entanglement
|
|
55
56
|
|
|
56
57
|
```typescript
|
|
57
58
|
const a = qpm.acquireProperty();
|
|
58
59
|
const b = qpm.acquireProperty();
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
m.cycle(a); // a = |1⟩
|
|
62
|
+
m.i_swap(a, b, 0.5); // half iSwap: exactly one of the pair ends up |1⟩
|
|
62
63
|
|
|
63
|
-
const [va, vb] =
|
|
64
|
-
// va
|
|
64
|
+
const [va, vb] = m.measure_properties([a, b]);
|
|
65
|
+
// va + vb === 1, every time
|
|
65
66
|
```
|
|
66
67
|
|
|
68
|
+
Predicated gates entangle too. `m.shift(b, undefined, [a.is(1)])` is a CNOT: `b` flips only where `a` is `|1⟩`. Predicates are the third argument, so pass `undefined` as the fraction to get the discrete gate.
|
|
69
|
+
|
|
67
70
|
## Editions
|
|
68
71
|
|
|
69
72
|
Two WASM builds are included:
|
|
70
73
|
|
|
71
74
|
| Edition | Dimensions | Max Qudits | Use Case |
|
|
72
75
|
|---------|-----------|------------|----------|
|
|
73
|
-
| **Qutrit** (default) | 2
|
|
76
|
+
| **Qutrit** (default) | 2 to 3 | 12 | Games using qutrits (3-state quantum digits) |
|
|
74
77
|
| **Qubit** | 2 | 20 | Games needing more qubits at dimension 2 |
|
|
75
78
|
|
|
76
79
|
To use the Qubit Edition:
|
|
@@ -78,31 +81,48 @@ To use the Qubit Edition:
|
|
|
78
81
|
```typescript
|
|
79
82
|
import { useQuantumForgeBuild, ensureLoaded } from "quantum-forge/quantum";
|
|
80
83
|
|
|
81
|
-
useQuantumForgeBuild("qubit"); //
|
|
84
|
+
useQuantumForgeBuild("qubit"); // before ensureLoaded()
|
|
85
|
+
await ensureLoaded();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Node and headless use
|
|
89
|
+
|
|
90
|
+
The same code runs in Node (tests, servers, agents) with no Vite plugin. The loader finds the WASM inside the installed package, and `useQuantumForgeBuild("qubit")` picks the variant the same way. Point the loader elsewhere only when the WASM files live outside the package:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { setWasmBasePath, ensureLoaded } from "quantum-forge/quantum";
|
|
94
|
+
import { pathToFileURL } from "node:url";
|
|
95
|
+
|
|
96
|
+
setWasmBasePath(pathToFileURL("/opt/wasm/quantum-forge-qubit").href);
|
|
82
97
|
await ensureLoaded();
|
|
83
98
|
```
|
|
84
99
|
|
|
100
|
+
Node 22 or newer.
|
|
101
|
+
|
|
85
102
|
## Package Exports
|
|
86
103
|
|
|
87
104
|
| Export | Contents |
|
|
88
105
|
|--------|----------|
|
|
89
|
-
| `quantum-forge/quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
|
|
106
|
+
| `quantum-forge/quantum` | `ensureLoaded`, `startBackgroundLoad`, `isReady`, `getModule`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild`, `setWasmBasePath`, `getVersion`, `getMaxDimension`, `getMaxQudits`, `getMaxStateSize`, `getAttribution`, `registerServiceWorker`, `OP` |
|
|
90
107
|
| `quantum-forge/logging` | `Logger` |
|
|
91
108
|
| `quantum-forge/vite-plugin` | `quantumForgeVitePlugin` |
|
|
92
109
|
|
|
93
110
|
## Gates
|
|
94
111
|
|
|
112
|
+
All gates are functions on the module from `getModule()`. Each takes the target property, an optional `fraction`, and optional `predicates`.
|
|
113
|
+
|
|
95
114
|
| Gate | Qudits | Description |
|
|
96
115
|
|------|--------|-------------|
|
|
97
116
|
| `hadamard` | 1 | Equal superposition |
|
|
98
|
-
| `cycle` | 1 |
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `y` | 1 | Y
|
|
102
|
-
| `
|
|
117
|
+
| `cycle` | 1 | \|0⟩→\|1⟩→\|2⟩→\|0⟩ (a NOT at dimension 2) |
|
|
118
|
+
| `shift` (`x`) | 1 | Inverse of cycle; same as cycle at dimension 2 |
|
|
119
|
+
| `clock` (`z`) | 1 | Phase rotation |
|
|
120
|
+
| `y` | 1 | Pauli Y, dimension 2 only |
|
|
121
|
+
| `i_swap` | 2 | Entangling swap; `fraction` is required |
|
|
103
122
|
| `swap` | 2 | Value swap |
|
|
123
|
+
| `phase_rotate` | any | Phase on the states matching the predicates |
|
|
104
124
|
|
|
105
|
-
|
|
125
|
+
Measurement: `measure_properties`, `measure_predicate`, `forced_measure_properties`. Read-only: `probabilities`, `predicate_probability`, `reduced_density_matrix`. Omit `fraction` for the discrete gate; pass a number for the continuous variant (`0.5` is the square root of the gate).
|
|
106
126
|
|
|
107
127
|
## Documentation
|
|
108
128
|
|
|
@@ -112,4 +132,4 @@ All gates support fractional application and conditional predicates.
|
|
|
112
132
|
|
|
113
133
|
## License
|
|
114
134
|
|
|
115
|
-
TypeScript source: MIT (see [LICENSE.md](./LICENSE.md)). WASM binaries: proprietary
|
|
135
|
+
TypeScript source: MIT (see [LICENSE.md](./LICENSE.md)). WASM binaries: proprietary, free for apps under $100K annual revenue with attribution. See [dist/LICENSE-BINARY.md](./dist/LICENSE-BINARY.md) for binary terms.
|
package/dist/lib/quantum.d.ts
CHANGED
|
@@ -239,17 +239,23 @@ declare namespace __quantum_forge_api_mjs {
|
|
|
239
239
|
/**
|
|
240
240
|
* Set the base URL path where Quantum Forge WASM files are served.
|
|
241
241
|
* Default is "/quantum-forge" which matches the Vite plugin's serve path.
|
|
242
|
-
* Consumers using the Vite plugin don't need to call this.
|
|
242
|
+
* Consumers using the Vite plugin don't need to call this. In Node the default
|
|
243
|
+
* is this package's own dist/ directory, so a file URL is only needed when the
|
|
244
|
+
* WASM lives somewhere else.
|
|
243
245
|
*/
|
|
244
246
|
declare function setWasmBasePath(path: string): void;
|
|
245
247
|
/**
|
|
246
|
-
* Select a named WASM build variant (e.g. "
|
|
247
|
-
*
|
|
248
|
+
* Select a named WASM build variant (e.g. "qubit").
|
|
249
|
+
* In a page this loads from "/quantum-forge-{name}"; in Node it loads from
|
|
250
|
+
* this package's dist/quantum-forge-{name}/. Replaces any earlier
|
|
251
|
+
* setWasmBasePath() call.
|
|
248
252
|
*
|
|
249
253
|
* Must be called before `ensureLoaded()`. If the module is already loaded,
|
|
250
254
|
* a warning is logged and the call is ignored.
|
|
251
255
|
*/
|
|
252
256
|
declare function useQuantumForgeBuild(name: string): void;
|
|
257
|
+
/** The base path ensureLoaded() imports from, resolved at load time. */
|
|
258
|
+
declare function getWasmBasePath(): string;
|
|
253
259
|
/**
|
|
254
260
|
* Start loading the WASM module in the background.
|
|
255
261
|
* Call this after the page has rendered (e.g., after DOMContentLoaded or initial paint).
|
|
@@ -533,4 +539,4 @@ declare const OP: {
|
|
|
533
539
|
readonly ROTATE_BASIS_PAIR: 11;
|
|
534
540
|
};
|
|
535
541
|
|
|
536
|
-
export { type BatchOp, type BatchResult, OP, type OpCode, type OpNum, type PredicateSpec$1 as PredicateSpec, type QuantumOperation, QuantumPropertyManager, QuantumRecorder, type QuantumRecorderHook, type SerializedPredicate, ensureLoaded, getAttribution, getMaxDimension, getMaxQudits, getMaxStateSize, getModule, getQuantumForge, getVersion, getWasmMemoryBytes, isReady, registerServiceWorker, setWasmBasePath, startBackgroundLoad, useQuantumForgeBuild };
|
|
542
|
+
export { type BatchOp, type BatchResult, OP, type OpCode, type OpNum, type PredicateSpec$1 as PredicateSpec, type QuantumOperation, QuantumPropertyManager, QuantumRecorder, type QuantumRecorderHook, type SerializedPredicate, ensureLoaded, getAttribution, getMaxDimension, getMaxQudits, getMaxStateSize, getModule, getQuantumForge, getVersion, getWasmBasePath, getWasmMemoryBytes, isReady, registerServiceWorker, setWasmBasePath, startBackgroundLoad, useQuantumForgeBuild };
|
package/dist/lib/quantum.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// src/quantum/QuantumForgeLoader.ts
|
|
2
|
-
var
|
|
2
|
+
var explicitBasePath = null;
|
|
3
|
+
var variant = null;
|
|
3
4
|
function setWasmBasePath(path) {
|
|
4
|
-
|
|
5
|
+
explicitBasePath = path.endsWith("/") ? path.slice(0, -1) : path;
|
|
5
6
|
}
|
|
6
7
|
function useQuantumForgeBuild(name) {
|
|
7
8
|
if (isInitialized) {
|
|
@@ -11,7 +12,20 @@ function useQuantumForgeBuild(name) {
|
|
|
11
12
|
);
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
variant = name;
|
|
16
|
+
explicitBasePath = null;
|
|
17
|
+
}
|
|
18
|
+
function runningFromDisk() {
|
|
19
|
+
return typeof process !== "undefined" && typeof process.versions?.node === "string" && import.meta.url.startsWith("file:");
|
|
20
|
+
}
|
|
21
|
+
function getWasmBasePath() {
|
|
22
|
+
if (explicitBasePath !== null) return explicitBasePath;
|
|
23
|
+
if (runningFromDisk()) {
|
|
24
|
+
const dist = import.meta.url.endsWith(".ts") ? "../../dist/" : "../";
|
|
25
|
+
const dir = variant ? `${dist}quantum-forge-${variant}/` : dist;
|
|
26
|
+
return new URL(dir, import.meta.url).href.replace(/\/$/, "");
|
|
27
|
+
}
|
|
28
|
+
return variant ? `/quantum-forge-${variant}` : "/quantum-forge";
|
|
15
29
|
}
|
|
16
30
|
var quantumForgeModule = null;
|
|
17
31
|
var initPromise = null;
|
|
@@ -48,7 +62,7 @@ async function ensureLoaded() {
|
|
|
48
62
|
initPromise = (async () => {
|
|
49
63
|
const startTime = performance.now();
|
|
50
64
|
logger?.info?.("Loading Quantum Forge WASM module...", "QuantumForgeLoader");
|
|
51
|
-
const modulePath = `${
|
|
65
|
+
const modulePath = `${getWasmBasePath()}/quantum-forge-web-api.mjs`;
|
|
52
66
|
const mod = await import(
|
|
53
67
|
/* @vite-ignore */
|
|
54
68
|
modulePath
|
|
@@ -548,6 +562,7 @@ export {
|
|
|
548
562
|
getModule,
|
|
549
563
|
getQuantumForge,
|
|
550
564
|
getVersion,
|
|
565
|
+
getWasmBasePath,
|
|
551
566
|
getWasmMemoryBytes,
|
|
552
567
|
isReady,
|
|
553
568
|
registerServiceWorker,
|
package/dist/lib/quantum.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/quantum/QuantumForgeLoader.ts","../../src/quantum/QuantumPropertyManager.ts","../../src/quantum/QuantumRecorder.ts","../../src/quantum/index.ts"],"sourcesContent":["/**\n * Quantum Forge Loader - Lazy loader for Quantum Forge WASM module\n *\n * Defers WASM loading from the critical path while preloading in background\n * so it's ready when needed. If not ready, callers can show loading UI.\n * \n * IMPORTANT: Quantum Forge is built from source and copied to dist/ as\n * quantum-forge-web-api.mjs (not an npm package). Run 'npm run setup' to build.\n */\n\nimport type { LoggerInterface } from \"../logging/Logger\";\n\n// Type for the quantum forge module\ntype QuantumForgeModuleType = typeof import(\"./quantum-forge-api.mjs\");\n\n// Configurable base path for WASM artifacts\nlet wasmBasePath = \"/quantum-forge\";\n\n/**\n * Set the base URL path where Quantum Forge WASM files are served.\n * Default is \"/quantum-forge\" which matches the Vite plugin's serve path.\n * Consumers using the Vite plugin don't need to call this.\n */\nexport function setWasmBasePath(path: string): void {\n wasmBasePath = path.endsWith(\"/\") ? path.slice(0, -1) : path;\n}\n\n/**\n * Select a named WASM build variant (e.g. \"d7n10\").\n * Sugar for `setWasmBasePath(\"/quantum-forge-{name}\")`.\n *\n * Must be called before `ensureLoaded()`. If the module is already loaded,\n * a warning is logged and the call is ignored.\n */\nexport function useQuantumForgeBuild(name: string): void {\n if (isInitialized) {\n logger?.warn?.(\n `useQuantumForgeBuild(\"${name}\") called after module already loaded — ignoring. Call before ensureLoaded().`,\n \"QuantumForgeLoader\",\n );\n return;\n }\n setWasmBasePath(`/quantum-forge-${name}`);\n}\n\n// Cache the module and initialization state\nlet quantumForgeModule: QuantumForgeModuleType | null = null;\nlet initPromise: Promise<void> | null = null;\nlet isInitialized = false;\nlet loadStarted = false;\n\n// Logger reference (set during startBackgroundLoad)\nlet logger: LoggerInterface | undefined;\n\n/**\n * Start loading the WASM module in the background.\n * Call this after the page has rendered (e.g., after DOMContentLoaded or initial paint).\n */\nexport function startBackgroundLoad(loggerRef?: LoggerInterface): void {\n if (loadStarted) return;\n loadStarted = true;\n logger = loggerRef;\n\n // Use requestIdleCallback if available, otherwise setTimeout\n const scheduleLoad = (callback: () => void) => {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(callback, { timeout: 2000 });\n } else {\n setTimeout(callback, 100);\n }\n };\n\n scheduleLoad(() => {\n logger?.info?.(\"Starting background Quantum Forge load\", \"QuantumForgeLoader\");\n // Trigger the load but don't await - let it happen in background\n ensureLoaded().catch((err) => {\n logger?.warn?.(\n `Background Quantum Forge load failed: ${err?.message ?? err}`,\n \"QuantumForgeLoader\",\n );\n });\n });\n}\n\n/**\n * Ensure Quantum Forge is loaded and initialized.\n * Returns a promise that resolves when the module is ready.\n * Can be called multiple times - will return the same promise.\n */\nexport async function ensureLoaded(): Promise<void> {\n if (isInitialized) return;\n\n if (initPromise) {\n await initPromise;\n return;\n }\n\n initPromise = (async () => {\n const startTime = performance.now();\n logger?.info?.(\"Loading Quantum Forge WASM module...\", \"QuantumForgeLoader\");\n\n // Dynamic import of Quantum Forge WASM module from the configured base path\n const modulePath = `${wasmBasePath}/quantum-forge-web-api.mjs`;\n const mod = (await import(/* @vite-ignore */ modulePath)) as QuantumForgeModuleType;\n quantumForgeModule = mod;\n\n // Initialize the WASM, routing stderr through the logger\n await mod.QuantumForge.initialize({\n printErr: (text: string) => logger?.warn?.(text, \"QuantumForge/WASM\"),\n });\n\n const version = mod.QuantumForge.getVersion();\n const maxDim = mod.QuantumForge.getMaxDimension();\n const maxQudits = mod.QuantumForge.getMaxQudits();\n const elapsed = (performance.now() - startTime).toFixed(0);\n\n logger?.info?.(\n `Quantum Forge v${version} ready in ${elapsed}ms (max dim: ${maxDim}, max qudits: ${maxQudits})`,\n \"QuantumForgeLoader\",\n );\n\n console.log(\n \"%c\\u269B Powered by Quantum Forge %c quantumnative.io \",\n \"background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px 0 0 3px; font-weight: bold;\",\n \"background: #1e1b4b; color: #c7d2fe; padding: 2px 6px; border-radius: 0 3px 3px 0;\",\n );\n\n isInitialized = true;\n })();\n\n // Handle errors by clearing the promise so retry is possible\n initPromise.catch(() => {\n initPromise = null;\n });\n\n await initPromise;\n}\n\n/**\n * Check if Quantum Forge is ready to use (non-blocking).\n */\nexport function isReady(): boolean {\n return isInitialized;\n}\n\n/**\n * Get the loaded module. Throws if not loaded.\n * For synchronous access after ensuring it's loaded.\n */\nexport function getModule(): typeof import(\"./quantum-forge-api.mjs\") {\n if (!quantumForgeModule || !isInitialized) {\n throw new Error(\"QuantumForge not loaded. Call ensureLoaded() first and await it.\");\n }\n return quantumForgeModule;\n}\n\n/**\n * Get the QuantumForge class from the loaded module.\n */\nexport function getQuantumForge(): typeof import(\"./quantum-forge-api.mjs\").QuantumForge {\n return getModule().QuantumForge;\n}\n\n/**\n * Convenience re-exports for common operations.\n * These will throw if module not loaded.\n */\nexport function getVersion(): string {\n return getQuantumForge().getVersion();\n}\n\nexport function getMaxDimension(): number {\n return getQuantumForge().getMaxDimension();\n}\n\nexport function getMaxQudits(): number {\n return getQuantumForge().getMaxQudits();\n}\n\nexport function getMaxStateSize(): number {\n return getQuantumForge().getMaxStateSize();\n}\n\n/**\n * Get the WASM memory bytes (for analytics).\n */\nexport function getWasmMemoryBytes(): number | null {\n if (!isInitialized) return null;\n try {\n const qf = getQuantumForge() as any;\n return typeof qf.getMemoryBytes === \"function\" ? qf.getMemoryBytes() : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the required attribution text for display in your application.\n * Include this in a user-visible location (credits screen, about page, etc.).\n */\nexport function getAttribution(): string {\n return \"Powered by Quantum Forge \\u2014 \\u00A9 Quantum Native \\u2014 quantumnative.io\";\n}\n\n/**\n * Register the Quantum Forge service worker for offline WASM caching.\n * Call once from your game controller after page load.\n * The SW caches WASM artifacts on first fetch so subsequent loads work offline.\n *\n * @param swPath - Path to the service worker file. Default: \"/quantum-forge-sw.js\"\n */\nexport async function registerServiceWorker(\n swPath = \"/quantum-forge-sw.js\",\n): Promise<ServiceWorkerRegistration | null> {\n if (!(\"serviceWorker\" in navigator)) return null;\n try {\n const reg = await navigator.serviceWorker.register(swPath);\n logger?.info?.(`Service worker registered (scope: ${reg.scope})`, \"QuantumForgeLoader\");\n return reg;\n } catch (err) {\n logger?.warn?.(\n `Service worker registration failed: ${err instanceof Error ? err.message : err}`,\n \"QuantumForgeLoader\",\n );\n return null;\n }\n}\n","/**\n * QuantumPropertyManager — manages quantum property lifecycles.\n *\n * Handles the common pattern of acquiring, pooling, and releasing WASM\n * QuantumProperty handles. Games either extend this class or compose it\n * to add game-specific quantum operations via getModule().\n *\n * Property pooling is critical: measured/removed properties are recycled\n * to avoid growing the tensor product and hitting qudit limits.\n *\n * For opt-in operation recording, attach a QuantumRecorder via setRecorder().\n */\n\nimport { getModule } from \"./QuantumForgeLoader\";\nimport type { LoggerInterface } from \"../logging/Logger\";\nimport type { QuantumProperty as QFProperty } from \"./quantum-forge-api.mjs\";\n\nexport interface PredicateSpec {\n property: QFProperty;\n value: number;\n isEqual: boolean;\n}\n\nexport interface QuantumRecorderHook {\n onAcquire?(prop: QFProperty): void;\n onRelease?(prop: QFProperty, value: number): void;\n onSetProperty?(id: string, prop: QFProperty): void;\n onDeleteProperty?(id: string): void;\n}\n\nexport class QuantumPropertyManager {\n readonly dimension: number;\n private properties: Map<string, QFProperty> = new Map();\n private pool: QFProperty[] = [];\n protected logger?: LoggerInterface;\n private _recorder?: QuantumRecorderHook;\n\n constructor(options: { dimension?: number; logger?: LoggerInterface } = {}) {\n this.dimension = options.dimension ?? 2;\n this.logger = options.logger;\n }\n\n // -- Recorder hook --\n\n /** Attach an optional recorder for operation logging. */\n setRecorder(recorder: QuantumRecorderHook | undefined): void {\n this._recorder = recorder;\n }\n\n /** Get the currently attached recorder, if any. */\n getRecorder(): QuantumRecorderHook | undefined {\n return this._recorder;\n }\n\n // -- Property lifecycle --\n\n /**\n * Get a property at |0⟩ — reuses a pooled one if available,\n * otherwise creates a fresh standalone property.\n */\n acquireProperty(): QFProperty {\n let prop: QFProperty;\n if (this.pool.length > 0) {\n prop = this.pool.pop()!;\n } else {\n prop = getModule().QuantumForge.createQuantumProperty(this.dimension);\n }\n this._recorder?.onAcquire?.(prop);\n return prop;\n }\n\n /**\n * Return a property to the pool after resetting it to |0⟩.\n * Uses the `reset` primitive which applies non-fractional cycles —\n * correct for all dimensions (no superposition created).\n */\n releaseProperty(prop: QFProperty, measuredValue: number): void {\n this._recorder?.onRelease?.(prop, measuredValue);\n getModule().reset(prop, measuredValue);\n this.pool.push(prop);\n }\n\n // -- ID mapping --\n\n setProperty(id: string, prop: QFProperty): void {\n this._recorder?.onSetProperty?.(id, prop);\n this.properties.set(id, prop);\n }\n\n getProperty(id: string): QFProperty | undefined {\n return this.properties.get(id);\n }\n\n deleteProperty(id: string): void {\n this._recorder?.onDeleteProperty?.(id);\n this.properties.delete(id);\n }\n\n hasProperty(id: string): boolean {\n return this.properties.has(id);\n }\n\n // -- Public operations --\n\n /**\n * Remove a property by ID: measure it, pool the handle, delete the mapping.\n */\n removeProperty(id: string): void {\n const prop = this.properties.get(id);\n if (prop) {\n const [value] = getModule().measure_properties([prop]);\n this.releaseProperty(prop, value);\n }\n this.deleteProperty(id);\n }\n\n /** Clear all properties, pool, and recorder. */\n clear(): void {\n this.properties.clear();\n this.pool = [];\n }\n\n get size(): number {\n return this.properties.size;\n }\n\n get poolSize(): number {\n return this.pool.length;\n }\n\n // -- WASM module access --\n\n getModule(): ReturnType<typeof getModule> {\n return getModule();\n }\n\n // -- Internal access for QuantumRecorder replay --\n\n /** @internal — used by QuantumRecorder.replayLog() to restore pool state. */\n _setPool(pool: QFProperty[]): void {\n this.pool = pool;\n }\n\n /** @internal — used by QuantumRecorder to enumerate live handles. */\n _getProperties(): Map<string, QFProperty> {\n return this.properties;\n }\n\n /** @internal — used by QuantumRecorder to enumerate pool handles. */\n _getPool(): QFProperty[] {\n return this.pool;\n }\n}\n","/**\n * QuantumRecorder — opt-in recording and replay of quantum operations.\n *\n * Attach to a QuantumPropertyManager via `manager.setRecorder(recorder)`.\n * When recording is active, lifecycle hooks log every state-mutating\n * operation. The log can be replayed via replayLog() to recreate\n * identical quantum state — measurements are forced to their recorded\n * outcomes using forced_measure_properties.\n *\n * For gate recording, call wrapGate() around each WASM gate call.\n */\n\nimport { getModule } from \"./QuantumForgeLoader\";\nimport type { QuantumPropertyManager, QuantumRecorderHook } from \"./QuantumPropertyManager\";\nimport type { QuantumOperation, SerializedPredicate } from \"./QuantumOperationLog\";\nimport type { QuantumProperty as QFProperty, Predicate as QFPredicate } from \"./quantum-forge-api.mjs\";\n\nexport interface PredicateSpec {\n property: QFProperty;\n value: number;\n isEqual: boolean;\n}\n\nexport class QuantumRecorder implements QuantumRecorderHook {\n private _recording = false;\n private _log: QuantumOperation[] = [];\n private _handleToIndex: Map<QFProperty, number> = new Map();\n private _nextIndex = 0;\n private readonly _manager: QuantumPropertyManager;\n\n constructor(manager: QuantumPropertyManager) {\n this._manager = manager;\n }\n\n // -- QuantumRecorderHook implementation --\n\n onAcquire(prop: QFProperty): void {\n if (!this._recording) return;\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n this._log.push({ op: \"acquire\", index });\n }\n\n onRelease(prop: QFProperty, value: number): void {\n if (!this._recording) return;\n const index = this._handleToIndex.get(prop);\n if (index !== undefined) {\n this._log.push({ op: \"release\", index, value });\n }\n }\n\n onSetProperty(id: string, prop: QFProperty): void {\n if (!this._recording) return;\n const index = this._handleToIndex.get(prop);\n if (index !== undefined) {\n this._log.push({ op: \"assign\", index, id });\n }\n }\n\n onDeleteProperty(id: string): void {\n if (!this._recording) return;\n this._log.push({ op: \"unassign\", id });\n }\n\n // -- Gate recording --\n\n /**\n * Build WASM predicate objects from PredicateSpec array.\n */\n buildWasmPredicates(specs: PredicateSpec[]): QFPredicate[] {\n return specs.map((s) =>\n s.isEqual ? s.property.is(s.value) : s.property.is_not(s.value),\n );\n }\n\n /**\n * Serialize predicates for the operation log.\n */\n serializePredicates(specs: PredicateSpec[]): SerializedPredicate[] | undefined {\n if (specs.length === 0) return undefined;\n return specs.map((s) => {\n const index = this._handleToIndex.get(s.property);\n return {\n propertyIndex: index ?? -1,\n value: s.value,\n isEqual: s.isEqual,\n };\n });\n }\n\n /**\n * Record a gate operation. Call this when recording is active\n * and you want to log a gate call for replay.\n */\n recordOp(op: QuantumOperation): void {\n if (!this._recording) return;\n this._log.push(op);\n }\n\n /**\n * Get the recorded index for a property handle.\n */\n getIndex(prop: QFProperty): number | undefined {\n return this._handleToIndex.get(prop);\n }\n\n // -- Recording API --\n\n /** Begin recording quantum operations. Resets any existing log. */\n startRecording(): void {\n this._recording = true;\n this._log = [];\n this._handleToIndex.clear();\n this._nextIndex = 0;\n\n // Assign indices to all currently-live handles so operations\n // on pre-existing properties are tracked correctly.\n for (const prop of this._manager._getProperties().values()) {\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n }\n for (const prop of this._manager._getPool()) {\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n }\n }\n\n /** Stop recording and return the captured log. */\n stopRecording(): QuantumOperation[] {\n this._recording = false;\n return [...this._log];\n }\n\n /** Whether recording is currently active. */\n isRecording(): boolean {\n return this._recording;\n }\n\n /** Get a copy of the current operation log (even while recording). */\n getOperationLog(): QuantumOperation[] {\n return [...this._log];\n }\n\n /**\n * Replay an operation log to recreate quantum state from scratch.\n * Clears all existing state on the manager first. Measurements are\n * forced to their recorded outcomes via forced_measure_properties.\n */\n replayLog(operations: QuantumOperation[]): void {\n // Clear manager state\n this._manager.clear();\n this._recording = false;\n this._log = [];\n this._handleToIndex.clear();\n this._nextIndex = 0;\n\n const module = getModule();\n const dimension = this._manager.dimension;\n const indexToHandle = new Map<number, QFProperty>();\n const replayPool: QFProperty[] = [];\n\n for (const entry of operations) {\n switch (entry.op) {\n case \"acquire\": {\n let prop: QFProperty;\n if (replayPool.length > 0) {\n prop = replayPool.pop()!;\n } else {\n prop = module.QuantumForge.createQuantumProperty(dimension);\n }\n indexToHandle.set(entry.index, prop);\n break;\n }\n\n case \"release\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n module.reset(prop, entry.value);\n replayPool.push(prop);\n }\n break;\n }\n\n case \"assign\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n this._manager._getProperties().set(entry.id, prop);\n }\n break;\n }\n\n case \"unassign\": {\n this._manager._getProperties().delete(entry.id);\n break;\n }\n\n case \"cycle\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.cycle(prop);\n } else {\n module.cycle(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"shift\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.shift(prop);\n } else {\n module.shift(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"i_swap\": {\n const prop1 = indexToHandle.get(entry.index1);\n const prop2 = indexToHandle.get(entry.index2);\n if (prop1 && prop2) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.i_swap(prop1, prop2, entry.fraction, preds);\n }\n break;\n }\n\n case \"clock\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.clock(prop, entry.fraction, preds);\n }\n break;\n }\n\n case \"y\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.y(prop);\n } else {\n module.y(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"hadamard\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.hadamard(prop);\n } else {\n module.hadamard(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"inverse_hadamard\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.inverse_hadamard(prop, preds);\n }\n break;\n }\n\n case \"swap\": {\n const prop1 = indexToHandle.get(entry.index1);\n const prop2 = indexToHandle.get(entry.index2);\n if (prop1 && prop2) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.swap(prop1, prop2, preds);\n }\n break;\n }\n\n case \"phase_rotate\": {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (preds) {\n module.phase_rotate(preds, entry.angle);\n }\n break;\n }\n\n case \"measure_predicate\": {\n // During replay, we don't force measure_predicate outcomes —\n // the state should be deterministic from prior forced measurements.\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (preds) {\n module.measure_predicate(preds);\n }\n break;\n }\n\n case \"reset\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n module.reset(prop, entry.value);\n }\n break;\n }\n\n case \"measure\": {\n const props = entry.indices.map((i) => indexToHandle.get(i)).filter(Boolean) as QFProperty[];\n if (props.length === entry.indices.length) {\n module.forced_measure_properties(props, entry.outcomes);\n }\n break;\n }\n }\n }\n\n // Restore internal pool from replay pool\n this._manager._setPool(replayPool);\n\n // Rebuild _handleToIndex from indexToHandle for future recording\n this._handleToIndex.clear();\n for (const [index, handle] of indexToHandle) {\n this._handleToIndex.set(handle, index);\n }\n this._nextIndex = operations.reduce((max, op) => {\n if (\"index\" in op && typeof op.index === \"number\") return Math.max(max, op.index + 1);\n if (\"index1\" in op) {\n const dualOp = op as { index1: number; index2: number };\n return Math.max(max, dualOp.index1 + 1, dualOp.index2 + 1);\n }\n if (\"indices\" in op) {\n const measureOp = op as { indices: number[] };\n const maxIdx = Math.max(...measureOp.indices);\n return Math.max(max, maxIdx + 1);\n }\n return max;\n }, 0);\n }\n\n // -- Private helpers --\n\n private _replayPredicates(\n serialized: SerializedPredicate[] | undefined,\n indexToHandle: Map<number, QFProperty>,\n ): QFPredicate[] | undefined {\n if (!serialized || serialized.length === 0) return undefined;\n const preds: QFPredicate[] = [];\n for (const sp of serialized) {\n const prop = indexToHandle.get(sp.propertyIndex);\n if (!prop) return undefined;\n preds.push(sp.isEqual ? prop.is(sp.value) : prop.is_not(sp.value));\n }\n return preds;\n }\n}\n","export {\n startBackgroundLoad,\n ensureLoaded,\n isReady,\n getModule,\n getQuantumForge,\n getVersion,\n getMaxDimension,\n getMaxQudits,\n getMaxStateSize,\n getWasmMemoryBytes,\n setWasmBasePath,\n useQuantumForgeBuild,\n getAttribution,\n registerServiceWorker,\n} from \"./QuantumForgeLoader\";\nexport { QuantumPropertyManager } from \"./QuantumPropertyManager\";\nexport type { PredicateSpec, QuantumRecorderHook } from \"./QuantumPropertyManager\";\nexport { QuantumRecorder } from \"./QuantumRecorder\";\nexport type { QuantumOperation, SerializedPredicate } from \"./QuantumOperationLog\";\nexport type { OpCode, BatchOp, BatchResult, OpNum } from \"./quantum-forge-api.mjs\";\n\n/** Numeric opcode constants for tape encoding. Matches C++ OpCode enum. */\nexport const OP = {\n CYCLE: 0, SHIFT: 1, CLOCK: 2,\n X: 3, Z: 4, Y: 5,\n HADAMARD: 6, INVERSE_HADAMARD: 7,\n SWAP: 8, I_SWAP: 9,\n PHASE_ROTATE: 10,\n ROTATE_BASIS_PAIR: 11,\n} as const;\n"],"mappings":";AAgBA,IAAI,eAAe;AAOZ,SAAS,gBAAgB,MAAoB;AAClD,iBAAe,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AAC1D;AASO,SAAS,qBAAqB,MAAoB;AACvD,MAAI,eAAe;AACjB,YAAQ;AAAA,MACN,yBAAyB,IAAI;AAAA,MAC7B;AAAA,IACF;AACA;AAAA,EACF;AACA,kBAAgB,kBAAkB,IAAI,EAAE;AAC1C;AAGA,IAAI,qBAAoD;AACxD,IAAI,cAAoC;AACxC,IAAI,gBAAgB;AACpB,IAAI,cAAc;AAGlB,IAAI;AAMG,SAAS,oBAAoB,WAAmC;AACrE,MAAI,YAAa;AACjB,gBAAc;AACd,WAAS;AAGT,QAAM,eAAe,CAAC,aAAyB;AAC7C,QAAI,OAAO,wBAAwB,YAAY;AAC7C,0BAAoB,UAAU,EAAE,SAAS,IAAK,CAAC;AAAA,IACjD,OAAO;AACL,iBAAW,UAAU,GAAG;AAAA,IAC1B;AAAA,EACF;AAEA,eAAa,MAAM;AACjB,YAAQ,OAAO,0CAA0C,oBAAoB;AAE7E,iBAAa,EAAE,MAAM,CAAC,QAAQ;AAC5B,cAAQ;AAAA,QACN,yCAAyC,KAAK,WAAW,GAAG;AAAA,QAC5D;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAOA,eAAsB,eAA8B;AAClD,MAAI,cAAe;AAEnB,MAAI,aAAa;AACf,UAAM;AACN;AAAA,EACF;AAEA,iBAAe,YAAY;AACzB,UAAM,YAAY,YAAY,IAAI;AAClC,YAAQ,OAAO,wCAAwC,oBAAoB;AAG3E,UAAM,aAAa,GAAG,YAAY;AAClC,UAAM,MAAO,MAAM;AAAA;AAAA,MAA0B;AAAA;AAC7C,yBAAqB;AAGrB,UAAM,IAAI,aAAa,WAAW;AAAA,MAChC,UAAU,CAAC,SAAiB,QAAQ,OAAO,MAAM,mBAAmB;AAAA,IACtE,CAAC;AAED,UAAM,UAAU,IAAI,aAAa,WAAW;AAC5C,UAAM,SAAS,IAAI,aAAa,gBAAgB;AAChD,UAAM,YAAY,IAAI,aAAa,aAAa;AAChD,UAAM,WAAW,YAAY,IAAI,IAAI,WAAW,QAAQ,CAAC;AAEzD,YAAQ;AAAA,MACN,kBAAkB,OAAO,aAAa,OAAO,gBAAgB,MAAM,iBAAiB,SAAS;AAAA,MAC7F;AAAA,IACF;AAEA,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,oBAAgB;AAAA,EAClB,GAAG;AAGH,cAAY,MAAM,MAAM;AACtB,kBAAc;AAAA,EAChB,CAAC;AAED,QAAM;AACR;AAKO,SAAS,UAAmB;AACjC,SAAO;AACT;AAMO,SAAS,YAAsD;AACpE,MAAI,CAAC,sBAAsB,CAAC,eAAe;AACzC,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACpF;AACA,SAAO;AACT;AAKO,SAAS,kBAAyE;AACvF,SAAO,UAAU,EAAE;AACrB;AAMO,SAAS,aAAqB;AACnC,SAAO,gBAAgB,EAAE,WAAW;AACtC;AAEO,SAAS,kBAA0B;AACxC,SAAO,gBAAgB,EAAE,gBAAgB;AAC3C;AAEO,SAAS,eAAuB;AACrC,SAAO,gBAAgB,EAAE,aAAa;AACxC;AAEO,SAAS,kBAA0B;AACxC,SAAO,gBAAgB,EAAE,gBAAgB;AAC3C;AAKO,SAAS,qBAAoC;AAClD,MAAI,CAAC,cAAe,QAAO;AAC3B,MAAI;AACF,UAAM,KAAK,gBAAgB;AAC3B,WAAO,OAAO,GAAG,mBAAmB,aAAa,GAAG,eAAe,IAAI;AAAA,EACzE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,iBAAyB;AACvC,SAAO;AACT;AASA,eAAsB,sBACpB,SAAS,wBACkC;AAC3C,MAAI,EAAE,mBAAmB,WAAY,QAAO;AAC5C,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,cAAc,SAAS,MAAM;AACzD,YAAQ,OAAO,qCAAqC,IAAI,KAAK,KAAK,oBAAoB;AACtF,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ;AAAA,MACN,uCAAuC,eAAe,QAAQ,IAAI,UAAU,GAAG;AAAA,MAC/E;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;ACpMO,IAAM,yBAAN,MAA6B;AAAA,EACzB;AAAA,EACD,aAAsC,oBAAI,IAAI;AAAA,EAC9C,OAAqB,CAAC;AAAA,EACpB;AAAA,EACF;AAAA,EAER,YAAY,UAA4D,CAAC,GAAG;AAC1E,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA;AAAA;AAAA,EAKA,YAAY,UAAiD;AAC3D,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA,EAGA,cAA+C;AAC7C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,kBAA8B;AAC5B,QAAI;AACJ,QAAI,KAAK,KAAK,SAAS,GAAG;AACxB,aAAO,KAAK,KAAK,IAAI;AAAA,IACvB,OAAO;AACL,aAAO,UAAU,EAAE,aAAa,sBAAsB,KAAK,SAAS;AAAA,IACtE;AACA,SAAK,WAAW,YAAY,IAAI;AAChC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgB,MAAkB,eAA6B;AAC7D,SAAK,WAAW,YAAY,MAAM,aAAa;AAC/C,cAAU,EAAE,MAAM,MAAM,aAAa;AACrC,SAAK,KAAK,KAAK,IAAI;AAAA,EACrB;AAAA;AAAA,EAIA,YAAY,IAAY,MAAwB;AAC9C,SAAK,WAAW,gBAAgB,IAAI,IAAI;AACxC,SAAK,WAAW,IAAI,IAAI,IAAI;AAAA,EAC9B;AAAA,EAEA,YAAY,IAAoC;AAC9C,WAAO,KAAK,WAAW,IAAI,EAAE;AAAA,EAC/B;AAAA,EAEA,eAAe,IAAkB;AAC/B,SAAK,WAAW,mBAAmB,EAAE;AACrC,SAAK,WAAW,OAAO,EAAE;AAAA,EAC3B;AAAA,EAEA,YAAY,IAAqB;AAC/B,WAAO,KAAK,WAAW,IAAI,EAAE;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,IAAkB;AAC/B,UAAM,OAAO,KAAK,WAAW,IAAI,EAAE;AACnC,QAAI,MAAM;AACR,YAAM,CAAC,KAAK,IAAI,UAAU,EAAE,mBAAmB,CAAC,IAAI,CAAC;AACrD,WAAK,gBAAgB,MAAM,KAAK;AAAA,IAClC;AACA,SAAK,eAAe,EAAE;AAAA,EACxB;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,WAAW,MAAM;AACtB,SAAK,OAAO,CAAC;AAAA,EACf;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,IAAI,WAAmB;AACrB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA,EAIA,YAA0C;AACxC,WAAO,UAAU;AAAA,EACnB;AAAA;AAAA;AAAA,EAKA,SAAS,MAA0B;AACjC,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAGA,iBAA0C;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AACF;;;ACjIO,IAAM,kBAAN,MAAqD;AAAA,EAClD,aAAa;AAAA,EACb,OAA2B,CAAC;AAAA,EAC5B,iBAA0C,oBAAI,IAAI;AAAA,EAClD,aAAa;AAAA,EACJ;AAAA,EAEjB,YAAY,SAAiC;AAC3C,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA,EAIA,UAAU,MAAwB;AAChC,QAAI,CAAC,KAAK,WAAY;AACtB,UAAM,QAAQ,KAAK;AACnB,SAAK,eAAe,IAAI,MAAM,KAAK;AACnC,SAAK,KAAK,KAAK,EAAE,IAAI,WAAW,MAAM,CAAC;AAAA,EACzC;AAAA,EAEA,UAAU,MAAkB,OAAqB;AAC/C,QAAI,CAAC,KAAK,WAAY;AACtB,UAAM,QAAQ,KAAK,eAAe,IAAI,IAAI;AAC1C,QAAI,UAAU,QAAW;AACvB,WAAK,KAAK,KAAK,EAAE,IAAI,WAAW,OAAO,MAAM,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,cAAc,IAAY,MAAwB;AAChD,QAAI,CAAC,KAAK,WAAY;AACtB,UAAM,QAAQ,KAAK,eAAe,IAAI,IAAI;AAC1C,QAAI,UAAU,QAAW;AACvB,WAAK,KAAK,KAAK,EAAE,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,iBAAiB,IAAkB;AACjC,QAAI,CAAC,KAAK,WAAY;AACtB,SAAK,KAAK,KAAK,EAAE,IAAI,YAAY,GAAG,CAAC;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,OAAuC;AACzD,WAAO,MAAM;AAAA,MAAI,CAAC,MAChB,EAAE,UAAU,EAAE,SAAS,GAAG,EAAE,KAAK,IAAI,EAAE,SAAS,OAAO,EAAE,KAAK;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,OAA2D;AAC7E,QAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,WAAO,MAAM,IAAI,CAAC,MAAM;AACtB,YAAM,QAAQ,KAAK,eAAe,IAAI,EAAE,QAAQ;AAChD,aAAO;AAAA,QACL,eAAe,SAAS;AAAA,QACxB,OAAO,EAAE;AAAA,QACT,SAAS,EAAE;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,IAA4B;AACnC,QAAI,CAAC,KAAK,WAAY;AACtB,SAAK,KAAK,KAAK,EAAE;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,MAAsC;AAC7C,WAAO,KAAK,eAAe,IAAI,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA,EAKA,iBAAuB;AACrB,SAAK,aAAa;AAClB,SAAK,OAAO,CAAC;AACb,SAAK,eAAe,MAAM;AAC1B,SAAK,aAAa;AAIlB,eAAW,QAAQ,KAAK,SAAS,eAAe,EAAE,OAAO,GAAG;AAC1D,YAAM,QAAQ,KAAK;AACnB,WAAK,eAAe,IAAI,MAAM,KAAK;AAAA,IACrC;AACA,eAAW,QAAQ,KAAK,SAAS,SAAS,GAAG;AAC3C,YAAM,QAAQ,KAAK;AACnB,WAAK,eAAe,IAAI,MAAM,KAAK;AAAA,IACrC;AAAA,EACF;AAAA;AAAA,EAGA,gBAAoC;AAClC,SAAK,aAAa;AAClB,WAAO,CAAC,GAAG,KAAK,IAAI;AAAA,EACtB;AAAA;AAAA,EAGA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,kBAAsC;AACpC,WAAO,CAAC,GAAG,KAAK,IAAI;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,YAAsC;AAE9C,SAAK,SAAS,MAAM;AACpB,SAAK,aAAa;AAClB,SAAK,OAAO,CAAC;AACb,SAAK,eAAe,MAAM;AAC1B,SAAK,aAAa;AAElB,UAAM,SAAS,UAAU;AACzB,UAAM,YAAY,KAAK,SAAS;AAChC,UAAM,gBAAgB,oBAAI,IAAwB;AAClD,UAAM,aAA2B,CAAC;AAElC,eAAW,SAAS,YAAY;AAC9B,cAAQ,MAAM,IAAI;AAAA,QAChB,KAAK,WAAW;AACd,cAAI;AACJ,cAAI,WAAW,SAAS,GAAG;AACzB,mBAAO,WAAW,IAAI;AAAA,UACxB,OAAO;AACL,mBAAO,OAAO,aAAa,sBAAsB,SAAS;AAAA,UAC5D;AACA,wBAAc,IAAI,MAAM,OAAO,IAAI;AACnC;AAAA,QACF;AAAA,QAEA,KAAK,WAAW;AACd,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,mBAAO,MAAM,MAAM,MAAM,KAAK;AAC9B,uBAAW,KAAK,IAAI;AAAA,UACtB;AACA;AAAA,QACF;AAAA,QAEA,KAAK,UAAU;AACb,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,iBAAK,SAAS,eAAe,EAAE,IAAI,MAAM,IAAI,IAAI;AAAA,UACnD;AACA;AAAA,QACF;AAAA,QAEA,KAAK,YAAY;AACf,eAAK,SAAS,eAAe,EAAE,OAAO,MAAM,EAAE;AAC9C;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,gBAAI,MAAM,aAAa,KAAK,CAAC,OAAO;AAClC,qBAAO,MAAM,IAAI;AAAA,YACnB,OAAO;AACL,qBAAO,MAAM,MAAM,MAAM,UAAU,KAAK;AAAA,YAC1C;AAAA,UACF;AACA;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,gBAAI,MAAM,aAAa,KAAK,CAAC,OAAO;AAClC,qBAAO,MAAM,IAAI;AAAA,YACnB,OAAO;AACL,qBAAO,MAAM,MAAM,MAAM,UAAU,KAAK;AAAA,YAC1C;AAAA,UACF;AACA;AAAA,QACF;AAAA,QAEA,KAAK,UAAU;AACb,gBAAM,QAAQ,cAAc,IAAI,MAAM,MAAM;AAC5C,gBAAM,QAAQ,cAAc,IAAI,MAAM,MAAM;AAC5C,cAAI,SAAS,OAAO;AAClB,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,mBAAO,OAAO,OAAO,OAAO,MAAM,UAAU,KAAK;AAAA,UACnD;AACA;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,mBAAO,MAAM,MAAM,MAAM,UAAU,KAAK;AAAA,UAC1C;AACA;AAAA,QACF;AAAA,QAEA,KAAK,KAAK;AACR,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,gBAAI,MAAM,aAAa,KAAK,CAAC,OAAO;AAClC,qBAAO,EAAE,IAAI;AAAA,YACf,OAAO;AACL,qBAAO,EAAE,MAAM,MAAM,UAAU,KAAK;AAAA,YACtC;AAAA,UACF;AACA;AAAA,QACF;AAAA,QAEA,KAAK,YAAY;AACf,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,gBAAI,MAAM,aAAa,KAAK,CAAC,OAAO;AAClC,qBAAO,SAAS,IAAI;AAAA,YACtB,OAAO;AACL,qBAAO,SAAS,MAAM,MAAM,UAAU,KAAK;AAAA,YAC7C;AAAA,UACF;AACA;AAAA,QACF;AAAA,QAEA,KAAK,oBAAoB;AACvB,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,mBAAO,iBAAiB,MAAM,KAAK;AAAA,UACrC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,QAAQ;AACX,gBAAM,QAAQ,cAAc,IAAI,MAAM,MAAM;AAC5C,gBAAM,QAAQ,cAAc,IAAI,MAAM,MAAM;AAC5C,cAAI,SAAS,OAAO;AAClB,kBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,mBAAO,KAAK,OAAO,OAAO,KAAK;AAAA,UACjC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,gBAAgB;AACnB,gBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,cAAI,OAAO;AACT,mBAAO,aAAa,OAAO,MAAM,KAAK;AAAA,UACxC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,qBAAqB;AAGxB,gBAAM,QAAQ,KAAK,kBAAkB,MAAM,YAAY,aAAa;AACpE,cAAI,OAAO;AACT,mBAAO,kBAAkB,KAAK;AAAA,UAChC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,SAAS;AACZ,gBAAM,OAAO,cAAc,IAAI,MAAM,KAAK;AAC1C,cAAI,MAAM;AACR,mBAAO,MAAM,MAAM,MAAM,KAAK;AAAA,UAChC;AACA;AAAA,QACF;AAAA,QAEA,KAAK,WAAW;AACd,gBAAM,QAAQ,MAAM,QAAQ,IAAI,CAAC,MAAM,cAAc,IAAI,CAAC,CAAC,EAAE,OAAO,OAAO;AAC3E,cAAI,MAAM,WAAW,MAAM,QAAQ,QAAQ;AACzC,mBAAO,0BAA0B,OAAO,MAAM,QAAQ;AAAA,UACxD;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,SAAS,SAAS,UAAU;AAGjC,SAAK,eAAe,MAAM;AAC1B,eAAW,CAAC,OAAO,MAAM,KAAK,eAAe;AAC3C,WAAK,eAAe,IAAI,QAAQ,KAAK;AAAA,IACvC;AACA,SAAK,aAAa,WAAW,OAAO,CAAC,KAAK,OAAO;AAC/C,UAAI,WAAW,MAAM,OAAO,GAAG,UAAU,SAAU,QAAO,KAAK,IAAI,KAAK,GAAG,QAAQ,CAAC;AACpF,UAAI,YAAY,IAAI;AAClB,cAAM,SAAS;AACf,eAAO,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,OAAO,SAAS,CAAC;AAAA,MAC3D;AACA,UAAI,aAAa,IAAI;AACnB,cAAM,YAAY;AAClB,cAAM,SAAS,KAAK,IAAI,GAAG,UAAU,OAAO;AAC5C,eAAO,KAAK,IAAI,KAAK,SAAS,CAAC;AAAA,MACjC;AACA,aAAO;AAAA,IACT,GAAG,CAAC;AAAA,EACN;AAAA;AAAA,EAIQ,kBACN,YACA,eAC2B;AAC3B,QAAI,CAAC,cAAc,WAAW,WAAW,EAAG,QAAO;AACnD,UAAM,QAAuB,CAAC;AAC9B,eAAW,MAAM,YAAY;AAC3B,YAAM,OAAO,cAAc,IAAI,GAAG,aAAa;AAC/C,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,KAAK,GAAG,UAAU,KAAK,GAAG,GAAG,KAAK,IAAI,KAAK,OAAO,GAAG,KAAK,CAAC;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AACF;;;ACjVO,IAAM,KAAK;AAAA,EAChB,OAAO;AAAA,EAAG,OAAO;AAAA,EAAG,OAAO;AAAA,EAC3B,GAAG;AAAA,EAAG,GAAG;AAAA,EAAG,GAAG;AAAA,EACf,UAAU;AAAA,EAAG,kBAAkB;AAAA,EAC/B,MAAM;AAAA,EAAG,QAAQ;AAAA,EACjB,cAAc;AAAA,EACd,mBAAmB;AACrB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/quantum/QuantumForgeLoader.ts","../../src/quantum/QuantumPropertyManager.ts","../../src/quantum/QuantumRecorder.ts","../../src/quantum/index.ts"],"sourcesContent":["/**\n * Quantum Forge Loader - Lazy loader for Quantum Forge WASM module\n *\n * Defers WASM loading from the critical path while preloading in background\n * so it's ready when needed. If not ready, callers can show loading UI.\n * \n * IMPORTANT: Quantum Forge is built from source and copied to dist/ as\n * quantum-forge-web-api.mjs (not an npm package). Run 'npm run setup' to build.\n */\n\nimport type { LoggerInterface } from \"../logging/Logger\";\n\n// Type for the quantum forge module\ntype QuantumForgeModuleType = typeof import(\"./quantum-forge-api.mjs\");\n\n// Where the WASM comes from.\n//\n// In a page it is a URL path the Vite plugin serves: \"/quantum-forge\" for the\n// default build, \"/quantum-forge-<name>\" for a variant. In Node there is no\n// server, so the loader resolves the files inside this package instead: dist/\n// for the default build, dist/quantum-forge-<name>/ for a variant. Either way\n// the consumer calls useQuantumForgeBuild() and ensureLoaded() the same way.\nlet explicitBasePath: string | null = null;\nlet variant: string | null = null;\n\n/**\n * Set the base URL path where Quantum Forge WASM files are served.\n * Default is \"/quantum-forge\" which matches the Vite plugin's serve path.\n * Consumers using the Vite plugin don't need to call this. In Node the default\n * is this package's own dist/ directory, so a file URL is only needed when the\n * WASM lives somewhere else.\n */\nexport function setWasmBasePath(path: string): void {\n explicitBasePath = path.endsWith(\"/\") ? path.slice(0, -1) : path;\n}\n\n/**\n * Select a named WASM build variant (e.g. \"qubit\").\n * In a page this loads from \"/quantum-forge-{name}\"; in Node it loads from\n * this package's dist/quantum-forge-{name}/. Replaces any earlier\n * setWasmBasePath() call.\n *\n * Must be called before `ensureLoaded()`. If the module is already loaded,\n * a warning is logged and the call is ignored.\n */\nexport function useQuantumForgeBuild(name: string): void {\n if (isInitialized) {\n logger?.warn?.(\n `useQuantumForgeBuild(\"${name}\") called after module already loaded — ignoring. Call before ensureLoaded().`,\n \"QuantumForgeLoader\",\n );\n return;\n }\n variant = name;\n explicitBasePath = null;\n}\n\n/** True when this module was loaded from disk (Node, vitest), not served over HTTP. */\nfunction runningFromDisk(): boolean {\n return (\n typeof process !== \"undefined\" &&\n typeof process.versions?.node === \"string\" &&\n import.meta.url.startsWith(\"file:\")\n );\n}\n\n/** The base path ensureLoaded() imports from, resolved at load time. */\nexport function getWasmBasePath(): string {\n if (explicitBasePath !== null) return explicitBasePath;\n if (runningFromDisk()) {\n // Built layout: dist/lib/quantum.js next to dist/quantum-forge-web-api.mjs\n // and dist/quantum-forge-<variant>/quantum-forge-web-api.mjs. Under vitest\n // the monorepo aliases straight to this .ts file in src/quantum/, two\n // levels above the same dist/.\n const dist = import.meta.url.endsWith(\".ts\") ? \"../../dist/\" : \"../\";\n const dir = variant ? `${dist}quantum-forge-${variant}/` : dist;\n return new URL(dir, import.meta.url).href.replace(/\\/$/, \"\");\n }\n return variant ? `/quantum-forge-${variant}` : \"/quantum-forge\";\n}\n\n// Cache the module and initialization state\nlet quantumForgeModule: QuantumForgeModuleType | null = null;\nlet initPromise: Promise<void> | null = null;\nlet isInitialized = false;\nlet loadStarted = false;\n\n// Logger reference (set during startBackgroundLoad)\nlet logger: LoggerInterface | undefined;\n\n/**\n * Start loading the WASM module in the background.\n * Call this after the page has rendered (e.g., after DOMContentLoaded or initial paint).\n */\nexport function startBackgroundLoad(loggerRef?: LoggerInterface): void {\n if (loadStarted) return;\n loadStarted = true;\n logger = loggerRef;\n\n // Use requestIdleCallback if available, otherwise setTimeout\n const scheduleLoad = (callback: () => void) => {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(callback, { timeout: 2000 });\n } else {\n setTimeout(callback, 100);\n }\n };\n\n scheduleLoad(() => {\n logger?.info?.(\"Starting background Quantum Forge load\", \"QuantumForgeLoader\");\n // Trigger the load but don't await - let it happen in background\n ensureLoaded().catch((err) => {\n logger?.warn?.(\n `Background Quantum Forge load failed: ${err?.message ?? err}`,\n \"QuantumForgeLoader\",\n );\n });\n });\n}\n\n/**\n * Ensure Quantum Forge is loaded and initialized.\n * Returns a promise that resolves when the module is ready.\n * Can be called multiple times - will return the same promise.\n */\nexport async function ensureLoaded(): Promise<void> {\n if (isInitialized) return;\n\n if (initPromise) {\n await initPromise;\n return;\n }\n\n initPromise = (async () => {\n const startTime = performance.now();\n logger?.info?.(\"Loading Quantum Forge WASM module...\", \"QuantumForgeLoader\");\n\n // Dynamic import of Quantum Forge WASM module from the configured base path\n const modulePath = `${getWasmBasePath()}/quantum-forge-web-api.mjs`;\n const mod = (await import(/* @vite-ignore */ modulePath)) as QuantumForgeModuleType;\n quantumForgeModule = mod;\n\n // Initialize the WASM, routing stderr through the logger\n await mod.QuantumForge.initialize({\n printErr: (text: string) => logger?.warn?.(text, \"QuantumForge/WASM\"),\n });\n\n const version = mod.QuantumForge.getVersion();\n const maxDim = mod.QuantumForge.getMaxDimension();\n const maxQudits = mod.QuantumForge.getMaxQudits();\n const elapsed = (performance.now() - startTime).toFixed(0);\n\n logger?.info?.(\n `Quantum Forge v${version} ready in ${elapsed}ms (max dim: ${maxDim}, max qudits: ${maxQudits})`,\n \"QuantumForgeLoader\",\n );\n\n console.log(\n \"%c\\u269B Powered by Quantum Forge %c quantumnative.io \",\n \"background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px 0 0 3px; font-weight: bold;\",\n \"background: #1e1b4b; color: #c7d2fe; padding: 2px 6px; border-radius: 0 3px 3px 0;\",\n );\n\n isInitialized = true;\n })();\n\n // Handle errors by clearing the promise so retry is possible\n initPromise.catch(() => {\n initPromise = null;\n });\n\n await initPromise;\n}\n\n/**\n * Check if Quantum Forge is ready to use (non-blocking).\n */\nexport function isReady(): boolean {\n return isInitialized;\n}\n\n/**\n * Get the loaded module. Throws if not loaded.\n * For synchronous access after ensuring it's loaded.\n */\nexport function getModule(): typeof import(\"./quantum-forge-api.mjs\") {\n if (!quantumForgeModule || !isInitialized) {\n throw new Error(\"QuantumForge not loaded. Call ensureLoaded() first and await it.\");\n }\n return quantumForgeModule;\n}\n\n/**\n * Get the QuantumForge class from the loaded module.\n */\nexport function getQuantumForge(): typeof import(\"./quantum-forge-api.mjs\").QuantumForge {\n return getModule().QuantumForge;\n}\n\n/**\n * Convenience re-exports for common operations.\n * These will throw if module not loaded.\n */\nexport function getVersion(): string {\n return getQuantumForge().getVersion();\n}\n\nexport function getMaxDimension(): number {\n return getQuantumForge().getMaxDimension();\n}\n\nexport function getMaxQudits(): number {\n return getQuantumForge().getMaxQudits();\n}\n\nexport function getMaxStateSize(): number {\n return getQuantumForge().getMaxStateSize();\n}\n\n/**\n * Get the WASM memory bytes (for analytics).\n */\nexport function getWasmMemoryBytes(): number | null {\n if (!isInitialized) return null;\n try {\n const qf = getQuantumForge() as any;\n return typeof qf.getMemoryBytes === \"function\" ? qf.getMemoryBytes() : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the required attribution text for display in your application.\n * Include this in a user-visible location (credits screen, about page, etc.).\n */\nexport function getAttribution(): string {\n return \"Powered by Quantum Forge \\u2014 \\u00A9 Quantum Native \\u2014 quantumnative.io\";\n}\n\n/**\n * Register the Quantum Forge service worker for offline WASM caching.\n * Call once from your game controller after page load.\n * The SW caches WASM artifacts on first fetch so subsequent loads work offline.\n *\n * @param swPath - Path to the service worker file. Default: \"/quantum-forge-sw.js\"\n */\nexport async function registerServiceWorker(\n swPath = \"/quantum-forge-sw.js\",\n): Promise<ServiceWorkerRegistration | null> {\n if (!(\"serviceWorker\" in navigator)) return null;\n try {\n const reg = await navigator.serviceWorker.register(swPath);\n logger?.info?.(`Service worker registered (scope: ${reg.scope})`, \"QuantumForgeLoader\");\n return reg;\n } catch (err) {\n logger?.warn?.(\n `Service worker registration failed: ${err instanceof Error ? err.message : err}`,\n \"QuantumForgeLoader\",\n );\n return null;\n }\n}\n","/**\n * QuantumPropertyManager — manages quantum property lifecycles.\n *\n * Handles the common pattern of acquiring, pooling, and releasing WASM\n * QuantumProperty handles. Games either extend this class or compose it\n * to add game-specific quantum operations via getModule().\n *\n * Property pooling is critical: measured/removed properties are recycled\n * to avoid growing the tensor product and hitting qudit limits.\n *\n * For opt-in operation recording, attach a QuantumRecorder via setRecorder().\n */\n\nimport { getModule } from \"./QuantumForgeLoader\";\nimport type { LoggerInterface } from \"../logging/Logger\";\nimport type { QuantumProperty as QFProperty } from \"./quantum-forge-api.mjs\";\n\nexport interface PredicateSpec {\n property: QFProperty;\n value: number;\n isEqual: boolean;\n}\n\nexport interface QuantumRecorderHook {\n onAcquire?(prop: QFProperty): void;\n onRelease?(prop: QFProperty, value: number): void;\n onSetProperty?(id: string, prop: QFProperty): void;\n onDeleteProperty?(id: string): void;\n}\n\nexport class QuantumPropertyManager {\n readonly dimension: number;\n private properties: Map<string, QFProperty> = new Map();\n private pool: QFProperty[] = [];\n protected logger?: LoggerInterface;\n private _recorder?: QuantumRecorderHook;\n\n constructor(options: { dimension?: number; logger?: LoggerInterface } = {}) {\n this.dimension = options.dimension ?? 2;\n this.logger = options.logger;\n }\n\n // -- Recorder hook --\n\n /** Attach an optional recorder for operation logging. */\n setRecorder(recorder: QuantumRecorderHook | undefined): void {\n this._recorder = recorder;\n }\n\n /** Get the currently attached recorder, if any. */\n getRecorder(): QuantumRecorderHook | undefined {\n return this._recorder;\n }\n\n // -- Property lifecycle --\n\n /**\n * Get a property at |0⟩ — reuses a pooled one if available,\n * otherwise creates a fresh standalone property.\n */\n acquireProperty(): QFProperty {\n let prop: QFProperty;\n if (this.pool.length > 0) {\n prop = this.pool.pop()!;\n } else {\n prop = getModule().QuantumForge.createQuantumProperty(this.dimension);\n }\n this._recorder?.onAcquire?.(prop);\n return prop;\n }\n\n /**\n * Return a property to the pool after resetting it to |0⟩.\n * Uses the `reset` primitive which applies non-fractional cycles —\n * correct for all dimensions (no superposition created).\n */\n releaseProperty(prop: QFProperty, measuredValue: number): void {\n this._recorder?.onRelease?.(prop, measuredValue);\n getModule().reset(prop, measuredValue);\n this.pool.push(prop);\n }\n\n // -- ID mapping --\n\n setProperty(id: string, prop: QFProperty): void {\n this._recorder?.onSetProperty?.(id, prop);\n this.properties.set(id, prop);\n }\n\n getProperty(id: string): QFProperty | undefined {\n return this.properties.get(id);\n }\n\n deleteProperty(id: string): void {\n this._recorder?.onDeleteProperty?.(id);\n this.properties.delete(id);\n }\n\n hasProperty(id: string): boolean {\n return this.properties.has(id);\n }\n\n // -- Public operations --\n\n /**\n * Remove a property by ID: measure it, pool the handle, delete the mapping.\n */\n removeProperty(id: string): void {\n const prop = this.properties.get(id);\n if (prop) {\n const [value] = getModule().measure_properties([prop]);\n this.releaseProperty(prop, value);\n }\n this.deleteProperty(id);\n }\n\n /** Clear all properties, pool, and recorder. */\n clear(): void {\n this.properties.clear();\n this.pool = [];\n }\n\n get size(): number {\n return this.properties.size;\n }\n\n get poolSize(): number {\n return this.pool.length;\n }\n\n // -- WASM module access --\n\n getModule(): ReturnType<typeof getModule> {\n return getModule();\n }\n\n // -- Internal access for QuantumRecorder replay --\n\n /** @internal — used by QuantumRecorder.replayLog() to restore pool state. */\n _setPool(pool: QFProperty[]): void {\n this.pool = pool;\n }\n\n /** @internal — used by QuantumRecorder to enumerate live handles. */\n _getProperties(): Map<string, QFProperty> {\n return this.properties;\n }\n\n /** @internal — used by QuantumRecorder to enumerate pool handles. */\n _getPool(): QFProperty[] {\n return this.pool;\n }\n}\n","/**\n * QuantumRecorder — opt-in recording and replay of quantum operations.\n *\n * Attach to a QuantumPropertyManager via `manager.setRecorder(recorder)`.\n * When recording is active, lifecycle hooks log every state-mutating\n * operation. The log can be replayed via replayLog() to recreate\n * identical quantum state — measurements are forced to their recorded\n * outcomes using forced_measure_properties.\n *\n * For gate recording, call wrapGate() around each WASM gate call.\n */\n\nimport { getModule } from \"./QuantumForgeLoader\";\nimport type { QuantumPropertyManager, QuantumRecorderHook } from \"./QuantumPropertyManager\";\nimport type { QuantumOperation, SerializedPredicate } from \"./QuantumOperationLog\";\nimport type { QuantumProperty as QFProperty, Predicate as QFPredicate } from \"./quantum-forge-api.mjs\";\n\nexport interface PredicateSpec {\n property: QFProperty;\n value: number;\n isEqual: boolean;\n}\n\nexport class QuantumRecorder implements QuantumRecorderHook {\n private _recording = false;\n private _log: QuantumOperation[] = [];\n private _handleToIndex: Map<QFProperty, number> = new Map();\n private _nextIndex = 0;\n private readonly _manager: QuantumPropertyManager;\n\n constructor(manager: QuantumPropertyManager) {\n this._manager = manager;\n }\n\n // -- QuantumRecorderHook implementation --\n\n onAcquire(prop: QFProperty): void {\n if (!this._recording) return;\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n this._log.push({ op: \"acquire\", index });\n }\n\n onRelease(prop: QFProperty, value: number): void {\n if (!this._recording) return;\n const index = this._handleToIndex.get(prop);\n if (index !== undefined) {\n this._log.push({ op: \"release\", index, value });\n }\n }\n\n onSetProperty(id: string, prop: QFProperty): void {\n if (!this._recording) return;\n const index = this._handleToIndex.get(prop);\n if (index !== undefined) {\n this._log.push({ op: \"assign\", index, id });\n }\n }\n\n onDeleteProperty(id: string): void {\n if (!this._recording) return;\n this._log.push({ op: \"unassign\", id });\n }\n\n // -- Gate recording --\n\n /**\n * Build WASM predicate objects from PredicateSpec array.\n */\n buildWasmPredicates(specs: PredicateSpec[]): QFPredicate[] {\n return specs.map((s) =>\n s.isEqual ? s.property.is(s.value) : s.property.is_not(s.value),\n );\n }\n\n /**\n * Serialize predicates for the operation log.\n */\n serializePredicates(specs: PredicateSpec[]): SerializedPredicate[] | undefined {\n if (specs.length === 0) return undefined;\n return specs.map((s) => {\n const index = this._handleToIndex.get(s.property);\n return {\n propertyIndex: index ?? -1,\n value: s.value,\n isEqual: s.isEqual,\n };\n });\n }\n\n /**\n * Record a gate operation. Call this when recording is active\n * and you want to log a gate call for replay.\n */\n recordOp(op: QuantumOperation): void {\n if (!this._recording) return;\n this._log.push(op);\n }\n\n /**\n * Get the recorded index for a property handle.\n */\n getIndex(prop: QFProperty): number | undefined {\n return this._handleToIndex.get(prop);\n }\n\n // -- Recording API --\n\n /** Begin recording quantum operations. Resets any existing log. */\n startRecording(): void {\n this._recording = true;\n this._log = [];\n this._handleToIndex.clear();\n this._nextIndex = 0;\n\n // Assign indices to all currently-live handles so operations\n // on pre-existing properties are tracked correctly.\n for (const prop of this._manager._getProperties().values()) {\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n }\n for (const prop of this._manager._getPool()) {\n const index = this._nextIndex++;\n this._handleToIndex.set(prop, index);\n }\n }\n\n /** Stop recording and return the captured log. */\n stopRecording(): QuantumOperation[] {\n this._recording = false;\n return [...this._log];\n }\n\n /** Whether recording is currently active. */\n isRecording(): boolean {\n return this._recording;\n }\n\n /** Get a copy of the current operation log (even while recording). */\n getOperationLog(): QuantumOperation[] {\n return [...this._log];\n }\n\n /**\n * Replay an operation log to recreate quantum state from scratch.\n * Clears all existing state on the manager first. Measurements are\n * forced to their recorded outcomes via forced_measure_properties.\n */\n replayLog(operations: QuantumOperation[]): void {\n // Clear manager state\n this._manager.clear();\n this._recording = false;\n this._log = [];\n this._handleToIndex.clear();\n this._nextIndex = 0;\n\n const module = getModule();\n const dimension = this._manager.dimension;\n const indexToHandle = new Map<number, QFProperty>();\n const replayPool: QFProperty[] = [];\n\n for (const entry of operations) {\n switch (entry.op) {\n case \"acquire\": {\n let prop: QFProperty;\n if (replayPool.length > 0) {\n prop = replayPool.pop()!;\n } else {\n prop = module.QuantumForge.createQuantumProperty(dimension);\n }\n indexToHandle.set(entry.index, prop);\n break;\n }\n\n case \"release\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n module.reset(prop, entry.value);\n replayPool.push(prop);\n }\n break;\n }\n\n case \"assign\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n this._manager._getProperties().set(entry.id, prop);\n }\n break;\n }\n\n case \"unassign\": {\n this._manager._getProperties().delete(entry.id);\n break;\n }\n\n case \"cycle\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.cycle(prop);\n } else {\n module.cycle(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"shift\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.shift(prop);\n } else {\n module.shift(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"i_swap\": {\n const prop1 = indexToHandle.get(entry.index1);\n const prop2 = indexToHandle.get(entry.index2);\n if (prop1 && prop2) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.i_swap(prop1, prop2, entry.fraction, preds);\n }\n break;\n }\n\n case \"clock\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.clock(prop, entry.fraction, preds);\n }\n break;\n }\n\n case \"y\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.y(prop);\n } else {\n module.y(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"hadamard\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (entry.fraction === 1 && !preds) {\n module.hadamard(prop);\n } else {\n module.hadamard(prop, entry.fraction, preds);\n }\n }\n break;\n }\n\n case \"inverse_hadamard\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.inverse_hadamard(prop, preds);\n }\n break;\n }\n\n case \"swap\": {\n const prop1 = indexToHandle.get(entry.index1);\n const prop2 = indexToHandle.get(entry.index2);\n if (prop1 && prop2) {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n module.swap(prop1, prop2, preds);\n }\n break;\n }\n\n case \"phase_rotate\": {\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (preds) {\n module.phase_rotate(preds, entry.angle);\n }\n break;\n }\n\n case \"measure_predicate\": {\n // During replay, we don't force measure_predicate outcomes —\n // the state should be deterministic from prior forced measurements.\n const preds = this._replayPredicates(entry.predicates, indexToHandle);\n if (preds) {\n module.measure_predicate(preds);\n }\n break;\n }\n\n case \"reset\": {\n const prop = indexToHandle.get(entry.index);\n if (prop) {\n module.reset(prop, entry.value);\n }\n break;\n }\n\n case \"measure\": {\n const props = entry.indices.map((i) => indexToHandle.get(i)).filter(Boolean) as QFProperty[];\n if (props.length === entry.indices.length) {\n module.forced_measure_properties(props, entry.outcomes);\n }\n break;\n }\n }\n }\n\n // Restore internal pool from replay pool\n this._manager._setPool(replayPool);\n\n // Rebuild _handleToIndex from indexToHandle for future recording\n this._handleToIndex.clear();\n for (const [index, handle] of indexToHandle) {\n this._handleToIndex.set(handle, index);\n }\n this._nextIndex = operations.reduce((max, op) => {\n if (\"index\" in op && typeof op.index === \"number\") return Math.max(max, op.index + 1);\n if (\"index1\" in op) {\n const dualOp = op as { index1: number; index2: number };\n return Math.max(max, dualOp.index1 + 1, dualOp.index2 + 1);\n }\n if (\"indices\" in op) {\n const measureOp = op as { indices: number[] };\n const maxIdx = Math.max(...measureOp.indices);\n return Math.max(max, maxIdx + 1);\n }\n return max;\n }, 0);\n }\n\n // -- Private helpers --\n\n private _replayPredicates(\n serialized: SerializedPredicate[] | undefined,\n indexToHandle: Map<number, QFProperty>,\n ): QFPredicate[] | undefined {\n if (!serialized || serialized.length === 0) return undefined;\n const preds: QFPredicate[] = [];\n for (const sp of serialized) {\n const prop = indexToHandle.get(sp.propertyIndex);\n if (!prop) return undefined;\n preds.push(sp.isEqual ? prop.is(sp.value) : prop.is_not(sp.value));\n }\n return preds;\n }\n}\n","export {\n startBackgroundLoad,\n ensureLoaded,\n isReady,\n getModule,\n getQuantumForge,\n getVersion,\n getMaxDimension,\n getMaxQudits,\n getMaxStateSize,\n getWasmMemoryBytes,\n setWasmBasePath,\n getWasmBasePath,\n useQuantumForgeBuild,\n getAttribution,\n registerServiceWorker,\n} from \"./QuantumForgeLoader\";\nexport { QuantumPropertyManager } from \"./QuantumPropertyManager\";\nexport type { PredicateSpec, QuantumRecorderHook } from \"./QuantumPropertyManager\";\nexport { QuantumRecorder } from \"./QuantumRecorder\";\nexport type { QuantumOperation, SerializedPredicate } from \"./QuantumOperationLog\";\nexport type { OpCode, BatchOp, BatchResult, OpNum } from \"./quantum-forge-api.mjs\";\n\n/** Numeric opcode constants for tape encoding. Matches C++ OpCode enum. */\nexport const OP = {\n CYCLE: 0, SHIFT: 1, CLOCK: 2,\n X: 3, Z: 4, Y: 5,\n HADAMARD: 6, INVERSE_HADAMARD: 7,\n SWAP: 8, I_SWAP: 9,\n PHASE_ROTATE: 10,\n ROTATE_BASIS_PAIR: 11,\n} as const;\n"],"mappings":"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","names":[]}
|
|
@@ -21,7 +21,7 @@ interface QuantumForgePluginOptions {
|
|
|
21
21
|
*
|
|
22
22
|
* Usage:
|
|
23
23
|
* ```ts
|
|
24
|
-
* import { quantumForgeVitePlugin } from "
|
|
24
|
+
* import { quantumForgeVitePlugin } from "quantum-forge/vite-plugin";
|
|
25
25
|
* export default defineConfig({
|
|
26
26
|
* plugins: [quantumForgeVitePlugin()],
|
|
27
27
|
* });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vite-plugin.ts"],"sourcesContent":["import { type Plugin } from \"vite\";\nimport { resolve, join } from \"path\";\nimport fs from \"fs\";\n\nexport interface QuantumForgePluginOptions {\n /**\n * Directory containing the WASM build artifacts.\n * Default: \"dist\" (relative to project root)\n */\n wasmDir?: string;\n\n /**\n * URL path prefix where WASM files are served during dev.\n * Default: \"/quantum-forge\"\n */\n servePath?: string;\n}\n\n/**\n * Vite plugin that serves Quantum Forge WASM artifacts during development.\n *\n * During dev, intercepts requests to `servePath/*` and serves matching\n * `quantum-forge-web-*` files from `wasmDir/`. Also excludes the WASM\n * module from Vite's dependency optimization.\n *\n * Usage:\n * ```ts\n * import { quantumForgeVitePlugin } from \"
|
|
1
|
+
{"version":3,"sources":["../../src/vite-plugin.ts"],"sourcesContent":["import { type Plugin } from \"vite\";\nimport { resolve, join } from \"path\";\nimport fs from \"fs\";\n\nexport interface QuantumForgePluginOptions {\n /**\n * Directory containing the WASM build artifacts.\n * Default: \"dist\" (relative to project root)\n */\n wasmDir?: string;\n\n /**\n * URL path prefix where WASM files are served during dev.\n * Default: \"/quantum-forge\"\n */\n servePath?: string;\n}\n\n/**\n * Vite plugin that serves Quantum Forge WASM artifacts during development.\n *\n * During dev, intercepts requests to `servePath/*` and serves matching\n * `quantum-forge-web-*` files from `wasmDir/`. Also excludes the WASM\n * module from Vite's dependency optimization.\n *\n * Usage:\n * ```ts\n * import { quantumForgeVitePlugin } from \"quantum-forge/vite-plugin\";\n * export default defineConfig({\n * plugins: [quantumForgeVitePlugin()],\n * });\n * ```\n */\nexport function quantumForgeVitePlugin(\n options: QuantumForgePluginOptions = {},\n): Plugin {\n const wasmDir = options.wasmDir ?? \"dist\";\n const servePath = options.servePath ?? \"/quantum-forge\";\n const prefix = servePath.endsWith(\"/\") ? servePath : servePath + \"/\";\n\n return {\n name: \"quantum-forge\",\n\n config() {\n return {\n optimizeDeps: {\n exclude: [\"quantum-forge-web-api.mjs\"],\n },\n };\n },\n\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n if (!req.url) return next();\n\n // Strip query string for matching\n const urlPath = req.url.split(\"?\")[0];\n\n // Match requests under the serve path (default and variant builds).\n // Default: /quantum-forge/file → dist/file\n // Variant: /quantum-forge-{name}/file → dist/quantum-forge-{name}/file\n if (!urlPath.startsWith(prefix) && !urlPath.startsWith(servePath + \"-\"))\n return next();\n\n let filename: string;\n let subdir: string;\n\n if (urlPath.startsWith(prefix)) {\n // Default build: /quantum-forge/file\n filename = urlPath.slice(prefix.length);\n subdir = \"\";\n } else {\n // Variant build: /quantum-forge-{name}/file\n // Extract variant name from path segment after servePath-\n const rest = urlPath.slice(servePath.length + 1); // strip \"/quantum-forge-\"\n const slashIdx = rest.indexOf(\"/\");\n if (slashIdx === -1) return next();\n const variantName = rest.slice(0, slashIdx);\n filename = rest.slice(slashIdx + 1);\n subdir = `quantum-forge-${variantName}`;\n }\n\n // Only serve quantum-forge files\n if (!filename.startsWith(\"quantum-forge-web-\")) return next();\n\n // Check project-local dist/ first, then fall back to the framework's\n // dist/ inside node_modules (consumer installs via registry)\n const localDir = subdir\n ? resolve(process.cwd(), wasmDir, subdir)\n : resolve(process.cwd(), wasmDir);\n const nmPkgNames = [\"quantum-forge\", \"quantum-forge\"];\n const candidates = [resolve(localDir, filename)];\n for (const pkg of nmPkgNames) {\n const nmDir = subdir\n ? resolve(process.cwd(), \"node_modules\", ...pkg.split(\"/\"), \"dist\", subdir)\n : resolve(process.cwd(), \"node_modules\", ...pkg.split(\"/\"), \"dist\");\n candidates.push(resolve(nmDir, filename));\n }\n const filePath = candidates.find((p) => fs.existsSync(p));\n if (!filePath) return next();\n\n const ext = filename.split(\".\").pop();\n const mimeTypes: Record<string, string> = {\n mjs: \"application/javascript\",\n wasm: \"application/wasm\",\n mts: \"application/javascript\",\n };\n res.setHeader(\n \"Content-Type\",\n mimeTypes[ext || \"\"] || \"application/octet-stream\",\n );\n fs.createReadStream(filePath).pipe(res);\n });\n },\n\n writeBundle(options) {\n const outDir = options.dir ?? resolve(process.cwd(), \"dist\");\n\n const wasmFiles = [\n \"quantum-forge-web-esm.mjs\",\n \"quantum-forge-web-esm.wasm\",\n \"quantum-forge-web-api.mjs\",\n ];\n\n const localBase = resolve(process.cwd(), wasmDir);\n const nmBases = [\n resolve(process.cwd(), \"node_modules\", \"quantum-forge\", \"dist\"),\n resolve(process.cwd(), \"node_modules\", \"@quantum-native\", \"quantum-forge\", \"dist\"),\n ];\n const allBases = [localBase, ...nmBases];\n\n // Copy a set of WASM files from a source dir to a dest dir\n const copyBuild = (srcDirs: string[], destDir: string) => {\n const resolved = wasmFiles\n .map((f) => {\n for (const dir of srcDirs) {\n const p = resolve(dir, f);\n if (fs.existsSync(p)) return p;\n }\n return null;\n })\n .filter((p): p is string => p !== null);\n\n if (resolved.length === 0) return;\n\n fs.mkdirSync(destDir, { recursive: true });\n for (const src of resolved) {\n const filename = src.split(\"/\").pop()!;\n fs.copyFileSync(src, join(destDir, filename));\n }\n };\n\n // Copy default build\n copyBuild(allBases, join(outDir, \"quantum-forge\"));\n\n // Copy any variant builds (e.g. quantum-forge-qubit/)\n for (const base of allBases) {\n if (!fs.existsSync(base)) continue;\n for (const entry of fs.readdirSync(base, { withFileTypes: true })) {\n if (entry.isDirectory() && entry.name.startsWith(\"quantum-forge-\")) {\n const variantSrc = resolve(base, entry.name);\n copyBuild([variantSrc], join(outDir, entry.name));\n }\n }\n }\n },\n };\n}\n"],"mappings":"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","names":["options"]}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quantum-forge",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "Quantum Forge WASM loader, property manager, and Vite plugin for quantum game development (Qutrit Edition d3n12 + Qubit Edition d2n20).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,8 +17,12 @@
|
|
|
17
17
|
"import": "./dist/lib/vite-plugin.js"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"quantum-forge": "scripts/cli.mjs"
|
|
22
|
+
},
|
|
20
23
|
"files": [
|
|
21
24
|
"dist/",
|
|
25
|
+
"scripts/cli.mjs",
|
|
22
26
|
"scripts/prepare.mjs",
|
|
23
27
|
"scripts/copy-quantum-forge.mjs",
|
|
24
28
|
"scripts/build-quantum-forge-variant.mjs",
|
|
@@ -28,7 +32,7 @@
|
|
|
28
32
|
],
|
|
29
33
|
"scripts": {
|
|
30
34
|
"prepare": "node scripts/prepare.mjs",
|
|
31
|
-
"prepublishOnly": "node scripts/stamp-service-worker.mjs",
|
|
35
|
+
"prepublishOnly": "node scripts/stamp-service-worker.mjs && node ../../scripts/finish-public-package.mjs",
|
|
32
36
|
"build:lib": "tsup",
|
|
33
37
|
"build:quantum-forge": "cd ../../wrappers/web && npm install && npm run build:medium && cd ../../framework/packages/core && node scripts/copy-quantum-forge.mjs",
|
|
34
38
|
"build:quantum-forge:small": "cd ../../wrappers/web && npm install && npm run build:small && cd ../../framework/packages/core && node scripts/copy-quantum-forge.mjs",
|
|
@@ -60,12 +64,12 @@
|
|
|
60
64
|
"url": "https://github.com/quantum-native/quantum-forge.git"
|
|
61
65
|
},
|
|
62
66
|
"author": "Chris Cantwell",
|
|
63
|
-
"license": "
|
|
67
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
64
68
|
"publishConfig": {
|
|
65
69
|
"registry": "https://registry.npmjs.org",
|
|
66
70
|
"access": "public"
|
|
67
71
|
},
|
|
68
72
|
"engines": {
|
|
69
|
-
"node": ">=
|
|
73
|
+
"node": ">=22.0.0"
|
|
70
74
|
}
|
|
71
75
|
}
|
package/quantum-forge-sw.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* `quantum-forge init` copies this file to your project's public/ directory.
|
|
7
7
|
* For an existing project, copy it yourself:
|
|
8
8
|
*
|
|
9
|
-
* cp node_modules
|
|
9
|
+
* cp node_modules/quantum-forge/quantum-forge-sw.js public/
|
|
10
10
|
*
|
|
11
11
|
* Then register it from your game controller:
|
|
12
12
|
*
|
|
13
|
-
* import { registerServiceWorker } from "
|
|
13
|
+
* import { registerServiceWorker } from "quantum-forge/quantum";
|
|
14
14
|
* await registerServiceWorker();
|
|
15
15
|
*
|
|
16
16
|
* Or manually: navigator.serviceWorker.register("/quantum-forge-sw.js");
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// published. An unprocessed copy falls back to "dev", which still works — the
|
|
21
21
|
// fetch handler revalidates in the background, so a deployment cannot pin old
|
|
22
22
|
// artifacts forever even when the cache name never changes.
|
|
23
|
-
const QF_VERSION = "2.
|
|
23
|
+
const QF_VERSION = "2.7.1";
|
|
24
24
|
|
|
25
25
|
// Scope the cache to this registration so two Quantum Forge games served from
|
|
26
26
|
// the same origin (e.g. /game1/ and /game2/ on GitHub Pages) cannot delete
|
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `quantum-forge` command.
|
|
5
|
+
*
|
|
6
|
+
* The CLI itself (init, add-system, validate, doctor) lives in the engine
|
|
7
|
+
* package: scaffolding needs the engine's templates and its vendored example.
|
|
8
|
+
* npx resolves a command by package name, so `npx quantum-forge init` fetched
|
|
9
|
+
* this package, found no executable, and died with "could not determine
|
|
10
|
+
* executable to run". This file is that executable. It hands every argument
|
|
11
|
+
* to the engine CLI:
|
|
12
|
+
*
|
|
13
|
+
* 1. An engine installed next to this package (a project's node_modules,
|
|
14
|
+
* or the monorepo checkout) runs in this process.
|
|
15
|
+
* 2. Otherwise `npm exec` fetches the engine and runs its CLI, exactly as
|
|
16
|
+
* `npx quantum-forge-engine ...` would.
|
|
17
|
+
*
|
|
18
|
+
* Both spellings therefore work everywhere:
|
|
19
|
+
*
|
|
20
|
+
* npx quantum-forge init my-game
|
|
21
|
+
* npx quantum-forge-engine init my-game
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import fs from "fs";
|
|
25
|
+
import path from "path";
|
|
26
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
27
|
+
import { spawnSync } from "child_process";
|
|
28
|
+
|
|
29
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const PACKAGE_DIR = path.resolve(__dirname, "..");
|
|
31
|
+
|
|
32
|
+
function readPackageName() {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_DIR, "package.json"), "utf-8"));
|
|
35
|
+
if (typeof pkg.name === "string" && pkg.name) return pkg.name;
|
|
36
|
+
} catch {
|
|
37
|
+
// fall through to the public name
|
|
38
|
+
}
|
|
39
|
+
return "quantum-forge";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// "quantum-forge" -> "quantum-forge-engine", and the scoped name to its scoped
|
|
43
|
+
// sibling, so a GitHub Packages install never reaches for the public registry.
|
|
44
|
+
const ENGINE = `${readPackageName()}-engine`;
|
|
45
|
+
const ENGINE_CLI = path.join("scripts", "init.mjs");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The engine's CLI script, if an engine is installed where this command can
|
|
49
|
+
* see it. The engine's `exports` map hides scripts/ from import resolution,
|
|
50
|
+
* so this walks the filesystem the way Node's resolver would.
|
|
51
|
+
*/
|
|
52
|
+
function findLocalEngineCli() {
|
|
53
|
+
const candidates = [];
|
|
54
|
+
|
|
55
|
+
// The project the command runs in, walking up to the filesystem root.
|
|
56
|
+
let dir = process.cwd();
|
|
57
|
+
for (;;) {
|
|
58
|
+
candidates.push(path.join(dir, "node_modules", ENGINE, ENGINE_CLI));
|
|
59
|
+
const parent = path.dirname(dir);
|
|
60
|
+
if (parent === dir) break;
|
|
61
|
+
dir = parent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The node_modules this package sits in: one level up for an unscoped
|
|
65
|
+
// install, two for a scoped one.
|
|
66
|
+
const scoped = path.basename(path.dirname(PACKAGE_DIR)).startsWith("@");
|
|
67
|
+
const modulesRoot = scoped ? path.dirname(path.dirname(PACKAGE_DIR)) : path.dirname(PACKAGE_DIR);
|
|
68
|
+
candidates.push(path.join(modulesRoot, ENGINE, ENGINE_CLI));
|
|
69
|
+
|
|
70
|
+
// The monorepo checkout: packages/core next to packages/engine.
|
|
71
|
+
candidates.push(path.join(path.dirname(PACKAGE_DIR), "engine", ENGINE_CLI));
|
|
72
|
+
|
|
73
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* How to invoke npm without a shell, so there is nothing to quote on Windows.
|
|
78
|
+
* npm and npx set `npm_execpath` for the processes they launch; pnpm and Yarn
|
|
79
|
+
* set it too, pointing at their own CLIs, which do not understand `npm exec`,
|
|
80
|
+
* so only npm's own files are accepted. Without it, npm sits next to the Node
|
|
81
|
+
* binary (or under libexec for Homebrew). On Unix the `npm` on PATH is a plain
|
|
82
|
+
* script, so it can be spawned by name as a last resort; on Windows it is a
|
|
83
|
+
* .cmd file that needs a shell, which is exactly what this avoids.
|
|
84
|
+
*/
|
|
85
|
+
function npmCommand() {
|
|
86
|
+
const candidates = [];
|
|
87
|
+
const execpath = process.env.npm_execpath;
|
|
88
|
+
if (execpath) {
|
|
89
|
+
const base = path.basename(execpath);
|
|
90
|
+
if (base === "npm-cli.js") candidates.push(execpath);
|
|
91
|
+
if (base === "npx-cli.js") candidates.push(path.join(path.dirname(execpath), "npm-cli.js"));
|
|
92
|
+
}
|
|
93
|
+
const nodeDir = path.dirname(process.execPath);
|
|
94
|
+
candidates.push(
|
|
95
|
+
// Windows: node.exe and node_modules/npm side by side.
|
|
96
|
+
path.join(nodeDir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
97
|
+
// Unix: bin/node next to lib/node_modules/npm.
|
|
98
|
+
path.join(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
|
|
99
|
+
// Homebrew: the Cellar keeps npm under libexec.
|
|
100
|
+
path.join(nodeDir, "..", "libexec", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
|
|
101
|
+
);
|
|
102
|
+
const cli = candidates.find((candidate) => fs.existsSync(candidate));
|
|
103
|
+
if (cli) return { file: process.execPath, prefix: [cli] };
|
|
104
|
+
if (process.platform !== "win32") return { file: "npm", prefix: [] };
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const args = process.argv.slice(2);
|
|
109
|
+
const localCli = findLocalEngineCli();
|
|
110
|
+
|
|
111
|
+
if (localCli) {
|
|
112
|
+
// Same process, same argv: the engine CLI reads process.argv itself.
|
|
113
|
+
await import(pathToFileURL(localCli).href);
|
|
114
|
+
} else {
|
|
115
|
+
const direct = `npx ${ENGINE} ${args.join(" ")}`.trim();
|
|
116
|
+
const npm = npmCommand();
|
|
117
|
+
if (!npm) {
|
|
118
|
+
process.stderr.write(
|
|
119
|
+
`quantum-forge: the CLI ships in ${ENGINE}, and npm could not be located to fetch it.\n` +
|
|
120
|
+
`Run it directly: ${direct}\n`,
|
|
121
|
+
);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
process.stderr.write(`quantum-forge: the CLI ships in ${ENGINE}; fetching it with npm exec...\n`);
|
|
125
|
+
// `npm exec -- <pkg> [args]` runs the package's only bin, whatever it is
|
|
126
|
+
// named, and downloads the package first when it is not installed.
|
|
127
|
+
const result = spawnSync(npm.file, [...npm.prefix, "exec", "--yes", "--", ENGINE, ...args], {
|
|
128
|
+
stdio: "inherit",
|
|
129
|
+
});
|
|
130
|
+
if (result.error) {
|
|
131
|
+
process.stderr.write(
|
|
132
|
+
`quantum-forge: could not run npm (${result.error.message}).\nRun it directly: ${direct}\n`,
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
process.exit(result.status ?? 1);
|
|
137
|
+
}
|
package/scripts/prepare.mjs
CHANGED
|
@@ -63,6 +63,18 @@ function buildLib() {
|
|
|
63
63
|
log(" TypeScript library built.", "green");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// ─── Public names ────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
function finishPublicPackage() {
|
|
69
|
+
const finisher = path.resolve(ROOT, "..", "..", "scripts", "finish-public-package.mjs");
|
|
70
|
+
if (!fs.existsSync(finisher)) return;
|
|
71
|
+
const result = spawnSync(process.execPath, [finisher, ROOT], { stdio: "inherit" });
|
|
72
|
+
if (result.status !== 0) {
|
|
73
|
+
log(" Public-name check failed.", "red");
|
|
74
|
+
process.exit(result.status ?? 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
// ─── Dev detection ───────────────────────────────────────────────────────────
|
|
67
79
|
|
|
68
80
|
function isDevRepo() {
|
|
@@ -282,6 +294,10 @@ async function main() {
|
|
|
282
294
|
if (isDevRepo()) {
|
|
283
295
|
log(" Dev repository detected (monorepo).", "dim");
|
|
284
296
|
buildLib();
|
|
297
|
+
// `prepare` runs after prepublishOnly during `npm publish`, so a public
|
|
298
|
+
// publish's rewritten declarations have just been rebuilt with the scoped
|
|
299
|
+
// names. Redo the rewrite (and its check) when the finisher exists.
|
|
300
|
+
finishPublicPackage();
|
|
285
301
|
return;
|
|
286
302
|
}
|
|
287
303
|
|