@mulanjs/mulanjs 1.0.1-dev.20260212143840
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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/compiler/compiler.js +90 -0
- package/dist/compiler/script-compiler.js +314 -0
- package/dist/compiler/sfc-parser.js +93 -0
- package/dist/compiler/style-compiler.js +56 -0
- package/dist/compiler/template-compiler.js +442 -0
- package/dist/components/bloch-sphere.js +252 -0
- package/dist/core/component.js +145 -0
- package/dist/core/hooks.js +229 -0
- package/dist/core/quantum.js +284 -0
- package/dist/core/query.js +63 -0
- package/dist/core/reactive.js +105 -0
- package/dist/core/renderer.js +70 -0
- package/dist/core/vault.js +81 -0
- package/dist/index.js +52 -0
- package/dist/mulan.esm.js +1948 -0
- package/dist/mulan.js +215 -0
- package/dist/router/index.js +210 -0
- package/dist/security/sanitizer.js +47 -0
- package/dist/store/index.js +42 -0
- package/dist/types/compiler/compiler.d.ts +7 -0
- package/dist/types/compiler/script-compiler.d.ts +8 -0
- package/dist/types/compiler/sfc-parser.d.ts +21 -0
- package/dist/types/compiler/style-compiler.d.ts +7 -0
- package/dist/types/compiler/template-compiler.d.ts +7 -0
- package/dist/types/compiler.d.ts +7 -0
- package/dist/types/components/bloch-sphere.d.ts +16 -0
- package/dist/types/core/component.d.ts +54 -0
- package/dist/types/core/hooks.d.ts +49 -0
- package/dist/types/core/quantum.d.ts +50 -0
- package/dist/types/core/query.d.ts +14 -0
- package/dist/types/core/reactive.d.ts +21 -0
- package/dist/types/core/renderer.d.ts +4 -0
- package/dist/types/core/vault.d.ts +12 -0
- package/dist/types/index.d.ts +70 -0
- package/dist/types/router/index.d.ts +24 -0
- package/dist/types/script-compiler.d.ts +8 -0
- package/dist/types/security/sanitizer.d.ts +17 -0
- package/dist/types/sfc-parser.d.ts +21 -0
- package/dist/types/store/index.d.ts +10 -0
- package/dist/types/style-compiler.d.ts +7 -0
- package/dist/types/template-compiler.d.ts +7 -0
- package/package.json +64 -0
- package/src/cli/extensions/mulanjs-vscode-1.0.0.vsix +0 -0
- package/src/cli/index.js +600 -0
- package/src/compiler/compiler.ts +102 -0
- package/src/compiler/script-compiler.ts +336 -0
- package/src/compiler/sfc-parser.ts +118 -0
- package/src/compiler/style-compiler.ts +66 -0
- package/src/compiler/template-compiler.ts +519 -0
- package/src/compiler/tsconfig.json +13 -0
- package/src/loader/index.js +81 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { muState } from './hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a quantum register of size n.
|
|
4
|
+
* State vector will have 2^n amplitudes.
|
|
5
|
+
*/
|
|
6
|
+
export function muRegister(n) {
|
|
7
|
+
const numStates = Math.pow(2, n);
|
|
8
|
+
const amplitudes = new Array(numStates).fill(0).map((_, i) => ({
|
|
9
|
+
re: i === 0 ? 1 : 0,
|
|
10
|
+
im: 0
|
|
11
|
+
}));
|
|
12
|
+
return muState({
|
|
13
|
+
value: {
|
|
14
|
+
size: n,
|
|
15
|
+
amplitudes
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Backward compatible muQubit (Single Qubit Register)
|
|
21
|
+
*/
|
|
22
|
+
export function muQubit(initial = 0) {
|
|
23
|
+
const q = muRegister(1);
|
|
24
|
+
if (initial === 1) {
|
|
25
|
+
muGate(q, 'X', 0);
|
|
26
|
+
}
|
|
27
|
+
// Add compatibility properties for Phase 1 components
|
|
28
|
+
// These properties are getters that dynamically access the amplitudes array
|
|
29
|
+
Object.defineProperty(q.value, 'alpha', { get: () => q.value.amplitudes[0] });
|
|
30
|
+
Object.defineProperty(q.value, 'beta', { get: () => q.value.amplitudes[1] });
|
|
31
|
+
return q;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Internal helper to update register state while maintaining compatibility
|
|
35
|
+
*/
|
|
36
|
+
function updateState(reg, newState) {
|
|
37
|
+
if (newState.size === 1) {
|
|
38
|
+
// Re-inject compatibility getters for muQubit users
|
|
39
|
+
Object.defineProperty(newState, 'alpha', {
|
|
40
|
+
get: () => newState.amplitudes[0],
|
|
41
|
+
configurable: true,
|
|
42
|
+
enumerable: true
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(newState, 'beta', {
|
|
45
|
+
get: () => newState.amplitudes[1],
|
|
46
|
+
configurable: true,
|
|
47
|
+
enumerable: true
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
reg.value = newState;
|
|
51
|
+
}
|
|
52
|
+
export function muGate(reg, type, target = 0, control) {
|
|
53
|
+
const state = reg.value;
|
|
54
|
+
const n = state.size;
|
|
55
|
+
const amplitudes = state.amplitudes;
|
|
56
|
+
const newAmps = amplitudes.map(a => (Object.assign({}, a)));
|
|
57
|
+
// Helper: Check if all control bits are 1
|
|
58
|
+
const checkControl = (index, ctrl) => {
|
|
59
|
+
if (ctrl === undefined)
|
|
60
|
+
return true;
|
|
61
|
+
if (typeof ctrl === 'number') {
|
|
62
|
+
return ((index >> ctrl) & 1) === 1;
|
|
63
|
+
}
|
|
64
|
+
return ctrl.every(c => ((index >> c) & 1) === 1);
|
|
65
|
+
};
|
|
66
|
+
// SWAP Gate (Unique case: affects 2 targets)
|
|
67
|
+
if (type === 'SWAP') {
|
|
68
|
+
const t1 = target;
|
|
69
|
+
const t2 = control; // SWAP uses control arg as second target
|
|
70
|
+
// Iterate only half to avoid double swapping
|
|
71
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
72
|
+
const bit1 = (i >> t1) & 1;
|
|
73
|
+
const bit2 = (i >> t2) & 1;
|
|
74
|
+
if (bit1 !== bit2) {
|
|
75
|
+
// Determine the swap partner index
|
|
76
|
+
// If i has (0,1), partner has (1,0) at those positions
|
|
77
|
+
const partner = i ^ (1 << t1) ^ (1 << t2);
|
|
78
|
+
if (i < partner) {
|
|
79
|
+
const temp = newAmps[i];
|
|
80
|
+
newAmps[i] = newAmps[partner];
|
|
81
|
+
newAmps[partner] = temp;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
updateState(reg, Object.assign(Object.assign({}, state), { amplitudes: newAmps }));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Standard Single-Qubit & Controlled Gates
|
|
89
|
+
if (type === 'H') {
|
|
90
|
+
const s = 1 / Math.sqrt(2);
|
|
91
|
+
const processed = new Set();
|
|
92
|
+
for (let i = 0; i < state.amplitudes.length; i++) {
|
|
93
|
+
if (processed.has(i))
|
|
94
|
+
continue;
|
|
95
|
+
// Check Controls first
|
|
96
|
+
if (!checkControl(i, control))
|
|
97
|
+
continue;
|
|
98
|
+
const targetBit = (i >> target) & 1;
|
|
99
|
+
const pairedIndex = targetBit ? (i & ~(1 << target)) : (i | (1 << target));
|
|
100
|
+
const a = state.amplitudes[i];
|
|
101
|
+
const b = state.amplitudes[pairedIndex];
|
|
102
|
+
if (targetBit === 0) {
|
|
103
|
+
newAmps[i] = { re: s * (a.re + b.re), im: s * (a.im + b.im) };
|
|
104
|
+
newAmps[pairedIndex] = { re: s * (a.re - b.re), im: s * (a.im - b.im) };
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
newAmps[pairedIndex] = { re: s * (b.re + a.re), im: s * (b.im + a.im) };
|
|
108
|
+
newAmps[i] = { re: s * (b.re - a.re), im: s * (b.im - a.im) };
|
|
109
|
+
}
|
|
110
|
+
processed.add(i);
|
|
111
|
+
processed.add(pairedIndex);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (type === 'X' || type === 'CNOT') {
|
|
115
|
+
// CNOT is just Controlled-X
|
|
116
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
117
|
+
if (!checkControl(i, control))
|
|
118
|
+
continue;
|
|
119
|
+
const targetBit = (i >> target) & 1;
|
|
120
|
+
const pairedIndex = targetBit ? (i & ~(1 << target)) : (i | (1 << target));
|
|
121
|
+
if (i < pairedIndex) {
|
|
122
|
+
const temp = newAmps[i];
|
|
123
|
+
newAmps[i] = newAmps[pairedIndex];
|
|
124
|
+
newAmps[pairedIndex] = temp;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (type === 'Z' || type === 'CZ') {
|
|
129
|
+
// Z and CZ are Phase Flips
|
|
130
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
131
|
+
if (!checkControl(i, control))
|
|
132
|
+
continue;
|
|
133
|
+
const targetBit = (i >> target) & 1;
|
|
134
|
+
// Z Gate acts on |1>
|
|
135
|
+
if (targetBit === 1) {
|
|
136
|
+
newAmps[i] = { re: -amplitudes[i].re, im: -amplitudes[i].im };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (type === 'Y') {
|
|
141
|
+
// Y: |0> -> i|1>, |1> -> -i|0>
|
|
142
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
143
|
+
if (!checkControl(i, control))
|
|
144
|
+
continue;
|
|
145
|
+
const targetBit = (i >> target) & 1;
|
|
146
|
+
const pairedIndex = targetBit ? (i & ~(1 << target)) : (i | (1 << target));
|
|
147
|
+
if (i < pairedIndex) {
|
|
148
|
+
// We process pairs (i, pairedIndex) where i has 0 at target, paired has 1
|
|
149
|
+
const a = newAmps[i]; // Coeff of |0>
|
|
150
|
+
const b = newAmps[pairedIndex]; // Coeff of |1>
|
|
151
|
+
// New |0> = -i * Old |1>
|
|
152
|
+
newAmps[i] = { re: b.im, im: -b.re };
|
|
153
|
+
// New |1> = i * Old |0>
|
|
154
|
+
newAmps[pairedIndex] = { re: -a.im, im: a.re };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
updateState(reg, Object.assign(Object.assign({}, state), { amplitudes: newAmps }));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Mulan Search Logic (Grover's Operator)
|
|
162
|
+
* Automatically constructs a multi-controlled Phase Flip (Z) for a specific target state.
|
|
163
|
+
* This is the core of the "Quantum Switch" or "Quantum Search" capability.
|
|
164
|
+
* @param reg Quantum Register
|
|
165
|
+
* @param targetState The integer state to "search" and mark (e.g. 2 for |10>)
|
|
166
|
+
*/
|
|
167
|
+
export function muSearch(reg, targetState) {
|
|
168
|
+
const n = reg.value.size;
|
|
169
|
+
const controls = [];
|
|
170
|
+
// 1. Identify which bits are 0 and need wrapping with X gates
|
|
171
|
+
// Logic: To mark |010>, we want C-C-Z to trigger on 111.
|
|
172
|
+
// So we apply X to bits that are 0, then C-C-Z, then X again.
|
|
173
|
+
// We treat the last bit as the 'target' for the Z gate, rest as controls
|
|
174
|
+
const targetQubit = n - 1;
|
|
175
|
+
for (let i = 0; i < n; i++) {
|
|
176
|
+
const bit = (targetState >> i) & 1;
|
|
177
|
+
if (i === targetQubit) {
|
|
178
|
+
// If target bit logic requires 0, we flip it to 1 for the Z gate to work
|
|
179
|
+
if (bit === 0)
|
|
180
|
+
muGate(reg, 'X', i);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
controls.push(i);
|
|
184
|
+
if (bit === 0)
|
|
185
|
+
muGate(reg, 'X', i);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// 2. Apply Multi-Controlled Z
|
|
189
|
+
muGate(reg, 'Z', targetQubit, controls);
|
|
190
|
+
// 3. Uncompute (Restore 0s)
|
|
191
|
+
for (let i = 0; i < n; i++) {
|
|
192
|
+
const bit = (targetState >> i) & 1;
|
|
193
|
+
if (bit === 0)
|
|
194
|
+
muGate(reg, 'X', i);
|
|
195
|
+
}
|
|
196
|
+
// --- GROVER DIFFUSER (Amplification) ---
|
|
197
|
+
// This flips the probability amplitudes around the mean, boosting the target state.
|
|
198
|
+
// 1. Apply H to all
|
|
199
|
+
for (let i = 0; i < n; i++)
|
|
200
|
+
muGate(reg, 'H', i);
|
|
201
|
+
// 2. Apply X to all
|
|
202
|
+
for (let i = 0; i < n; i++)
|
|
203
|
+
muGate(reg, 'X', i);
|
|
204
|
+
// 3. Multi-Controlled Z (Reflection about |0...0>)
|
|
205
|
+
// We want to flip phase of |11...1> state after X transformation (which corresponds to |00...0> original)
|
|
206
|
+
// Target is last qubit, controls are 0 to n-2
|
|
207
|
+
const diffControls = [];
|
|
208
|
+
for (let i = 0; i < n - 1; i++)
|
|
209
|
+
diffControls.push(i);
|
|
210
|
+
muGate(reg, 'Z', n - 1, diffControls);
|
|
211
|
+
// 4. Uncompute X
|
|
212
|
+
for (let i = 0; i < n; i++)
|
|
213
|
+
muGate(reg, 'X', i);
|
|
214
|
+
// 5. Uncompute H
|
|
215
|
+
for (let i = 0; i < n; i++)
|
|
216
|
+
muGate(reg, 'H', i);
|
|
217
|
+
}
|
|
218
|
+
export function muEntangle(reg, i, j) {
|
|
219
|
+
muGate(reg, 'H', i);
|
|
220
|
+
muGate(reg, 'CNOT', j, i);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Quantum Teleportation Protocol
|
|
224
|
+
* Transfers the state of `msgIdx` to `targetIdx` using `ancillaIdx` as a resource.
|
|
225
|
+
* @param reg Register
|
|
226
|
+
* @param msgIdx The qubit containing the state to teleport (Alice)
|
|
227
|
+
* @param ancillaIdx The helper qubit (Alice's half of entanglement)
|
|
228
|
+
* @param targetIdx The destination qubit (Bob)
|
|
229
|
+
*/
|
|
230
|
+
export function muTeleport(reg, msgIdx, ancillaIdx, targetIdx) {
|
|
231
|
+
// 1. Create Bell Pair (Entanglement) between Ancilla and Target
|
|
232
|
+
// Represents the shared link between Alice and Bob
|
|
233
|
+
muEntangle(reg, ancillaIdx, targetIdx);
|
|
234
|
+
// 2. Bell Measurement on Message + Ancilla (Alice's side)
|
|
235
|
+
muGate(reg, 'CNOT', ancillaIdx, msgIdx); // Control: msg, Target: ancilla
|
|
236
|
+
muGate(reg, 'H', msgIdx);
|
|
237
|
+
// 3. Measure Alice's qubits (This collapses them)
|
|
238
|
+
const m1 = muMeasure(reg, msgIdx); // Measures "Z" component
|
|
239
|
+
const m2 = muMeasure(reg, ancillaIdx); // Measures "X" component
|
|
240
|
+
// 4. Classical Communication & Correction (Bob's side)
|
|
241
|
+
// Apply corrections to Target based on measurements
|
|
242
|
+
if (m2 === 1)
|
|
243
|
+
muGate(reg, 'X', targetIdx);
|
|
244
|
+
if (m1 === 1)
|
|
245
|
+
muGate(reg, 'Z', targetIdx);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Measures a specific qubit in the register, collapsing the superposition.
|
|
249
|
+
* @param reg Quantum Register
|
|
250
|
+
* @param target Index of qubit to measure
|
|
251
|
+
* @returns 0 or 1
|
|
252
|
+
*/
|
|
253
|
+
export function muMeasure(reg, target = 0) {
|
|
254
|
+
const state = reg.value;
|
|
255
|
+
const amplitudes = state.amplitudes;
|
|
256
|
+
let prob0 = 0;
|
|
257
|
+
// 1. Calculate Probability of |0>
|
|
258
|
+
for (let i = 0; i < amplitudes.length; i++) {
|
|
259
|
+
if (((i >> target) & 1) === 0) {
|
|
260
|
+
const a = amplitudes[i];
|
|
261
|
+
prob0 += (a.re * a.re) + (a.im * a.im);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// 2. Determine Outcome
|
|
265
|
+
const result = Math.random() < prob0 ? 0 : 1;
|
|
266
|
+
const resultProb = result === 0 ? prob0 : (1 - prob0);
|
|
267
|
+
const normFactor = resultProb > 0 ? (1 / Math.sqrt(resultProb)) : 0;
|
|
268
|
+
// 3. Collapse the State (Wavefunction Collapse)
|
|
269
|
+
const newAmps = amplitudes.map((a, i) => {
|
|
270
|
+
const bit = (i >> target) & 1;
|
|
271
|
+
if (bit !== result) {
|
|
272
|
+
return { re: 0, im: 0 };
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
return { re: a.re * normFactor, im: a.im * normFactor };
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
// We need to call updateState to ensure reactivity if specialized getters exist
|
|
279
|
+
// But since this file has updateState internal, we can just call it.
|
|
280
|
+
// However, the internal updateState function needs to be in scope.
|
|
281
|
+
// It is defined at line 60. So we are good.
|
|
282
|
+
updateState(reg, Object.assign(Object.assign({}, state), { amplitudes: newAmps }));
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { reactive, effect } from './reactive';
|
|
11
|
+
export function useQuery(queryFn, options = { enabled: true }) {
|
|
12
|
+
const state = reactive({
|
|
13
|
+
data: null,
|
|
14
|
+
isLoading: false,
|
|
15
|
+
error: null
|
|
16
|
+
});
|
|
17
|
+
const execute = () => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
state.isLoading = true;
|
|
19
|
+
state.error = null;
|
|
20
|
+
try {
|
|
21
|
+
const result = yield queryFn();
|
|
22
|
+
state.data = result;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
state.error = err;
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
state.isLoading = false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (options.enabled) {
|
|
32
|
+
// Run automatically
|
|
33
|
+
effect(() => {
|
|
34
|
+
// Basic effect wrapper to allow reactivity if queryFn relies on signals
|
|
35
|
+
execute();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return Object.assign(Object.assign({}, state), { refetch: execute });
|
|
39
|
+
}
|
|
40
|
+
export function useMutation(mutationFn) {
|
|
41
|
+
const state = reactive({
|
|
42
|
+
data: null,
|
|
43
|
+
isLoading: false,
|
|
44
|
+
error: null
|
|
45
|
+
});
|
|
46
|
+
const mutate = (args) => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
state.isLoading = true;
|
|
48
|
+
state.error = null;
|
|
49
|
+
try {
|
|
50
|
+
const result = yield mutationFn(args);
|
|
51
|
+
state.data = result;
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
state.error = err;
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
state.isLoading = false;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return Object.assign(Object.assign({}, state), { mutate });
|
|
63
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// MulanJS 2.0: Signal-Powered Reactivity Engine
|
|
2
|
+
// "Compatible Surface, World-Class Engine"
|
|
3
|
+
let activeEffect = null;
|
|
4
|
+
const targetMap = new WeakMap();
|
|
5
|
+
// --- Signal Core (The Engine) ---
|
|
6
|
+
export class Signal {
|
|
7
|
+
constructor(initialValue) {
|
|
8
|
+
this._subscribers = new Set();
|
|
9
|
+
this._value = initialValue;
|
|
10
|
+
}
|
|
11
|
+
get value() {
|
|
12
|
+
if (activeEffect) {
|
|
13
|
+
this._subscribers.add(activeEffect);
|
|
14
|
+
}
|
|
15
|
+
return this._value;
|
|
16
|
+
}
|
|
17
|
+
set value(newValue) {
|
|
18
|
+
if (newValue !== this._value) {
|
|
19
|
+
this._value = newValue;
|
|
20
|
+
this.notify();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
notify() {
|
|
24
|
+
this._subscribers.forEach(fn => fn());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// --- Compatibility Layer (The Surface) ---
|
|
28
|
+
export function effect(fn) {
|
|
29
|
+
let stopped = false;
|
|
30
|
+
const run = () => {
|
|
31
|
+
if (stopped)
|
|
32
|
+
return;
|
|
33
|
+
const prev = activeEffect;
|
|
34
|
+
activeEffect = run;
|
|
35
|
+
try {
|
|
36
|
+
fn();
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
activeEffect = prev;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
run();
|
|
43
|
+
return () => {
|
|
44
|
+
stopped = true;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function track(target, key) {
|
|
48
|
+
if (activeEffect) {
|
|
49
|
+
let depsMap = targetMap.get(target);
|
|
50
|
+
if (!depsMap) {
|
|
51
|
+
targetMap.set(target, (depsMap = new Map()));
|
|
52
|
+
}
|
|
53
|
+
let dep = depsMap.get(key);
|
|
54
|
+
if (!dep) {
|
|
55
|
+
depsMap.set(key, (dep = new Set()));
|
|
56
|
+
}
|
|
57
|
+
dep.add(activeEffect);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function trigger(target, key) {
|
|
61
|
+
const depsMap = targetMap.get(target);
|
|
62
|
+
if (!depsMap)
|
|
63
|
+
return;
|
|
64
|
+
const dep = depsMap.get(key);
|
|
65
|
+
if (dep) {
|
|
66
|
+
dep.forEach((fn) => fn());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates a reactive proxy object (Vue-compatible).
|
|
71
|
+
* Now optimized to respect Mulan Cycle.
|
|
72
|
+
*/
|
|
73
|
+
export function reactive(target) {
|
|
74
|
+
return new Proxy(target, {
|
|
75
|
+
get(obj, prop, receiver) {
|
|
76
|
+
// IRON FORTRESS: Prototype Pollution Protection (Read)
|
|
77
|
+
if (prop === '__proto__' || prop === 'constructor' || prop === 'prototype') {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
track(obj, prop);
|
|
81
|
+
const val = Reflect.get(obj, prop, receiver);
|
|
82
|
+
if (val !== null && typeof val === 'object') {
|
|
83
|
+
return reactive(val);
|
|
84
|
+
}
|
|
85
|
+
return val;
|
|
86
|
+
},
|
|
87
|
+
set(obj, prop, value, receiver) {
|
|
88
|
+
// IRON FORTRESS: Prototype Pollution Protection
|
|
89
|
+
if (prop === '__proto__' || prop === 'constructor' || prop === 'prototype') {
|
|
90
|
+
console.warn(`[Iron Fortress] Blocked attempt to modify dangerous property: ${String(prop)}`);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const result = Reflect.set(obj, prop, value, receiver);
|
|
94
|
+
trigger(obj, prop);
|
|
95
|
+
return result;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a standalone reactive reference.
|
|
101
|
+
* Backed by the Mulan Signal Engine.
|
|
102
|
+
*/
|
|
103
|
+
export function ref(value) {
|
|
104
|
+
return new Signal(value);
|
|
105
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function render(template, container) {
|
|
2
|
+
// Focus Preservation (The "Mulan Glance" Technique)
|
|
3
|
+
let focusedId = null;
|
|
4
|
+
let selectionStart = null;
|
|
5
|
+
let selectionEnd = null;
|
|
6
|
+
let preservedValue = null;
|
|
7
|
+
if (document.activeElement && container.contains(document.activeElement)) {
|
|
8
|
+
const el = document.activeElement;
|
|
9
|
+
if (el.hasAttribute('data-mu-id')) {
|
|
10
|
+
focusedId = el.getAttribute('data-mu-id');
|
|
11
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
|
|
12
|
+
selectionStart = el.selectionStart;
|
|
13
|
+
selectionEnd = el.selectionEnd;
|
|
14
|
+
preservedValue = el.value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
container.innerHTML = template;
|
|
19
|
+
// Restore Focus and Value
|
|
20
|
+
if (focusedId) {
|
|
21
|
+
const el = container.querySelector(`[data-mu-id="${focusedId}"]`);
|
|
22
|
+
if (el) {
|
|
23
|
+
// Restore value first to ensure cursor positioning works correctly
|
|
24
|
+
if (preservedValue !== null && (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)) {
|
|
25
|
+
el.value = preservedValue;
|
|
26
|
+
}
|
|
27
|
+
el.focus();
|
|
28
|
+
if ((el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) && selectionStart !== null) {
|
|
29
|
+
try {
|
|
30
|
+
el.setSelectionRange(selectionStart, selectionEnd);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
// input types that don't support selection (e.g. number/email) might throw
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function hydrate(template, container) {
|
|
40
|
+
// In a string-based framework, hydration is often just "take over".
|
|
41
|
+
// For now, we'll verify if the server content matches (simple check)
|
|
42
|
+
// and then assume control. In a more advanced version, we'd attach listeners without re-rendering.
|
|
43
|
+
if (container.innerHTML.trim() !== template.trim()) {
|
|
44
|
+
console.warn('Hydration Mismatch: Server rendered content differs from Client.');
|
|
45
|
+
container.innerHTML = template; // Fallback to full render
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// console.log('Hydration Successful.');
|
|
49
|
+
}
|
|
50
|
+
// Future: Attach event listeners here if we had a mechanism for it.
|
|
51
|
+
}
|
|
52
|
+
export function renderToString(template) {
|
|
53
|
+
// SSR Function: returns the HTML string for the server to send.
|
|
54
|
+
// Can include additional sanitization or metadata injection here.
|
|
55
|
+
return template;
|
|
56
|
+
}
|
|
57
|
+
export function sanitize(str) {
|
|
58
|
+
// Check if we are in a browser environment
|
|
59
|
+
if (typeof document !== 'undefined') {
|
|
60
|
+
const temp = document.createElement('div');
|
|
61
|
+
temp.textContent = str;
|
|
62
|
+
return temp.innerHTML;
|
|
63
|
+
}
|
|
64
|
+
// Simple SSR fallback sanitizer (rudimentary)
|
|
65
|
+
return str.replace(/&/g, "&")
|
|
66
|
+
.replace(/</g, "<")
|
|
67
|
+
.replace(/>/g, ">")
|
|
68
|
+
.replace(/"/g, """)
|
|
69
|
+
.replace(/'/g, "'");
|
|
70
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { muEffect } from './hooks';
|
|
2
|
+
import { reactive } from './reactive';
|
|
3
|
+
const MULAN_SECRET = 'b3ast_mulan_s3cur1ty_k3y';
|
|
4
|
+
function beastXOR(str) {
|
|
5
|
+
let result = '';
|
|
6
|
+
for (let i = 0; i < str.length; i++) {
|
|
7
|
+
result += String.fromCharCode(str.charCodeAt(i) ^ MULAN_SECRET.charCodeAt(i % MULAN_SECRET.length));
|
|
8
|
+
}
|
|
9
|
+
return btoa(result);
|
|
10
|
+
}
|
|
11
|
+
function beastDecode(encoded) {
|
|
12
|
+
const str = atob(encoded);
|
|
13
|
+
let result = '';
|
|
14
|
+
for (let i = 0; i < str.length; i++) {
|
|
15
|
+
result += String.fromCharCode(str.charCodeAt(i) ^ MULAN_SECRET.charCodeAt(i % MULAN_SECRET.length));
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Mulan Vault: The World's First Native Persistent State Primitive.
|
|
21
|
+
* Now fortified with Iron Fortress Obfuscation.
|
|
22
|
+
*/
|
|
23
|
+
export function persistent(key, initialValue, options = {}) {
|
|
24
|
+
const storage = options.storage || window.localStorage;
|
|
25
|
+
// 1. Load from Storage
|
|
26
|
+
const stored = storage.getItem(key);
|
|
27
|
+
let startVal = initialValue;
|
|
28
|
+
if (stored) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = options.encrypt ? beastDecode(stored) : stored;
|
|
31
|
+
startVal = JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
console.warn(`[Iron Fortress] Corrupt persistent data for key "${key}", resetting.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// 2. Create Reactive State (Follow muState pattern for consistency)
|
|
38
|
+
const isObject = typeof startVal === 'object' && startVal !== null;
|
|
39
|
+
const state = isObject ? reactive(startVal) : reactive({ value: startVal });
|
|
40
|
+
// 3. Auto-Save on Change
|
|
41
|
+
muEffect(() => {
|
|
42
|
+
try {
|
|
43
|
+
const payload = isObject ? state : state.value;
|
|
44
|
+
const raw = JSON.stringify(payload);
|
|
45
|
+
const toSave = options.encrypt ? beastXOR(raw) : raw;
|
|
46
|
+
storage.setItem(key, toSave);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.error(`[Iron Fortress] Failed to save key "${key}"`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// 4. Sync across Tabs
|
|
53
|
+
const sync = (e) => {
|
|
54
|
+
if (e.key === key && e.newValue) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = options.encrypt ? beastDecode(e.newValue) : e.newValue;
|
|
57
|
+
const newVal = JSON.parse(raw);
|
|
58
|
+
if (isObject) {
|
|
59
|
+
Object.assign(state, newVal);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
state.value = newVal;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (e) { }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
window.addEventListener('storage', sync);
|
|
69
|
+
// Auto-cleanup if used inside a component
|
|
70
|
+
try {
|
|
71
|
+
const { onMuDestroy } = require('./hooks');
|
|
72
|
+
onMuDestroy(() => {
|
|
73
|
+
window.removeEventListener('storage', sync);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// muVault might be used outside component setup in some advanced cases,
|
|
78
|
+
// fallback to manual or no-cleanup if so.
|
|
79
|
+
}
|
|
80
|
+
return state;
|
|
81
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export * from './core/reactive';
|
|
2
|
+
export { MuComponent, MuComponent as Component, defineComponent } from './core/component';
|
|
3
|
+
export * from './core/renderer';
|
|
4
|
+
export { MuRouter, MuRouter as Router } from './router/index';
|
|
5
|
+
export { MuStore } from './store/index';
|
|
6
|
+
export * from './security/sanitizer';
|
|
7
|
+
export * from './core/hooks';
|
|
8
|
+
export * from './core/query';
|
|
9
|
+
export * from './core/vault';
|
|
10
|
+
export * from './core/quantum';
|
|
11
|
+
export * from './components/bloch-sphere';
|
|
12
|
+
// Global Mulan Object for non-module usage
|
|
13
|
+
import { reactive, effect } from './core/reactive';
|
|
14
|
+
import { MuComponent, defineComponent } from './core/component';
|
|
15
|
+
import { MuRouter } from './router/index';
|
|
16
|
+
import { MuStore } from './store/index';
|
|
17
|
+
import { Security } from './security/sanitizer';
|
|
18
|
+
import * as Hooks from './core/hooks';
|
|
19
|
+
import * as Query from './core/query';
|
|
20
|
+
import * as Quantum from './core/quantum';
|
|
21
|
+
const Mulan = Object.assign(Object.assign(Object.assign(Object.assign({ reactive,
|
|
22
|
+
effect, Component: MuComponent, defineComponent, Router: MuRouter, Store: MuStore, Security: Security }, Hooks), Query), Quantum), {
|
|
23
|
+
// MULAN INSIGHT: Branded Logging
|
|
24
|
+
log: (msg, ...args) => {
|
|
25
|
+
console.log(`%c[MulanJS]%c ${msg}`, "color: #ff3e00; font-weight: bold; background: #222; padding: 2px 4px; border-radius: 3px;", "", ...args);
|
|
26
|
+
}, warn: (msg, ...args) => {
|
|
27
|
+
console.warn(`%c[MulanJS]%c ${msg}`, "color: #ffcc00; font-weight: bold; background: #222; padding: 2px 4px; border-radius: 3px;", "", ...args);
|
|
28
|
+
}, error: (msg, ...args) => {
|
|
29
|
+
console.error(`%c[MulanJS]%c ${msg}`, "color: #ff0000; font-weight: bold; background: #222; padding: 2px 4px; border-radius: 3px;", "", ...args);
|
|
30
|
+
} });
|
|
31
|
+
// Security: Freeze the object to prevent runtime tampering
|
|
32
|
+
Object.freeze(Mulan);
|
|
33
|
+
Object.freeze(Mulan.Security);
|
|
34
|
+
// MULAN INSIGHT: Initialize Global Registry
|
|
35
|
+
if (typeof window !== 'undefined') {
|
|
36
|
+
window.__MULAN_INSIGHT__ = window.__MULAN_INSIGHT__ || { components: new Map() };
|
|
37
|
+
// MULAN INSIGHT: HMR Support
|
|
38
|
+
window.__MULAN_REFRESH__ = () => {
|
|
39
|
+
var _a;
|
|
40
|
+
const components = (_a = window.__MULAN_INSIGHT__) === null || _a === void 0 ? void 0 : _a.components;
|
|
41
|
+
if (components) {
|
|
42
|
+
components.forEach((comp) => {
|
|
43
|
+
if (comp && typeof comp.update === 'function') {
|
|
44
|
+
// Force refresh
|
|
45
|
+
comp.update();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
window.Mulan = Mulan;
|
|
52
|
+
export default Mulan;
|