@async/framework 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +23 -7
  3. package/browser.d.ts +4 -7
  4. package/browser.js +143 -116
  5. package/browser.min.js +1 -1
  6. package/browser.ts +143 -116
  7. package/browser.umd.js +143 -116
  8. package/browser.umd.min.js +1 -1
  9. package/{server.d.ts → framework.d.ts} +4 -7
  10. package/framework.ts +5946 -0
  11. package/package.json +25 -17
  12. package/server.js +5945 -0
  13. package/examples/cache/index.html +0 -16
  14. package/examples/cache/main.js +0 -47
  15. package/examples/components/index.html +0 -11
  16. package/examples/components/main.js +0 -26
  17. package/examples/counter/index.html +0 -15
  18. package/examples/counter/main.js +0 -17
  19. package/examples/partials/index.html +0 -15
  20. package/examples/partials/main.js +0 -43
  21. package/examples/product/index.html +0 -32
  22. package/examples/product/main.js +0 -24
  23. package/examples/router/index.html +0 -18
  24. package/examples/router/main.js +0 -52
  25. package/examples/server-call/index.html +0 -21
  26. package/examples/server-call/main.js +0 -22
  27. package/examples/ssr/index.html +0 -12
  28. package/examples/ssr/main.js +0 -89
  29. package/examples/streaming/index.html +0 -16
  30. package/examples/streaming/main.js +0 -30
  31. package/src/app.js +0 -802
  32. package/src/async-signal.js +0 -277
  33. package/src/attributes.js +0 -52
  34. package/src/boundary-receiver.js +0 -302
  35. package/src/browser.js +0 -18
  36. package/src/cache.js +0 -189
  37. package/src/component.js +0 -373
  38. package/src/delay.js +0 -30
  39. package/src/elements.js +0 -63
  40. package/src/handlers.js +0 -219
  41. package/src/html.js +0 -158
  42. package/src/index.js +0 -20
  43. package/src/lazy-registry.js +0 -204
  44. package/src/loader.js +0 -765
  45. package/src/partials.js +0 -133
  46. package/src/registry-store.js +0 -267
  47. package/src/request-context.js +0 -40
  48. package/src/router.js +0 -571
  49. package/src/scheduler.js +0 -300
  50. package/src/server-entry.js +0 -20
  51. package/src/server-registry.js +0 -97
  52. package/src/server.js +0 -357
  53. package/src/signals.js +0 -592
