@webjsdev/core 0.7.45 → 0.7.47
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/dist/webjs-core-browser.js +5 -5
- package/dist/webjs-core-browser.js.map +3 -3
- package/dist/webjs-core-testing.js +22 -21
- package/dist/webjs-core-testing.js.map +3 -3
- package/dist/webjs-core.js +7 -6
- package/dist/webjs-core.js.map +3 -3
- package/index-browser.js +5 -0
- package/index.d.ts +4 -0
- package/index.js +5 -0
- package/package.json +2 -2
- package/src/asset-url.d.ts +3 -0
- package/src/asset-url.js +99 -0
- package/src/component.js +137 -2
- package/src/form-action.d.ts +16 -0
- package/src/form-action.js +1257 -0
- package/src/optimistic.js +7 -2
- package/src/render-client.js +1004 -107
- package/src/render-server.js +847 -70
- package/src/router-client.js +338 -23
- package/src/webjs-config.d.ts +49 -17
package/index-browser.js
CHANGED
|
@@ -30,6 +30,11 @@ export { render } from './src/render-client.js';
|
|
|
30
30
|
export { escapeText, escapeAttr } from './src/escape.js';
|
|
31
31
|
export { notFound, redirect, forbidden, unauthorized, isNotFound, isRedirect, isForbidden, isUnauthorized } from './src/nav.js';
|
|
32
32
|
export { cspNonce } from './src/csp-nonce.js';
|
|
33
|
+
export { asset } from './src/asset-url.js';
|
|
34
|
+
// Form actions (#1155). The field name is shared with the server dispatcher;
|
|
35
|
+
// `setFormActionResolver` is server-only wiring and is stripped here, because
|
|
36
|
+
// a browser stub carries its own identity and needs no resolver.
|
|
37
|
+
export { FORM_ACTION_FIELD, FORM_ACTION_ID_KEY } from './src/form-action.js';
|
|
33
38
|
export { repeat, isRepeat } from './src/repeat.js';
|
|
34
39
|
export { Suspense, isSuspense } from './src/suspense.js';
|
|
35
40
|
export { connectWS } from './src/websocket-client.js';
|
package/index.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export type {
|
|
|
52
52
|
WebjsRedirectRule,
|
|
53
53
|
WebjsTrailingSlash,
|
|
54
54
|
WebjsCspConfig,
|
|
55
|
+
WebjsDoctorConfig,
|
|
56
|
+
WebjsDoctorSeverity,
|
|
55
57
|
} from './src/webjs-config.d.ts';
|
|
56
58
|
|
|
57
59
|
// Compile-time serializability typing for server actions (#488): the opt-in
|
|
@@ -104,6 +106,8 @@ export { Task, TaskStatus } from './src/task.js';
|
|
|
104
106
|
// src/*.d.ts (#388 fixed the index.d.ts drift from index.js).
|
|
105
107
|
export { signal, computed, effect, batch, isSignal, Signal } from './src/signal.js';
|
|
106
108
|
export { cspNonce, setCspNonceProvider } from './src/csp-nonce.js';
|
|
109
|
+
export { asset, setAssetUrlProvider } from './src/asset-url.js';
|
|
110
|
+
export { FORM_ACTION_FIELD, FORM_ACTION_ID_KEY, setFormActionResolver } from './src/form-action.js';
|
|
107
111
|
export { takeSeed, scanSeeds, SEED_MISS } from './src/action-seed-client.js';
|
|
108
112
|
export { markStale, registerKeyTags, consumeStale, parseTagHeader, fetchMark } from './src/action-cache-client.js';
|
|
109
113
|
// Client action-abort plumbing (#492): a superseded async render aborts its
|
package/index.js
CHANGED
|
@@ -15,6 +15,11 @@ export { render } from './src/render-client.js';
|
|
|
15
15
|
export { escapeText, escapeAttr } from './src/escape.js';
|
|
16
16
|
export { notFound, redirect, forbidden, unauthorized, isNotFound, isRedirect, isForbidden, isUnauthorized } from './src/nav.js';
|
|
17
17
|
export { cspNonce, setCspNonceProvider } from './src/csp-nonce.js';
|
|
18
|
+
export { asset, setAssetUrlProvider } from './src/asset-url.js';
|
|
19
|
+
// Form actions (#1155). `FORM_ACTION_FIELD` is the hidden field both renderers
|
|
20
|
+
// emit and the server's form dispatcher reads; `setFormActionResolver` is the
|
|
21
|
+
// server-only wiring that maps a real action function back to its identity.
|
|
22
|
+
export { FORM_ACTION_FIELD, FORM_ACTION_ID_KEY, setFormActionResolver } from './src/form-action.js';
|
|
18
23
|
export { repeat, isRepeat } from './src/repeat.js';
|
|
19
24
|
export { Suspense, isSuspense } from './src/suspense.js';
|
|
20
25
|
export { connectWS } from './src/websocket-client.js';
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.47",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "The runtime for WebJs, a full-stack JavaScript framework built on web components with server-side rendering and no build step. Ships the html and css template tags, the WebComponent base class, signals, directives, and the isomorphic renderers.",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
package/src/asset-url.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a `public/` asset url to its content-fingerprinted form, so a
|
|
3
|
+
* deploy that changes the file changes the url and no cache can serve the
|
|
4
|
+
* previous bytes.
|
|
5
|
+
*
|
|
6
|
+
* import { html, asset } from '@webjsdev/core';
|
|
7
|
+
* html`<link rel="stylesheet" href=${asset('/public/app.css')}>`
|
|
8
|
+
*
|
|
9
|
+
* On the server, `@webjsdev/server` installs a provider that appends
|
|
10
|
+
* `?v=<content-hash>`; the framework then serves a `?v=`-carrying request
|
|
11
|
+
* `immutable` for a year instead of the short fallback. On the browser there
|
|
12
|
+
* is no provider and the path is returned UNCHANGED.
|
|
13
|
+
*
|
|
14
|
+
* USE IT IN A PAGE, LAYOUT, OR METADATA ROUTE, not inside a component that
|
|
15
|
+
* ships to the browser. Those modules render only on the server, so the
|
|
16
|
+
* fingerprinted url is the one the browser sees and keeps. A component is
|
|
17
|
+
* different: hydration is a full client RE-RENDER (`render-client.js` drops
|
|
18
|
+
* the hydrate marker and `createInstance` replaces the SSR'd children), so
|
|
19
|
+
* the client value overwrites the server one. With no provider in the
|
|
20
|
+
* browser, `asset()` returns the bare path there, so an `<img src=${asset(
|
|
21
|
+
* '/public/logo.svg')}>` inside a shipping component paints the hashed url
|
|
22
|
+
* at SSR and then swaps it for the un-hashed one on upgrade, fetching the
|
|
23
|
+
* same bytes twice and leaving the short-lived url in the DOM. It still
|
|
24
|
+
* WORKS (the path is always valid), which is why this is a convention rather
|
|
25
|
+
* than a `webjs check` rule, but it silently forfeits the caching the call
|
|
26
|
+
* was for. Import the asset url into a layout, or accept the plain path.
|
|
27
|
+
*
|
|
28
|
+
* The sibling `cspNonce()` carries the same server/browser asymmetry and the
|
|
29
|
+
* same scoping, for the same hydration reason.
|
|
30
|
+
*
|
|
31
|
+
* Call it INSIDE the render function, not at module scope. A depth-0 call is a
|
|
32
|
+
* module-scope side effect, which the elision analyser reads as client work, so
|
|
33
|
+
* hoisting `const CSS = asset('/public/app.css')` pins the whole page or layout
|
|
34
|
+
* into the browser bundle for no benefit. Resolver ordering is not the issue
|
|
35
|
+
* (the server installs its resolver at boot, before any app module loads).
|
|
36
|
+
*
|
|
37
|
+
* Under `webjs.basePath`, write the prefix yourself: `asset('/app/public/x.css')`.
|
|
38
|
+
* The framework only base-path-prefixes urls IT emits, so an author-written
|
|
39
|
+
* url is already your responsibility (an un-prefixed one 404s under a
|
|
40
|
+
* sub-path deploy with or without this helper). `asset()` strips the prefix
|
|
41
|
+
* to find the file and keeps it on the url it returns.
|
|
42
|
+
*
|
|
43
|
+
* Mark files that change only with a DEPLOY. The hash is memoized for the
|
|
44
|
+
* process lifetime, and prod never rebuilds, so a `public/` file rewritten in
|
|
45
|
+
* place while the server runs keeps its old url while being served `immutable`
|
|
46
|
+
* for a year. A build artifact (a compiled stylesheet, a bundled script, a
|
|
47
|
+
* committed image) is exactly right; a runtime-written upload is not, and
|
|
48
|
+
* should keep its plain path (or carry its own version in the filename).
|
|
49
|
+
*
|
|
50
|
+
* Why this is opt-in rather than automatic. An earlier attempt (#1196)
|
|
51
|
+
* rewrote asset urls by matching the assembled HTML, and two deep-review
|
|
52
|
+
* rounds found six major defects, five of them the same bug: at that layer
|
|
53
|
+
* framework output and author data are indistinguishable, so the matcher kept
|
|
54
|
+
* touching things it did not own (a custom element's reactive prop, a
|
|
55
|
+
* rendered code sample, a `rel=preload` hint whose real request comes from
|
|
56
|
+
* CSS, a data-driven `src` pointing at `/.env`). Marking the url at the point
|
|
57
|
+
* the author writes it makes the meaning unambiguous, so the blast radius is
|
|
58
|
+
* exactly the set of urls someone deliberately marked and nothing else in the
|
|
59
|
+
* document is ever read or rewritten.
|
|
60
|
+
*
|
|
61
|
+
* Scope note: only a `public/` path is fingerprinted, matching what the
|
|
62
|
+
* static-asset route will actually serve. Anything else is returned
|
|
63
|
+
* unchanged rather than guessed at.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/** @type {((path: string) => string) | null} */
|
|
67
|
+
let _provider = null;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Internal: server-only wiring. `@webjsdev/server` calls this once at load
|
|
71
|
+
* time to install the real resolver. Browser builds never call it, so
|
|
72
|
+
* `asset()` stays an identity function there.
|
|
73
|
+
*
|
|
74
|
+
* @param {(path: string) => string} fn
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
export function setAssetUrlProvider(fn) {
|
|
78
|
+
_provider = fn;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The runtime function. Returns the fingerprinted url on the server, or the
|
|
83
|
+
* path unchanged when there is no provider (browser), when fingerprinting is
|
|
84
|
+
* off (dev, so dev output stays byte-identical), or when the file cannot be
|
|
85
|
+
* resolved. Every failure mode degrades to the plain path, never to a broken
|
|
86
|
+
* one.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} path a root-absolute same-origin path, e.g. `/public/app.css`
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
export function asset(path) {
|
|
92
|
+
if (typeof path !== 'string' || !path) return path;
|
|
93
|
+
if (!_provider) return path;
|
|
94
|
+
try {
|
|
95
|
+
return _provider(path) || path;
|
|
96
|
+
} catch {
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/component.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { render as clientRender } from './render-client.js';
|
|
2
2
|
import { setActiveActionSignal } from './action-abort-client.js';
|
|
3
|
+
import { carriesFunction } from './form-action.js';
|
|
3
4
|
import { isCSS, adoptStyles } from './css.js';
|
|
4
5
|
import { register, tagOf } from './registry.js';
|
|
5
6
|
import { parse as deserializeProp } from './serialize.js';
|
|
@@ -99,6 +100,63 @@ function defaultHasChanged(a, b) {
|
|
|
99
100
|
return a !== b;
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* `String(v)` that cannot itself throw.
|
|
105
|
+
*
|
|
106
|
+
* A rejection reason is whatever the author rejected with, and not every value
|
|
107
|
+
* converts to a primitive: `Object.create(null)` has no `toString`, and a
|
|
108
|
+
* revoked Proxy throws on any operation. Coercing one in an error-reporting
|
|
109
|
+
* path turned a reportable failure into a second, unreported one.
|
|
110
|
+
*
|
|
111
|
+
* @param {unknown} v
|
|
112
|
+
* @returns {string}
|
|
113
|
+
*/
|
|
114
|
+
function safeString(v) {
|
|
115
|
+
try {
|
|
116
|
+
return String(v);
|
|
117
|
+
} catch {
|
|
118
|
+
return Object.prototype.toString.call(v);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Warn that a function value was dropped rather than reflected (#1169).
|
|
124
|
+
*
|
|
125
|
+
* A silently missing attribute is its own confusion, so say what went and why.
|
|
126
|
+
* The message deliberately does NOT include the value: printing the source is
|
|
127
|
+
* the leak this guard exists to prevent, and a server log is not always a
|
|
128
|
+
* private place.
|
|
129
|
+
*
|
|
130
|
+
* UNCONDITIONAL, matching the `.prop=${fn}` unserializable-value drop in
|
|
131
|
+
* `render-server.js`, which is the sibling path this guard mirrors. Two
|
|
132
|
+
* reasons not to gate it on a dev flag. It fires only on a genuine mistake (a
|
|
133
|
+
* function is never a meaningful attribute value), so there is no volume to
|
|
134
|
+
* suppress, and reflection runs per assignment rather than per frame.
|
|
135
|
+
*
|
|
136
|
+
* More decisively, a `NODE_ENV` gate does not survive the dist build.
|
|
137
|
+
* `scripts/build-framework-dist.js` runs esbuild with `platform: 'browser'`
|
|
138
|
+
* and `minify: true`, which substitutes `process.env.NODE_ENV` with
|
|
139
|
+
* `"production"` and folds the check to a constant. The SSR half of the
|
|
140
|
+
* warning would then be unreachable in every published build, which is how
|
|
141
|
+
* every installed app runs, and SSR is the half that matters most, since that
|
|
142
|
+
* is where the leaked source reached visitors.
|
|
143
|
+
*
|
|
144
|
+
* @param {{ constructor: unknown, tagName?: string }} host
|
|
145
|
+
* @param {string} propName
|
|
146
|
+
* @param {string} attrName
|
|
147
|
+
*/
|
|
148
|
+
function warnFunctionReflection(host, propName, attrName) {
|
|
149
|
+
if (typeof console === 'undefined' || !console.warn) return;
|
|
150
|
+
const tag = tagOf(/** @type any */ (host.constructor)) || host.tagName?.toLowerCase() || 'unknown';
|
|
151
|
+
console.warn(
|
|
152
|
+
`[webjs] reflect:true property "${propName}" on <${tag}> holds a function `
|
|
153
|
+
+ `(or an array carrying one), which has no HTML attribute representation. `
|
|
154
|
+
+ `Removing "${attrName}" instead of stringifying it (a stringified function `
|
|
155
|
+
+ `writes its source into the page, so a server action's body would ship to `
|
|
156
|
+
+ `the browser). Pass a string, or drop reflect on this property.`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
102
160
|
/**
|
|
103
161
|
* A minimal base for HTML Custom Elements that mirrors Lit's ergonomics
|
|
104
162
|
* while staying JSDoc-only and no-build.
|
|
@@ -666,13 +724,43 @@ class WebComponentBase extends Base {
|
|
|
666
724
|
const serialized = decl.converter.toAttribute(value, decl.type);
|
|
667
725
|
if (serialized == null) this.removeAttribute(attrName);
|
|
668
726
|
else this.setAttribute(attrName, serialized);
|
|
727
|
+
} else if (typeof value === 'function') {
|
|
728
|
+
// #1169: the value IS a function, and no branch below has a
|
|
729
|
+
// meaningful serialization for one. `String(fn)` is the function's
|
|
730
|
+
// SOURCE, so a reflected `'use server'` action would ship its whole
|
|
731
|
+
// body, closure secrets included, to every visitor, and
|
|
732
|
+
// `JSON.stringify(fn)` is `undefined`, which `setAttribute` writes as
|
|
733
|
+
// the literal string "undefined". Treat it like `null` and remove the
|
|
734
|
+
// attribute, matching what the `.prop=${fn}` SSR binding already does
|
|
735
|
+
// for an unserializable value.
|
|
736
|
+
//
|
|
737
|
+
// Placed AFTER the converter branch on purpose: a custom
|
|
738
|
+
// `toAttribute` is author-controlled, so its author has taken
|
|
739
|
+
// responsibility for serializing whatever they are handed.
|
|
740
|
+
this.removeAttribute(attrName);
|
|
741
|
+
warnFunctionReflection(this, propName, attrName);
|
|
669
742
|
} else if (decl.type === Boolean) {
|
|
670
743
|
if (value) this.setAttribute(attrName, '');
|
|
671
744
|
else this.removeAttribute(attrName);
|
|
672
745
|
} else if (value == null) {
|
|
673
746
|
this.removeAttribute(attrName);
|
|
674
747
|
} else if (decl.type === Object || decl.type === Array) {
|
|
748
|
+
// A JSON-typed prop CARRYING a function is safe and stays whole:
|
|
749
|
+
// `JSON.stringify` drops a function to `null` in an array and omits
|
|
750
|
+
// the key in an object, so `[1, 2, fn]` serializes to `[1,2,null]`
|
|
751
|
+
// with no source and no data loss. Refusing here would discard the
|
|
752
|
+
// `1` and the `2` on a path that never leaked.
|
|
675
753
|
this.setAttribute(attrName, JSON.stringify(value));
|
|
754
|
+
} else if (carriesFunction(value)) {
|
|
755
|
+
// The string fall-through is the one place a CARRIED function still
|
|
756
|
+
// leaks. `String([fn])` is `Array.prototype.join`, which runs
|
|
757
|
+
// `String()` on every element, so `[serverAction]` writes the same
|
|
758
|
+
// source `serverAction` does. Hence the recursive predicate the
|
|
759
|
+
// form-action guard already uses, rather than a bare `typeof`, but
|
|
760
|
+
// scoped to this branch rather than applied above, where JSON
|
|
761
|
+
// handles the same shape losslessly.
|
|
762
|
+
this.removeAttribute(attrName);
|
|
763
|
+
warnFunctionReflection(this, propName, attrName);
|
|
676
764
|
} else {
|
|
677
765
|
this.setAttribute(attrName, String(value));
|
|
678
766
|
}
|
|
@@ -1176,7 +1264,7 @@ class WebComponentBase extends Base {
|
|
|
1176
1264
|
// (shouldUpdate=false) running during the fetch does NOT resolve
|
|
1177
1265
|
// updateComplete early: the pending commit owns the resolution.
|
|
1178
1266
|
this.__pendingAsyncCommits = (this.__pendingAsyncCommits || 0) + 1;
|
|
1179
|
-
|
|
1267
|
+
const settle = () => {
|
|
1180
1268
|
// Always decrement first, even when superseded, so the in-flight
|
|
1181
1269
|
// count never leaks (a superseded cycle returns below without
|
|
1182
1270
|
// committing, but it is no longer pending).
|
|
@@ -1189,6 +1277,37 @@ class WebComponentBase extends Base {
|
|
|
1189
1277
|
}
|
|
1190
1278
|
// --- 7-8 + updateComplete ---
|
|
1191
1279
|
this._postCommit(changedProperties);
|
|
1280
|
+
};
|
|
1281
|
+
// A rejection has to settle the cycle too, or the counter stays >= 1
|
|
1282
|
+
// forever: that both wedges `updateComplete` for this instance and
|
|
1283
|
+
// disables the `!this.__pendingAsyncCommits` escape below for every
|
|
1284
|
+
// later non-committing cycle. `_commitAsync` is written not to reject,
|
|
1285
|
+
// but it is not the only thenable that reaches here: `update()` is a
|
|
1286
|
+
// documented override point and may return one, so the handler belongs
|
|
1287
|
+
// at this site rather than only at the one implementation of it.
|
|
1288
|
+
pendingCommit.then(settle, (error) => {
|
|
1289
|
+
// The token check gates the DOM write, NOT the release. A superseded
|
|
1290
|
+
// cycle rejecting late (a discarded fetch, or the #492 abort of its own
|
|
1291
|
+
// in-flight action surfacing as an AbortError) must not commit its
|
|
1292
|
+
// error state over the newer render that already replaced it, which is
|
|
1293
|
+
// the same rule both handlers inside _commitAsync follow. `settle()`
|
|
1294
|
+
// still runs either way: it does its own token check before touching
|
|
1295
|
+
// anything, and the decrement it owns has to happen regardless or the
|
|
1296
|
+
// counter wedges exactly as it did before this handler existed.
|
|
1297
|
+
// `finally`, not a trailing statement: the release must survive
|
|
1298
|
+
// anything the reporting path throws. `String(error)` is evaluated in
|
|
1299
|
+
// the argument expression, OUTSIDE _handleRenderError's own try/catch,
|
|
1300
|
+
// so a rejection reason that cannot be coerced (a null-prototype
|
|
1301
|
+
// object, a revoked Proxy) threw a TypeError straight out of this
|
|
1302
|
+
// handler and skipped the decrement, wedging the counter in exactly
|
|
1303
|
+
// the way this handler exists to prevent.
|
|
1304
|
+
try {
|
|
1305
|
+
if (token === this.__renderToken) {
|
|
1306
|
+
this._handleRenderError(error instanceof Error ? error : new Error(safeString(error)));
|
|
1307
|
+
}
|
|
1308
|
+
} finally {
|
|
1309
|
+
settle();
|
|
1310
|
+
}
|
|
1192
1311
|
});
|
|
1193
1312
|
return;
|
|
1194
1313
|
}
|
|
@@ -1399,7 +1518,23 @@ class WebComponentBase extends Base {
|
|
|
1399
1518
|
return Promise.resolve(pending).then(
|
|
1400
1519
|
(tpl) => {
|
|
1401
1520
|
if (token !== this.__renderToken) return; // superseded by a newer render
|
|
1402
|
-
|
|
1521
|
+
// The COMMIT throws too, not just the fetch, and a sibling rejection
|
|
1522
|
+
// handler cannot see it: `.then(onFulfil, onRejected)` never routes
|
|
1523
|
+
// onFulfil's own throw to onRejected. Left uncaught, the returned
|
|
1524
|
+
// promise rejected and _performRender's `.then` had no rejection
|
|
1525
|
+
// handler, so the error escaped as an unhandled rejection,
|
|
1526
|
+
// renderError() never ran, __pendingAsyncCommits never decremented
|
|
1527
|
+
// (wedging it >= 1 forever, which also disables the non-committing
|
|
1528
|
+
// cycle's _resolveUpdate escape), and updateComplete never settled.
|
|
1529
|
+
// The sync path routes an identical throw to the same boundary, so
|
|
1530
|
+
// this is what makes the two paths agree.
|
|
1531
|
+
try {
|
|
1532
|
+
clientRender(tpl, this._renderRoot);
|
|
1533
|
+
} catch (commitError) {
|
|
1534
|
+
this._handleRenderError(
|
|
1535
|
+
commitError instanceof Error ? commitError : new Error(String(commitError)),
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1403
1538
|
},
|
|
1404
1539
|
(error) => {
|
|
1405
1540
|
if (token !== this.__renderToken) return;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Form actions (#1155): `<form action=${importedAction}>` and its wire. */
|
|
2
|
+
|
|
3
|
+
/** The hidden field carrying a bound action's identity into the submission. */
|
|
4
|
+
export const FORM_ACTION_FIELD: '__webjs_action';
|
|
5
|
+
|
|
6
|
+
/** The property a generated client RPC stub carries its own identity on. */
|
|
7
|
+
export const FORM_ACTION_ID_KEY: '$$webjsAction';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Server-only wiring: maps a REAL server-action function back to its
|
|
11
|
+
* `<hash>/<fn>` identity. `@webjsdev/server` installs it at boot; the browser
|
|
12
|
+
* bundle drops it, because a stub carries its identity on itself.
|
|
13
|
+
*/
|
|
14
|
+
export function setFormActionResolver(
|
|
15
|
+
fn: (fn: Function) => string | null | Promise<string | null>,
|
|
16
|
+
): void;
|