eclipsa 0.1.8 → 0.1.10
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/{action-DqgkV3zb.mjs → action-DCc4fBhy.mjs} +258 -19
- package/action-DCc4fBhy.mjs.map +1 -0
- package/{client-DKPmN-wJ.mjs → client--uq9YZzN.mjs} +42 -3
- package/client--uq9YZzN.mjs.map +1 -0
- package/core/client/mod.mjs +3 -3
- package/core/dev-client/mod.mjs +3 -3
- package/core/internal.d.mts +1 -1
- package/core/internal.mjs +1 -1
- package/core/prod-client/mod.mjs +3 -3
- package/core/prod-client/mod.mjs.map +1 -1
- package/{internal-CHYAJznU.d.mts → internal-1QlldZaD.d.mts} +2 -1
- package/jsx/mod.mjs +1 -1
- package/mod.d.mts +2 -2
- package/mod.mjs +3 -3
- package/package.json +2 -2
- package/{signal-DBzloBrN.mjs → signal-DoW9q--8.mjs} +10 -2
- package/{signal-DBzloBrN.mjs.map → signal-DoW9q--8.mjs.map} +1 -1
- package/{ssr-CRg57Wn2.mjs → ssr-lXZqUHZs.mjs} +4 -4
- package/{ssr-CRg57Wn2.mjs.map → ssr-lXZqUHZs.mjs.map} +1 -1
- package/{ssr-D8F-DtCv.d.mts → ssr-wXShLrdB.d.mts} +2 -2
- package/vite/build/runtime.d.mts +9 -3
- package/vite/build/runtime.mjs +3 -3
- package/vite/mod.mjs +73 -41
- package/vite/mod.mjs.map +1 -1
- package/web-utils/mod.mjs +2 -2
- package/action-DqgkV3zb.mjs.map +0 -1
- package/client-DKPmN-wJ.mjs.map +0 -1
|
@@ -378,6 +378,96 @@ const resetClientHooks = () => {
|
|
|
378
378
|
clientHooks = {};
|
|
379
379
|
};
|
|
380
380
|
//#endregion
|
|
381
|
+
//#region core/action-csrf.ts
|
|
382
|
+
const ACTION_CSRF_COOKIE = "__eclipsa_action_csrf";
|
|
383
|
+
const ACTION_CSRF_FIELD = "__e_csrf";
|
|
384
|
+
const ACTION_CSRF_HEADER = "x-eclipsa-csrf";
|
|
385
|
+
const ACTION_CSRF_INPUT_ATTR = "data-e-action-csrf";
|
|
386
|
+
const ACTION_CSRF_ERROR_MESSAGE = "Invalid CSRF token.";
|
|
387
|
+
const ACTION_CSRF_TOKEN_KEY = Symbol.for("eclipsa.action-csrf-token");
|
|
388
|
+
const ACTION_CSRF_SET_COOKIE_KEY = Symbol.for("eclipsa.action-csrf-set-cookie");
|
|
389
|
+
const getCrypto = () => {
|
|
390
|
+
if (globalThis.crypto) return globalThis.crypto;
|
|
391
|
+
const nodeCrypto = typeof process === "undefined" ? void 0 : process.getBuiltinModule?.("node:crypto");
|
|
392
|
+
if (nodeCrypto?.webcrypto) return nodeCrypto.webcrypto;
|
|
393
|
+
throw new Error("Web Crypto API is not available in this environment.");
|
|
394
|
+
};
|
|
395
|
+
const toHex = (bytes) => [...bytes].map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
396
|
+
const createActionCsrfToken = () => {
|
|
397
|
+
const bytes = new Uint8Array(32);
|
|
398
|
+
getCrypto().getRandomValues(bytes);
|
|
399
|
+
return toHex(bytes);
|
|
400
|
+
};
|
|
401
|
+
const parseCookie = (cookieHeader, name) => {
|
|
402
|
+
if (!cookieHeader) return null;
|
|
403
|
+
for (const entry of cookieHeader.split(";")) {
|
|
404
|
+
const trimmed = entry.trim();
|
|
405
|
+
if (!trimmed.startsWith(`${name}=`)) continue;
|
|
406
|
+
const value = trimmed.slice(name.length + 1);
|
|
407
|
+
if (value.length === 0) return null;
|
|
408
|
+
try {
|
|
409
|
+
return decodeURIComponent(value);
|
|
410
|
+
} catch {
|
|
411
|
+
return value;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
};
|
|
416
|
+
const shouldUseSecureCookie = (c) => {
|
|
417
|
+
try {
|
|
418
|
+
if (new URL(c.req.raw.url).protocol === "https:") return true;
|
|
419
|
+
} catch {}
|
|
420
|
+
return c.req.header("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase() === "https";
|
|
421
|
+
};
|
|
422
|
+
const readActionCsrfTokenFromCookieHeader = (cookieHeader) => parseCookie(cookieHeader, ACTION_CSRF_COOKIE);
|
|
423
|
+
const readActionCsrfTokenFromDocument = (doc) => readActionCsrfTokenFromCookieHeader(doc.cookie);
|
|
424
|
+
const readActionCsrfTokenFromFormData = (value) => {
|
|
425
|
+
const token = value.get(ACTION_CSRF_FIELD);
|
|
426
|
+
return typeof token === "string" && token.length > 0 ? token : null;
|
|
427
|
+
};
|
|
428
|
+
const readActionCsrfTokenFromRequest = (c) => readActionCsrfTokenFromCookieHeader(c.req.header("cookie"));
|
|
429
|
+
const ensureActionCsrfToken = (c) => {
|
|
430
|
+
const record = c;
|
|
431
|
+
const existing = record[ACTION_CSRF_TOKEN_KEY];
|
|
432
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
433
|
+
const cookieToken = readActionCsrfTokenFromRequest(c);
|
|
434
|
+
if (cookieToken) {
|
|
435
|
+
record[ACTION_CSRF_TOKEN_KEY] = cookieToken;
|
|
436
|
+
record[ACTION_CSRF_SET_COOKIE_KEY] = false;
|
|
437
|
+
return cookieToken;
|
|
438
|
+
}
|
|
439
|
+
const created = createActionCsrfToken();
|
|
440
|
+
record[ACTION_CSRF_TOKEN_KEY] = created;
|
|
441
|
+
record[ACTION_CSRF_SET_COOKIE_KEY] = true;
|
|
442
|
+
return created;
|
|
443
|
+
};
|
|
444
|
+
const getCurrentActionCsrfToken = () => {
|
|
445
|
+
const context = getCurrentServerRequestContext();
|
|
446
|
+
return context ? ensureActionCsrfToken(context) : null;
|
|
447
|
+
};
|
|
448
|
+
const serializeActionCsrfCookie = (token, secure) => [
|
|
449
|
+
`${ACTION_CSRF_COOKIE}=${encodeURIComponent(token)}`,
|
|
450
|
+
"Path=/",
|
|
451
|
+
"SameSite=Lax",
|
|
452
|
+
...secure ? ["Secure"] : []
|
|
453
|
+
].join("; ");
|
|
454
|
+
const applyActionCsrfCookie = (response, c) => {
|
|
455
|
+
const record = c;
|
|
456
|
+
if (record[ACTION_CSRF_SET_COOKIE_KEY] !== true) return response;
|
|
457
|
+
const token = record[ACTION_CSRF_TOKEN_KEY];
|
|
458
|
+
if (!token) return response;
|
|
459
|
+
const cookieValue = serializeActionCsrfCookie(token, shouldUseSecureCookie(c));
|
|
460
|
+
try {
|
|
461
|
+
response.headers.append("set-cookie", cookieValue);
|
|
462
|
+
} catch {
|
|
463
|
+
const next = new Response(response.body, response);
|
|
464
|
+
next.headers.append("set-cookie", cookieValue);
|
|
465
|
+
response = next;
|
|
466
|
+
}
|
|
467
|
+
record[ACTION_CSRF_SET_COOKIE_KEY] = false;
|
|
468
|
+
return response;
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
381
471
|
//#region core/suspense.ts
|
|
382
472
|
const PENDING_SIGNAL_ERROR_KEY = Symbol.for("eclipsa.pending-signal-error");
|
|
383
473
|
const SUSPENSE_TYPE_KEY = Symbol.for("eclipsa.suspense-type");
|
|
@@ -1399,7 +1489,15 @@ const ROUTE_DOCUMENT_FALLBACK = Object.freeze({
|
|
|
1399
1489
|
document: true,
|
|
1400
1490
|
ok: false
|
|
1401
1491
|
});
|
|
1402
|
-
const
|
|
1492
|
+
const decodeRoutePathSegment = (segment) => {
|
|
1493
|
+
try {
|
|
1494
|
+
return decodeURIComponent(segment);
|
|
1495
|
+
} catch {
|
|
1496
|
+
return segment;
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
const splitRawRoutePath = (pathname) => normalizeRoutePath(pathname).split("/").filter(Boolean);
|
|
1500
|
+
const splitRoutePath = (pathname) => splitRawRoutePath(pathname).map(decodeRoutePathSegment);
|
|
1403
1501
|
const matchRouteSegments = (segments, pathnameSegments, routeIndex = 0, pathIndex = 0, params = {}) => {
|
|
1404
1502
|
if (routeIndex >= segments.length) return pathIndex >= pathnameSegments.length ? params : null;
|
|
1405
1503
|
const segment = segments[routeIndex];
|
|
@@ -1464,8 +1562,14 @@ const scoreSpecialManifestEntry = (entry, pathname) => {
|
|
|
1464
1562
|
return score;
|
|
1465
1563
|
};
|
|
1466
1564
|
const findSpecialManifestEntry = (manifest, pathname, kind) => {
|
|
1467
|
-
const
|
|
1565
|
+
const normalizedPath = normalizeRoutePath(pathname);
|
|
1566
|
+
const matched = matchRouteManifest(manifest, normalizedPath);
|
|
1468
1567
|
if (matched?.entry[kind]) return matched;
|
|
1568
|
+
const rawPathSegments = splitRawRoutePath(normalizedPath);
|
|
1569
|
+
for (let length = rawPathSegments.length - 1; length >= 0; length -= 1) {
|
|
1570
|
+
const candidate = matchRouteManifest(manifest, length === 0 ? "/" : `/${rawPathSegments.slice(0, length).join("/")}`);
|
|
1571
|
+
if (candidate?.entry[kind]) return candidate;
|
|
1572
|
+
}
|
|
1469
1573
|
let best = null;
|
|
1470
1574
|
let bestScore = -1;
|
|
1471
1575
|
for (const entry of manifest) {
|
|
@@ -1475,7 +1579,7 @@ const findSpecialManifestEntry = (manifest, pathname, kind) => {
|
|
|
1475
1579
|
best = {
|
|
1476
1580
|
entry,
|
|
1477
1581
|
params: EMPTY_ROUTE_PARAMS,
|
|
1478
|
-
pathname:
|
|
1582
|
+
pathname: normalizedPath
|
|
1479
1583
|
};
|
|
1480
1584
|
bestScore = score;
|
|
1481
1585
|
}
|
|
@@ -1766,7 +1870,7 @@ const parseInsertMarker = (value) => {
|
|
|
1766
1870
|
};
|
|
1767
1871
|
//#endregion
|
|
1768
1872
|
//#region core/runtime/serialization.ts
|
|
1769
|
-
const createRuntimeSerialization = ({ createProjectionSlot, ensureRouterState, ensureRuntimeElementId, evaluateProps, findRuntimeElement, getResolvedRuntimeSymbols, isPlainObject, isProjectionSlot, isRenderObject, isRouteSlot, loadSymbol, materializeScope, materializeSymbolReference, registerScope, registerSerializedScope, resolveRenderable }) => {
|
|
1873
|
+
const createRuntimeSerialization = ({ createProjectionSlot, ensureRouterState, ensureRuntimeElementId, evaluateProps, findRuntimeElement, getResolvedRuntimeSymbols, isPlainObject, isProjectionSlot, isRenderObject, isRouteSlot, loadSymbol, materializeComputedSignalReference, materializeScope, materializeSymbolReference, registerScope, registerSerializedScope, resolveRenderable }) => {
|
|
1770
1874
|
const getRenderComponentTypeRef = (value) => {
|
|
1771
1875
|
if (typeof value !== "function") return null;
|
|
1772
1876
|
return value[RENDER_COMPONENT_TYPE_KEY] ?? null;
|
|
@@ -1890,7 +1994,7 @@ const createRuntimeSerialization = ({ createProjectionSlot, ensureRouterState, e
|
|
|
1890
1994
|
const signalMeta = getSignalMeta(candidate);
|
|
1891
1995
|
if (signalMeta) return {
|
|
1892
1996
|
__eclipsa_type: "ref",
|
|
1893
|
-
kind: "signal",
|
|
1997
|
+
kind: signalMeta.kind === "computed-signal" ? "computed-signal" : "signal",
|
|
1894
1998
|
token: signalMeta.id
|
|
1895
1999
|
};
|
|
1896
2000
|
if (getNavigateMeta(candidate)) return {
|
|
@@ -2024,6 +2128,7 @@ const createRuntimeSerialization = ({ createProjectionSlot, ensureRouterState, e
|
|
|
2024
2128
|
if (!record) throw new Error(`Missing signal ${reference.token}.`);
|
|
2025
2129
|
return record.handle;
|
|
2026
2130
|
}
|
|
2131
|
+
if (reference.kind === "computed-signal") return materializeComputedSignalReference(container, reference.token);
|
|
2027
2132
|
if (reference.kind === "symbol") {
|
|
2028
2133
|
if (!reference.data || !Array.isArray(reference.data)) throw new TypeError("Symbol references require an encoded scope array.");
|
|
2029
2134
|
const scopeId = registerSerializedScope(container, reference.data);
|
|
@@ -2133,6 +2238,16 @@ const renderScopedStyleNode = (container, scopeId, style) => {
|
|
|
2133
2238
|
};
|
|
2134
2239
|
const renderFrameScopedStylesToString = (frame) => frame.scopedStyles.map((style) => renderScopedStyleString(frame.component.scopeId, style)).join("");
|
|
2135
2240
|
const renderFrameScopedStylesToNodes = (frame, container) => frame.scopedStyles.map((style) => renderScopedStyleNode(container, frame.component.scopeId, style));
|
|
2241
|
+
const createActionCsrfInputString = (token) => `<input ${ACTION_CSRF_INPUT_ATTR}="" name="${escapeAttr(ACTION_CSRF_FIELD)}" type="hidden" value="${escapeAttr(token)}">`;
|
|
2242
|
+
const createActionCsrfInputNode = (doc, token) => {
|
|
2243
|
+
const input = createElementNode(doc, "input");
|
|
2244
|
+
input.setAttribute(ACTION_CSRF_INPUT_ATTR, "");
|
|
2245
|
+
input.setAttribute("name", ACTION_CSRF_FIELD);
|
|
2246
|
+
input.setAttribute("type", "hidden");
|
|
2247
|
+
input.setAttribute("value", token);
|
|
2248
|
+
return input;
|
|
2249
|
+
};
|
|
2250
|
+
const readActionCsrfTokenFromRuntimeDocument = (doc) => doc && "cookie" in doc && typeof doc.cookie === "string" ? readActionCsrfTokenFromDocument(doc) : null;
|
|
2136
2251
|
const registerRuntimeScopedStyle = (cssText, attributes = {}) => {
|
|
2137
2252
|
const frame = getCurrentFrame();
|
|
2138
2253
|
if (!frame || frame.component.id === "$root") throw new Error("useStyleScoped() can only be used while rendering a component.");
|
|
@@ -2259,6 +2374,7 @@ const getRuntimeSerialization = () => {
|
|
|
2259
2374
|
isRenderObject,
|
|
2260
2375
|
isRouteSlot,
|
|
2261
2376
|
loadSymbol,
|
|
2377
|
+
materializeComputedSignalReference,
|
|
2262
2378
|
materializeScope,
|
|
2263
2379
|
materializeSymbolReference,
|
|
2264
2380
|
registerScope,
|
|
@@ -2280,8 +2396,15 @@ const findNextNumericId = (ids, prefix) => {
|
|
|
2280
2396
|
}
|
|
2281
2397
|
return nextId;
|
|
2282
2398
|
};
|
|
2283
|
-
const
|
|
2284
|
-
const
|
|
2399
|
+
const isWritableSignalMeta = (meta) => !!meta && meta.kind !== "computed-signal";
|
|
2400
|
+
const getRefSignalId = (value) => {
|
|
2401
|
+
const signalMeta = getSignalMeta(value);
|
|
2402
|
+
return isWritableSignalMeta(signalMeta) ? signalMeta.id : null;
|
|
2403
|
+
};
|
|
2404
|
+
const getBindableSignalId = (value) => {
|
|
2405
|
+
const signalMeta = getSignalMeta(value);
|
|
2406
|
+
return isWritableSignalMeta(signalMeta) ? signalMeta.id : null;
|
|
2407
|
+
};
|
|
2285
2408
|
const syncRuntimeRefMarker = (element, value) => {
|
|
2286
2409
|
const signalId = getRefSignalId(value);
|
|
2287
2410
|
if (signalId) {
|
|
@@ -2298,7 +2421,7 @@ const readBindableSignalValue = (value) => {
|
|
|
2298
2421
|
};
|
|
2299
2422
|
const assignRuntimeRef = (value, element, container = getCurrentContainer()) => {
|
|
2300
2423
|
const signalMeta = getSignalMeta(value);
|
|
2301
|
-
if (!signalMeta) return false;
|
|
2424
|
+
if (!isWritableSignalMeta(signalMeta)) return false;
|
|
2302
2425
|
const record = container?.signals.get(signalMeta.id);
|
|
2303
2426
|
if (record) {
|
|
2304
2427
|
writeSignalValue(container, record, element);
|
|
@@ -2453,12 +2576,41 @@ const createSignalHandle = (record, container) => {
|
|
|
2453
2576
|
setSignalMeta(handle, {
|
|
2454
2577
|
get: () => record.value,
|
|
2455
2578
|
id: record.id,
|
|
2579
|
+
kind: "signal",
|
|
2456
2580
|
set: (value) => {
|
|
2457
2581
|
writeSignalValue(container, record, value);
|
|
2458
2582
|
}
|
|
2459
2583
|
});
|
|
2460
2584
|
return handle;
|
|
2461
2585
|
};
|
|
2586
|
+
const isComputedSignalSnapshot = (value) => !!value && typeof value === "object" && value.__e_async_computed === true;
|
|
2587
|
+
const readComputedSignalValue = (record) => {
|
|
2588
|
+
recordSignalRead(record);
|
|
2589
|
+
const snapshot = record.value;
|
|
2590
|
+
if (!isComputedSignalSnapshot(snapshot)) return snapshot;
|
|
2591
|
+
if (snapshot.status === "pending") throw createPendingSignalError(snapshot.promise ?? Promise.resolve(void 0));
|
|
2592
|
+
if (snapshot.status === "rejected") throw snapshot.error;
|
|
2593
|
+
return snapshot.value;
|
|
2594
|
+
};
|
|
2595
|
+
const createComputedSignalHandle = (record, _container) => {
|
|
2596
|
+
const handle = {};
|
|
2597
|
+
Object.defineProperty(handle, "value", {
|
|
2598
|
+
configurable: true,
|
|
2599
|
+
enumerable: true,
|
|
2600
|
+
get() {
|
|
2601
|
+
return readComputedSignalValue(record);
|
|
2602
|
+
}
|
|
2603
|
+
});
|
|
2604
|
+
setSignalMeta(handle, {
|
|
2605
|
+
get: () => readComputedSignalValue(record),
|
|
2606
|
+
id: record.id,
|
|
2607
|
+
kind: "computed-signal",
|
|
2608
|
+
set: () => {
|
|
2609
|
+
throw new TypeError("Computed signals are read-only.");
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
return handle;
|
|
2613
|
+
};
|
|
2462
2614
|
const isPrimitiveSignalValue = (value) => value === null || typeof value !== "object" && typeof value !== "function";
|
|
2463
2615
|
const didSignalValueChange = (previous, next) => {
|
|
2464
2616
|
if (isPrimitiveSignalValue(previous) && isPrimitiveSignalValue(next)) return !Object.is(previous, next);
|
|
@@ -2683,6 +2835,11 @@ const materializeSymbolReference = (container, symbolId, scopeId) => {
|
|
|
2683
2835
|
});
|
|
2684
2836
|
return fn;
|
|
2685
2837
|
};
|
|
2838
|
+
const materializeComputedSignalReference = (container, signalId) => {
|
|
2839
|
+
const record = container.signals.get(signalId);
|
|
2840
|
+
if (!record) throw new Error(`Missing signal ${signalId}.`);
|
|
2841
|
+
return createComputedSignalHandle(record, container);
|
|
2842
|
+
};
|
|
2686
2843
|
const materializeScope = (container, scopeId) => {
|
|
2687
2844
|
const slots = container.scopes.get(scopeId);
|
|
2688
2845
|
if (!slots) throw new Error(`Missing scope ${scopeId}.`);
|
|
@@ -3990,6 +4147,7 @@ const renderStringNode = (inputElementLike) => {
|
|
|
3990
4147
|
const frame = getCurrentFrame();
|
|
3991
4148
|
let hasInnerHTML = false;
|
|
3992
4149
|
let innerHTML = null;
|
|
4150
|
+
let isActionForm = false;
|
|
3993
4151
|
if (frame && hasScopedStyles(frame) && resolved.type !== "style") attrParts.push(`${SCOPED_STYLE_ATTR}="${escapeAttr(frame.component.scopeId)}"`);
|
|
3994
4152
|
for (const name in resolved.props) {
|
|
3995
4153
|
if (!Object.hasOwn(resolved.props, name)) continue;
|
|
@@ -4033,13 +4191,21 @@ const renderStringNode = (inputElementLike) => {
|
|
|
4033
4191
|
if (resolved.type === "body" && name === "data-e-resume") continue;
|
|
4034
4192
|
if (value === true) {
|
|
4035
4193
|
attrParts.push(name);
|
|
4194
|
+
if (name === "data-e-action-form") isActionForm = true;
|
|
4036
4195
|
continue;
|
|
4037
4196
|
}
|
|
4197
|
+
if (name === "data-e-action-form") isActionForm = true;
|
|
4038
4198
|
attrParts.push(`${name}="${escapeAttr(String(value))}"`);
|
|
4039
4199
|
}
|
|
4040
4200
|
if (resolved.type === "body" && container) attrParts.push("data-e-resume=\"paused\"");
|
|
4041
4201
|
let childrenText = innerHTML ?? "";
|
|
4042
|
-
if (!hasInnerHTML)
|
|
4202
|
+
if (!hasInnerHTML) {
|
|
4203
|
+
if (resolved.type === "form" && isActionForm) {
|
|
4204
|
+
const csrfToken = getCurrentActionCsrfToken();
|
|
4205
|
+
if (csrfToken) childrenText += createActionCsrfInputString(csrfToken);
|
|
4206
|
+
}
|
|
4207
|
+
childrenText += renderStringNode(resolved.props.children);
|
|
4208
|
+
}
|
|
4043
4209
|
const rendered = resolved.type === "__ECLIPSA_FRAGMENT" ? childrenText : `<${resolved.type}${attrParts.length > 0 ? ` ${attrParts.join(" ")}` : ""}>${childrenText}</${resolved.type}>`;
|
|
4044
4210
|
return resolved.key === null || resolved.key === void 0 ? rendered : wrapStringWithKeyedRange(rendered, resolveKeyedRangeScope(), resolved.key);
|
|
4045
4211
|
};
|
|
@@ -4271,6 +4437,10 @@ const renderClientNodes = (inputElementLike, container) => {
|
|
|
4271
4437
|
rememberManagedAttributesForNode(element);
|
|
4272
4438
|
nodes = [element];
|
|
4273
4439
|
} else {
|
|
4440
|
+
if (resolved.type === "form" && element.getAttribute("data-e-action-form") !== null) {
|
|
4441
|
+
const csrfToken = readActionCsrfTokenFromRuntimeDocument(container.doc);
|
|
4442
|
+
if (csrfToken) element.appendChild(createActionCsrfInputNode(container.doc, csrfToken));
|
|
4443
|
+
}
|
|
4274
4444
|
const childNodes = renderClientNodes(resolved.props.children, container);
|
|
4275
4445
|
for (const child of childNodes) element.appendChild(child);
|
|
4276
4446
|
rememberManagedAttributesForNode(element);
|
|
@@ -5350,6 +5520,32 @@ const collectResumeHmrBoundaryIds = (container, symbolIds) => {
|
|
|
5350
5520
|
}
|
|
5351
5521
|
return result;
|
|
5352
5522
|
};
|
|
5523
|
+
const rewriteSerializedSymbolReferenceToken = (value, affectedIds, nextSymbolId) => {
|
|
5524
|
+
if (Array.isArray(value)) {
|
|
5525
|
+
for (const entry of value) rewriteSerializedSymbolReferenceToken(entry, affectedIds, nextSymbolId);
|
|
5526
|
+
return;
|
|
5527
|
+
}
|
|
5528
|
+
if (!value || typeof value !== "object" || !("__eclipsa_type" in value)) return;
|
|
5529
|
+
switch (value.__eclipsa_type) {
|
|
5530
|
+
case "object":
|
|
5531
|
+
for (const [, entry] of value.entries) rewriteSerializedSymbolReferenceToken(entry, affectedIds, nextSymbolId);
|
|
5532
|
+
return;
|
|
5533
|
+
case "map":
|
|
5534
|
+
for (const [key, entry] of value.entries) {
|
|
5535
|
+
rewriteSerializedSymbolReferenceToken(key, affectedIds, nextSymbolId);
|
|
5536
|
+
rewriteSerializedSymbolReferenceToken(entry, affectedIds, nextSymbolId);
|
|
5537
|
+
}
|
|
5538
|
+
return;
|
|
5539
|
+
case "set":
|
|
5540
|
+
for (const entry of value.entries) rewriteSerializedSymbolReferenceToken(entry, affectedIds, nextSymbolId);
|
|
5541
|
+
return;
|
|
5542
|
+
case "ref":
|
|
5543
|
+
if (value.kind === "symbol" && affectedIds.has(value.token)) value.token = nextSymbolId;
|
|
5544
|
+
if (value.data !== void 0) rewriteSerializedSymbolReferenceToken(value.data, affectedIds, nextSymbolId);
|
|
5545
|
+
return;
|
|
5546
|
+
default: return;
|
|
5547
|
+
}
|
|
5548
|
+
};
|
|
5353
5549
|
const applyResumeHmrSymbolReplacements = (container, replacements) => {
|
|
5354
5550
|
for (const [oldSymbolId, url] of Object.entries(replacements)) {
|
|
5355
5551
|
const currentUrl = container.symbols.get(oldSymbolId);
|
|
@@ -5364,6 +5560,7 @@ const applyResumeHmrSymbolReplacements = (container, replacements) => {
|
|
|
5364
5560
|
for (const component of container.components.values()) if (affectedIds.has(component.symbol)) component.symbol = nextSymbolId;
|
|
5365
5561
|
for (const watch of container.watches.values()) if (affectedIds.has(watch.symbol)) watch.symbol = nextSymbolId;
|
|
5366
5562
|
for (const visible of container.visibles.values()) if (affectedIds.has(visible.symbol)) visible.symbol = nextSymbolId;
|
|
5563
|
+
for (const slots of container.scopes.values()) for (const slot of slots) rewriteSerializedSymbolReferenceToken(slot, affectedIds, nextSymbolId);
|
|
5367
5564
|
container.symbols.set(nextSymbolId, url);
|
|
5368
5565
|
invalidateRuntimeSymbolCaches(container, [nextSymbolId]);
|
|
5369
5566
|
}
|
|
@@ -6111,7 +6308,10 @@ const useRuntimeSignal = (fallback) => {
|
|
|
6111
6308
|
};
|
|
6112
6309
|
const createDetachedRuntimeSignal = (container, id, fallback) => ensureSignalRecord(container, id, fallback).handle;
|
|
6113
6310
|
const getRuntimeComponentId = () => getCurrentFrame()?.component.id ?? null;
|
|
6114
|
-
const getRuntimeSignalId = (value) =>
|
|
6311
|
+
const getRuntimeSignalId = (value) => {
|
|
6312
|
+
const signalMeta = getSignalMeta(value);
|
|
6313
|
+
return isWritableSignalMeta(signalMeta) ? signalMeta.id : null;
|
|
6314
|
+
};
|
|
6115
6315
|
const useRuntimeNavigate = () => {
|
|
6116
6316
|
const container = getCurrentContainer();
|
|
6117
6317
|
if (!container) return createStandaloneNavigate();
|
|
@@ -6233,6 +6433,14 @@ const ACTION_STREAM_CONTENT_TYPE = "application/eclipsa-action-stream+json";
|
|
|
6233
6433
|
const ACTION_FORM_ATTR = "data-e-action-form";
|
|
6234
6434
|
const ACTION_FORM_FIELD = "__e_action";
|
|
6235
6435
|
const ACTION_INPUT_CACHE_KEY = Symbol.for("eclipsa.action-input-cache");
|
|
6436
|
+
const ACTION_CSRF_ERROR_CODE = Symbol.for("eclipsa.action-csrf-error");
|
|
6437
|
+
var ActionCsrfError = class extends Error {
|
|
6438
|
+
[ACTION_CSRF_ERROR_CODE] = true;
|
|
6439
|
+
constructor() {
|
|
6440
|
+
super(ACTION_CSRF_ERROR_MESSAGE);
|
|
6441
|
+
this.name = "ActionCsrfError";
|
|
6442
|
+
}
|
|
6443
|
+
};
|
|
6236
6444
|
const getActionRegistry = () => {
|
|
6237
6445
|
const globalRecord = globalThis;
|
|
6238
6446
|
const existing = globalRecord[ACTION_REGISTRY_KEY];
|
|
@@ -6258,7 +6466,7 @@ const normalizeFormSubmissionInput = (value) => {
|
|
|
6258
6466
|
if (!isFormDataValue(value)) return value;
|
|
6259
6467
|
const normalized = formDataToInputObject(value);
|
|
6260
6468
|
return Object.fromEntries(Object.entries(normalized).flatMap(([key, entry]) => {
|
|
6261
|
-
if (key === "__e_action") return [];
|
|
6469
|
+
if (key === "__e_action" || key === "__e_csrf") return [];
|
|
6262
6470
|
if (Array.isArray(entry)) {
|
|
6263
6471
|
const values = entry.filter((candidate) => typeof candidate === "string").map((candidate) => candidate);
|
|
6264
6472
|
return values.length > 0 ? [[key, values.length === 1 ? values[0] : values]] : [];
|
|
@@ -6322,6 +6530,17 @@ const deserializeActionServerValue = (value) => deserializePublicValue(value, {
|
|
|
6322
6530
|
};
|
|
6323
6531
|
throw new TypeError(`Unsupported action input reference kind "${reference.kind}".`);
|
|
6324
6532
|
} });
|
|
6533
|
+
const isActionCsrfValid = async (c) => {
|
|
6534
|
+
const cookieToken = readActionCsrfTokenFromRequest(c);
|
|
6535
|
+
if (!cookieToken) return false;
|
|
6536
|
+
const contentType = c.req.header("content-type") ?? "";
|
|
6537
|
+
if (contentType.startsWith("application/eclipsa-action+json")) return c.req.header(ACTION_CSRF_HEADER) === cookieToken;
|
|
6538
|
+
if (contentType.startsWith("application/x-www-form-urlencoded") || contentType.startsWith("multipart/form-data")) {
|
|
6539
|
+
const input = await getActionInputCache(c);
|
|
6540
|
+
return isFormDataValue(input) && readActionCsrfTokenFromFormData(input) === cookieToken;
|
|
6541
|
+
}
|
|
6542
|
+
return false;
|
|
6543
|
+
};
|
|
6325
6544
|
const deserializeActionClientValue = (container, value) => deserializePublicValue(value, { deserializeReference(reference) {
|
|
6326
6545
|
if (!container) throw new TypeError("Action references require an active runtime container.");
|
|
6327
6546
|
if (readActionRefContainerId(reference) !== container.id) throw new TypeError("Action reference does not belong to the active runtime container.");
|
|
@@ -6471,12 +6690,22 @@ const invokeAction = async (id, input, container) => {
|
|
|
6471
6690
|
const isFormSubmission = isFormDataValue(input);
|
|
6472
6691
|
const actionPath = `/__eclipsa/action/${encodeURIComponent(id)}`;
|
|
6473
6692
|
const requestContext = typeof window === "undefined" ? getCurrentServerRequestContext() : null;
|
|
6693
|
+
const csrfToken = typeof document !== "undefined" ? readActionCsrfTokenFromDocument(document) : requestContext ? ensureActionCsrfToken(requestContext) : null;
|
|
6474
6694
|
const currentRouteUrl = typeof window !== "undefined" ? window.location.href : requestContext?.req.header("x-eclipsa-route-url") ?? requestContext?.req.raw.url ?? null;
|
|
6475
6695
|
const requestUrl = requestContext ? new URL(actionPath, requestContext.req.raw.url).href : actionPath;
|
|
6476
|
-
const
|
|
6477
|
-
|
|
6696
|
+
const fetchImpl = requestContext && typeof requestContext.var.fetch === "function" ? requestContext.var.fetch : fetch;
|
|
6697
|
+
const csrfCookie = requestContext && csrfToken ? [requestContext.req.header("cookie"), `${ACTION_CSRF_COOKIE}=${encodeURIComponent(csrfToken)}`].filter(Boolean).join("; ") : null;
|
|
6698
|
+
const response = await fetchImpl(requestUrl, {
|
|
6699
|
+
body: isFormSubmission && csrfToken ? (() => {
|
|
6700
|
+
const next = new FormData();
|
|
6701
|
+
for (const [name, value] of input.entries()) next.append(name, value);
|
|
6702
|
+
next.set(ACTION_CSRF_FIELD, csrfToken);
|
|
6703
|
+
return next;
|
|
6704
|
+
})() : isFormSubmission ? input : JSON.stringify({ input: serializeActionClientValue(container, input) }),
|
|
6478
6705
|
headers: {
|
|
6479
6706
|
accept: `${ACTION_STREAM_CONTENT_TYPE}, ${ACTION_CONTENT_TYPE}`,
|
|
6707
|
+
...csrfCookie ? { cookie: csrfCookie } : {},
|
|
6708
|
+
...csrfToken ? { [ACTION_CSRF_HEADER]: csrfToken } : {},
|
|
6480
6709
|
...currentRouteUrl ? { [ROUTE_RPC_URL_HEADER]: currentRouteUrl } : {},
|
|
6481
6710
|
...isFormSubmission ? {} : { "content-type": ACTION_CONTENT_TYPE }
|
|
6482
6711
|
},
|
|
@@ -6540,6 +6769,7 @@ const getActionFormSubmissionId = async (c) => {
|
|
|
6540
6769
|
const executeActionSubmission = async (id, c) => {
|
|
6541
6770
|
const action = getActionRegistry().get(id);
|
|
6542
6771
|
if (!action) throw new Error(`Unknown action ${id}.`);
|
|
6772
|
+
if (!await isActionCsrfValid(c)) throw new ActionCsrfError();
|
|
6543
6773
|
const input = await readActionSubmissionInput(c);
|
|
6544
6774
|
const result = await composeMiddlewares(c, action.middlewares, action.handler);
|
|
6545
6775
|
if (result instanceof Response) return {
|
|
@@ -6583,6 +6813,15 @@ const createActionFormNode = (id, props) => {
|
|
|
6583
6813
|
hiddenInput.type = "hidden";
|
|
6584
6814
|
hiddenInput.value = id;
|
|
6585
6815
|
form.appendChild(hiddenInput);
|
|
6816
|
+
const csrfToken = readActionCsrfTokenFromDocument(document);
|
|
6817
|
+
if (csrfToken) {
|
|
6818
|
+
const csrfInput = document.createElement("input");
|
|
6819
|
+
csrfInput.name = ACTION_CSRF_FIELD;
|
|
6820
|
+
csrfInput.type = "hidden";
|
|
6821
|
+
csrfInput.value = csrfToken;
|
|
6822
|
+
csrfInput.setAttribute(ACTION_CSRF_INPUT_ATTR, "");
|
|
6823
|
+
form.appendChild(csrfInput);
|
|
6824
|
+
}
|
|
6586
6825
|
for (const [name, value] of Object.entries(props)) {
|
|
6587
6826
|
if (name === "children" || name === "action" || name === "method" || value === false || value === void 0 || value === null) continue;
|
|
6588
6827
|
if (name === "class") {
|
|
@@ -6639,16 +6878,16 @@ const hasAction = (id) => getActionRegistry().has(id);
|
|
|
6639
6878
|
const executeAction = async (id, c) => {
|
|
6640
6879
|
try {
|
|
6641
6880
|
const result = await executeActionSubmission(id, c);
|
|
6642
|
-
return result.kind === "response" ? result.response : toActionResponse(result.value);
|
|
6881
|
+
return applyActionCsrfCookie(result.kind === "response" ? result.response : toActionResponse(result.value), c);
|
|
6643
6882
|
} catch (error) {
|
|
6644
6883
|
const publicError = await transformCurrentPublicError(error, "action");
|
|
6645
|
-
return new Response(JSON.stringify({
|
|
6884
|
+
return applyActionCsrfCookie(new Response(JSON.stringify({
|
|
6646
6885
|
error: toSerializedActionError(publicError),
|
|
6647
6886
|
ok: false
|
|
6648
6887
|
}), {
|
|
6649
6888
|
headers: { "content-type": ACTION_CONTENT_TYPE },
|
|
6650
|
-
status: 500
|
|
6651
|
-
});
|
|
6889
|
+
status: error instanceof ActionCsrfError ? 403 : 500
|
|
6890
|
+
}), c);
|
|
6652
6891
|
}
|
|
6653
6892
|
};
|
|
6654
6893
|
const __eclipsaAction = (id, middlewares, handler) => {
|
|
@@ -6795,6 +7034,6 @@ const __eclipsaAction = (id, middlewares, handler) => {
|
|
|
6795
7034
|
}, id));
|
|
6796
7035
|
};
|
|
6797
7036
|
//#endregion
|
|
6798
|
-
export { shouldReconnectDetachedInsertMarkers as $,
|
|
7037
|
+
export { shouldReconnectDetachedInsertMarkers as $, deserializeValue as $n, __eclipsaLoader as $t, getRuntimeComponentId as A, createPendingSignalError as An, getActionHookMeta as At, refreshRegisteredRouteContainers as B, createRequestFetch as Bn, getRegisteredLoaderHook as Bt, createOnCleanup as C, createContext as Cn, RESUME_FINAL_STATE_ELEMENT_ID as Ct, createStandaloneRuntimeSignal as D, materializeRuntimeContextProvider as Dn, __eclipsaLazy as Dt, createResumeContainer as E, materializeRuntimeContext as En, __eclipsaEvent as Et, notFound as F, applyActionCsrfCookie as Fn, getLoaderHandleMeta as Ft, renderSSRAttr as G, registerClientHooks as Gn, registerLoaderHook as Gt, registerRuntimeScopedStyle as H, getClientHooks as Hn, getSignalMeta as Ht, preserveReusableContentInRoots as I, ensureActionCsrfToken as In, getLoaderHookMeta as It, renderString as J, runHandleError as Jn, setExternalComponentMeta as Jt, renderSSRMap as K, resetClientHooks as Kn, setActionHandleMeta as Kt, primeLocationState as L, readActionCsrfTokenFromDocument as Ln, getNavigateMeta as Lt, getRuntimeSignalId as M, isSuspenseType as Mn, getEventMeta as Mt, getStreamingResumeBootstrapScriptContent as N, ACTION_CSRF_FIELD as Nn, getExternalComponentMeta as Nt, createWatch as O, useContext as On, __eclipsaWatch as Ot, installResumeListeners as P, ACTION_CSRF_INPUT_ATTR as Pn, getLazyMeta as Pt, restoreSignalRefs as Q, withServerRequestContext as Qn, setSignalMeta as Qt, primeRouteModules as R, APP_HOOKS_ELEMENT_ID as Rn, getRegisteredActionHook as Rt, createEffect as S, resolveRouteMetadata as Sn, ACTION_FORM_ATTR$1 as St, createOnVisible as T, getRuntimeContextReference as Tn, __eclipsaComponent as Tt, renderClientInsertable as U, getCurrentServerRequestContext as Un, getWatchMeta as Ut, registerResumeContainer as V, deserializePublicValue as Vn, getRegisteredLoaderHookIds as Vt, renderClientInsertableForOwner as W, markPublicError as Wn, registerActionHook as Wt, restoreResumedExternalComponents as X, toPublicError as Xn, setLoaderHookMeta as Xt, restoreRegisteredRpcHandles as Y, serializePublicValue as Yn, setLoaderHandleMeta as Yt, restoreResumedLocalSignalEffects as Z, transformCurrentPublicError as Zn, setNavigateMeta as Zt, beginSSRContainer as _, ROUTE_REPLACE_ATTR as _n, getRememberedInsertMarkerNodeCount as _t, action as a, loader_exports as an, IS_BROWSER as ar, useRuntimeLocation as at, collectPendingSuspenseBoundaryIds as b, composeRouteMetadata as bn, rememberManagedAttributesForNodes as bt, getActionFormSubmissionId as c, registerLoader as cn, noSerialize as cr, useRuntimeRouteParams as ct, primeActionState as d, ROUTE_DATA_REQUEST_HEADER as dn, writeAsyncSignalSnapshot as dt, consumePendingSsrLoaderIds as en, escapeInlineScriptText as er, syncRuntimeRefMarker as et, registerAction as f, ROUTE_LINK_ATTR as fn, INSERT_MARKER_PREFIX as ft, beginAsyncSSRContainer as g, ROUTE_PREFLIGHT_REQUEST_HEADER as gn, parseInsertMarker as gt, assignRuntimeRef as h, ROUTE_PREFLIGHT_ENDPOINT as hn, parseComponentBoundaryMarker as ht, __eclipsaAction as i, loader as in, serializeValue as ir, tryPatchNodeSequenceInPlace as it, getRuntimeContainer as j, isPendingSignalError as jn, getComponentMeta as jt, getResumePayloadScriptContent as k, Suspense as kn, getActionHandleMeta as kt, getNormalizedActionInput as l, resolvePendingLoaders as ln, useRuntimeSignal as lt, applyResumeHmrUpdateToRegisteredContainers as m, ROUTE_PREFETCH_ATTR as mn, createInsertMarker as mt, ACTION_FORM_ATTR as n, hasLoader as nn, parseSerializedJSON as nr, toResumePayloadSubset as nt, executeAction as o, markPendingSsrLoader as on, IS_SSR as or, useRuntimeNavigate as ot, validator as p, ROUTE_MANIFEST_ELEMENT_ID as pn, createComponentBoundaryHtmlComment as pt, renderSSRValue as q, resolveReroute as qn, setActionHookMeta as qt, ACTION_FORM_FIELD as r, isPendingSsrLoaderError as rn, serializeJSONScriptContent as rr, tryPatchElementShellInPlace as rt, executeActionSubmission as s, primeLoaderState as sn, isNoSerialize as sr, useRuntimeRouteError as st, ACTION_CONTENT_TYPE as t, executeLoader as tn, escapeJSONScriptText as tr, toResumePayload as tt, hasAction as u, ROUTE_DATA_ENDPOINT as un, withRuntimeContainer as ut, bindRuntimeEvent as v, ROUTE_RPC_URL_HEADER as vn, rememberInsertMarkerRange as vt, createOnMount as w, getContextProviderMeta as wn, RESUME_STATE_ELEMENT_ID as wt, createDetachedClientInsertOwner as x, renderRouteMetadataHead as xn, syncManagedAttributeSnapshot as xt, captureClientInsertOwner as y, ROUTE_METADATA_HEAD_ATTR as yn, rememberManagedAttributesForNode as yt, readAsyncSignalSnapshot as z, attachRequestFetch as zn, getRegisteredActionHookIds as zt };
|
|
6799
7038
|
|
|
6800
|
-
//# sourceMappingURL=action-
|
|
7039
|
+
//# sourceMappingURL=action-DCc4fBhy.mjs.map
|