package/src/partials.js DELETED
@@ -1,133 +0,0 @@
1
- import { isTemplateResult, renderTemplate } from "./html.js";
2
- import { attachRegistryInspection, createRegistryStore } from "./registry-store.js";
3
- import { createLazyRegistry, isLazyDescriptor } from "./lazy-registry.js";
4
-
5
- export function createPartialRegistry(initialMap = {}, options = {}) {
6
- const registryStore = options.registry ?? createRegistryStore();
7
- const type = options.type ?? "partial";
8
- const entries = registryStore._map(type);
9
- const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
10
- const lazyPartials = new Map();
11
-
12
- const registry = attachRegistryInspection({
13
- register(id, fn) {
14
- assertId(id);
15
- if (typeof fn !== "function" && !isLazyDescriptor(fn)) {
16
- throw new TypeError(`Partial "${id}" must be a function.`);
17
- }
18
- if (entries.has(id)) {
19
- throw new Error(`Partial "${id}" is already registered.`);
20
- }
21
- entries.set(id, fn);
22
- return id;
23
- },
24
-
25
- registerMany(map) {
26
- for (const [id, fn] of Object.entries(map ?? {})) {
27
- registry.register(id, fn);
28
- }
29
- return registry;
30
- },
31
-
32
- unregister(id) {
33
- assertId(id);
34
- lazyPartials.delete(id);
35
- return entries.delete(id);
36
- },
37
-
38
- resolve(id) {
39
- assertId(id);
40
- const partial = entries.get(id);
41
- if (!isLazyDescriptor(partial)) {
42
- return partial;
43
- }
44
- if (!lazyPartials.has(id)) {
45
- lazyPartials.set(id, async function runLazyPartial(...args) {
46
- const resolved = await lazyRegistry.resolve(type, id, partial);
47
- if (typeof resolved !== "function") {
48
- throw new TypeError(`Partial "${id}" did not resolve to a function.`);
49
- }
50
- return resolved.apply(this, args);
51
- });
52
- }
53
- return lazyPartials.get(id);
54
- },
55
-
56
- async render(id, props = {}, context = {}) {
57
- assertId(id);
58
- const fn = registry.resolve(id);
59
- if (!fn) {
60
- throw new Error(`Partial "${id}" is not registered.`);
61
- }
62
-
63
- const partialContext = {
64
- ...context,
65
- id,
66
- props,
67
- cache: context.cache,
68
- partials: registry
69
- };
70
- const result = await fn.call(partialContext, props);
71
- return normalizePartialResult(result, partialContext);
72
- },
73
-
74
- _adoptMany() {
75
- return registry;
76
- }
77
- }, registryStore, type);
78
-
79
- registry.registerMany(initialMap);
80
- return registry;
81
- }
82
-
83
- export function normalizePartialResult(result, context = {}) {
84
- if (isPartialEnvelope(result)) {
85
- return {
86
- ...result,
87
- html: Object.hasOwn(result, "html") ? renderPartialValue(result.html, context) : result.html
88
- };
89
- }
90
-
91
- return { html: renderPartialValue(result, context) };
92
- }
93
-
94
- function renderPartialValue(value, context) {
95
- if (value?.nodeType) {
96
- return value;
97
- }
98
- if (typeof value === "string") {
99
- return value;
100
- }
101
- if (isTemplateResult(value)) {
102
- return renderTemplate(value, templateRenderOptions(context));
103
- }
104
- return renderTemplate(value, templateRenderOptions(context));
105
- }
106
-
107
- function templateRenderOptions(context) {
108
- return {
109
- attributes: context.loader?.attributes,
110
- signals: context.signals,
111
- bind: context.loader?._registerBinding?.bind(context.loader)
112
- };
113
- }
114
-
115
- function isPartialEnvelope(value) {
116
- return Boolean(
117
- value &&
118
- typeof value === "object" &&
119
- !Array.isArray(value) &&
120
- (Object.hasOwn(value, "html") ||
121
- Object.hasOwn(value, "signals") ||
122
- Object.hasOwn(value, "boundary") ||
123
- Object.hasOwn(value, "redirect") ||
124
- Object.hasOwn(value, "status") ||
125
- Object.hasOwn(value, "cache"))
126
- );
127
- }
128
-
129
- function assertId(id) {
130
- if (typeof id !== "string" || id.length === 0) {
131
- throw new TypeError("Partial id must be a non-empty string.");
132
- }
133
- }
@@ -1,267 +0,0 @@
1
- import { publicRegistryValue } from "./lazy-registry.js";
2
-
3
- const declarationTypes = new Set(["signal", "handler", "server", "partial", "route", "component", "asyncSignal"]);
4
- const cacheTypes = new Set(["cache.browser", "cache.server"]);
5
- const cacheEntryTypes = new Set(["cache.browser.entries", "cache.server.entries"]);
6
- const allTypes = new Set([...declarationTypes, ...cacheTypes, ...cacheEntryTypes]);
7
-
8
- export function createRegistryStore(initial = {}, options = {}) {
9
- const backing = options.backing ?? createBacking();
10
- const target = options.target ?? "server";
11
-
12
- const registry = {
13
- target,
14
-
15
- register(type, id, value) {
16
- const map = registry._map(type);
17
- assertId(type, id);
18
- if (map.has(id)) {
19
- throw new Error(`${type} "${id}" is already registered.`);
20
- }
21
- map.set(id, value);
22
- return id;
23
- },
24
-
25
- registerMany(type, map = {}) {
26
- for (const [id, value] of Object.entries(map ?? {})) {
27
- registry.register(type, id, value);
28
- }
29
- return registry;
30
- },
31
-
32
- set(type, id, value) {
33
- const map = registry._map(type);
34
- assertId(type, id);
35
- map.set(id, value);
36
- return value;
37
- },
38
-
39
- unregister(type, id) {
40
- return registry.delete(type, id);
41
- },
42
-
43
- delete(type, id) {
44
- return registry._map(type).delete(id);
45
- },
46
-
47
- keys(type) {
48
- if (isHiddenInBrowser(type, target)) {
49
- return [];
50
- }
51
- return [...registry._map(type).keys()];
52
- },
53
-
54
- entries(type, entryOptions = {}) {
55
- const normalized = normalizeType(type);
56
- if (isHiddenInBrowser(normalized, entryOptions.target ?? target)) {
57
- return [];
58
- }
59
- return [...registry._map(normalized)].map(([id, value]) => [
60
- id,
61
- publicValue(normalized, id, value, { target, ...entryOptions })
62
- ]);
63
- },
64
-
65
- has(type, id) {
66
- assertId(type, id);
67
- if (isHiddenInBrowser(type, target)) {
68
- return false;
69
- }
70
- return registry._map(type).has(id);
71
- },
72
-
73
- get(type, id, getOptions = {}) {
74
- assertId(type, id);
75
- const normalized = normalizeType(type);
76
- if (isHiddenInBrowser(normalized, getOptions.target ?? target)) {
77
- return undefined;
78
- }
79
- const value = registry._map(normalized).get(id);
80
- if (value === undefined) {
81
- return undefined;
82
- }
83
- return publicValue(normalized, id, value, { target, ...getOptions });
84
- },
85
-
86
- snapshot(snapshotOptions = {}) {
87
- const snapshotTarget = snapshotOptions.target ?? target;
88
- return {
89
- signal: snapshotSignals(backing.signal),
90
- handler: snapshotDescriptors(backing.handler),
91
- server: snapshotDescriptors(backing.server),
92
- partial: snapshotDescriptors(backing.partial),
93
- route: snapshotPlain(backing.route),
94
- component: snapshotDescriptors(backing.component),
95
- asyncSignal: snapshotDescriptors(backing.asyncSignal),
96
- cache: {
97
- browser: snapshotPlain(backing.cache.browser),
98
- server: snapshotPlain(backing.cache.server)
99
- },
100
- entries: {
101
- browser: snapshotCacheEntries(backing.cacheEntries.browser),
102
- server: snapshotTarget === "browser" ? {} : snapshotCacheEntries(backing.cacheEntries.server)
103
- }
104
- };
105
- },
106
-
107
- rawSnapshot() {
108
- return {
109
- signal: Object.fromEntries(backing.signal),
110
- handler: Object.fromEntries(backing.handler),
111
- server: Object.fromEntries(backing.server),
112
- partial: Object.fromEntries(backing.partial),
113
- route: Object.fromEntries(backing.route),
114
- component: Object.fromEntries(backing.component),
115
- asyncSignal: Object.fromEntries(backing.asyncSignal),
116
- cache: {
117
- browser: Object.fromEntries(backing.cache.browser),
118
- server: Object.fromEntries(backing.cache.server)
119
- }
120
- };
121
- },
122
-
123
- view(viewOptions = {}) {
124
- return createRegistryStore(undefined, {
125
- backing,
126
- target: viewOptions.target ?? target
127
- });
128
- },
129
-
130
- _map(type) {
131
- const normalized = normalizeType(type);
132
- if (declarationTypes.has(normalized)) {
133
- return backing[normalized];
134
- }
135
- if (normalized === "cache.browser") {
136
- return backing.cache.browser;
137
- }
138
- if (normalized === "cache.server") {
139
- return backing.cache.server;
140
- }
141
- if (normalized === "cache.browser.entries") {
142
- return backing.cacheEntries.browser;
143
- }
144
- if (normalized === "cache.server.entries") {
145
- return backing.cacheEntries.server;
146
- }
147
- throw new Error(`Unknown Async registry type "${type}".`);
148
- }
149
- };
150
-
151
- applyInitial(registry, initial);
152
- return registry;
153
- }
154
-
155
- export function attachRegistryInspection(target, registry, type) {
156
- Object.defineProperty(target, "registry", {
157
- configurable: true,
158
- enumerable: true,
159
- value: registry
160
- });
161
- target.keys = () => registry.keys(type);
162
- target.entries = () => registry.entries(type);
163
- target.inspect = () => registry.entries(type);
164
- return target;
165
- }
166
-
167
- function createBacking() {
168
- return {
169
- signal: new Map(),
170
- handler: new Map(),
171
- server: new Map(),
172
- partial: new Map(),
173
- route: new Map(),
174
- component: new Map(),
175
- asyncSignal: new Map(),
176
- cache: {
177
- browser: new Map(),
178
- server: new Map()
179
- },
180
- cacheEntries: {
181
- browser: new Map(),
182
- server: new Map()
183
- }
184
- };
185
- }
186
-
187
- function applyInitial(registry, initial = {}) {
188
- registry.registerMany("signal", initial.signal);
189
- registry.registerMany("handler", initial.handler);
190
- registry.registerMany("server", initial.server);
191
- registry.registerMany("partial", initial.partial);
192
- registry.registerMany("route", initial.route);
193
- registry.registerMany("component", initial.component);
194
- registry.registerMany("asyncSignal", initial.asyncSignal);
195
- registry.registerMany("cache.browser", initial.cache?.browser);
196
- registry.registerMany("cache.server", initial.cache?.server);
197
-
198
- const entries = initial.entries ?? {};
199
- for (const [key, value] of Object.entries(entries.browser ?? {})) {
200
- registry.set("cache.browser.entries", key, cacheEntry(value));
201
- }
202
- for (const [key, value] of Object.entries(entries.server ?? {})) {
203
- registry.set("cache.server.entries", key, cacheEntry(value));
204
- }
205
- }
206
-
207
- function normalizeType(type) {
208
- if (!allTypes.has(type)) {
209
- throw new Error(`Unknown Async registry type "${type}".`);
210
- }
211
- return type;
212
- }
213
-
214
- function assertId(type, id) {
215
- if (typeof id !== "string" || id.length === 0) {
216
- throw new TypeError(`${type} id must be a non-empty string.`);
217
- }
218
- }
219
-
220
- function publicValue(type, id, value, options) {
221
- if (type === "server" && options.target === "browser") {
222
- return publicRegistryValue(value, id);
223
- }
224
- if (cacheEntryTypes.has(type)) {
225
- return value?.value;
226
- }
227
- return value;
228
- }
229
-
230
- function isHiddenInBrowser(type, target) {
231
- return type === "cache.server.entries" && target === "browser";
232
- }
233
-
234
- function snapshotSignals(map) {
235
- const snapshot = {};
236
- for (const [id, entry] of map) {
237
- snapshot[id] = typeof entry?.snapshot === "function" ? entry.snapshot() : entry?.value ?? entry;
238
- }
239
- return snapshot;
240
- }
241
-
242
- function snapshotDescriptors(map) {
243
- const snapshot = {};
244
- for (const [id, value] of map) {
245
- snapshot[id] = publicRegistryValue(value, id);
246
- }
247
- return snapshot;
248
- }
249
-
250
- function snapshotPlain(map) {
251
- return Object.fromEntries(map);
252
- }
253
-
254
- function snapshotCacheEntries(map) {
255
- const snapshot = {};
256
- for (const [id, entry] of map) {
257
- snapshot[id] = entry?.value;
258
- }
259
- return snapshot;
260
- }
261
-
262
- function cacheEntry(value) {
263
- if (value && typeof value === "object" && Object.hasOwn(value, "value")) {
264
- return value;
265
- }
266
- return { value };
267
- }
@@ -1,40 +0,0 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
-
3
- export function createRequestContextStore() {
4
- const storage = new AsyncLocalStorage();
5
-
6
- return {
7
- storage,
8
-
9
- run(context, fn, ...args) {
10
- if (typeof fn !== "function") {
11
- throw new TypeError("requestContext.run(context, fn) requires a function.");
12
- }
13
- return storage.run(context ?? {}, fn, ...args);
14
- },
15
-
16
- get() {
17
- return storage.getStore();
18
- },
19
-
20
- snapshot() {
21
- return { ...(storage.getStore() ?? {}) };
22
- }
23
- };
24
- }
25
-
26
- export function readRequestContext(store) {
27
- if (!store) {
28
- return {};
29
- }
30
- if (typeof store.get === "function") {
31
- return store.get() ?? {};
32
- }
33
- if (typeof store.getStore === "function") {
34
- return store.getStore() ?? {};
35
- }
36
- if (typeof store === "object") {
37
- return store;
38
- }
39
- return {};
40
- }