@async/framework 0.9.0 → 0.10.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.
package/src/signals.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { asyncSignal as createAsyncSignal, isAsyncSignal } from "./async-signal.js";
2
2
  import { attachRegistryInspection, createRegistryStore } from "./registry-store.js";
3
+ import { createLazyRegistry, isLazyDescriptor } from "./lazy-registry.js";
3
4
 
4
5
  const signalKind = Symbol.for("@async/framework.signal");
5
6
  const computedKind = Symbol.for("@async/framework.computed");
@@ -122,6 +123,8 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
122
123
  const registryStore = options.registry ?? createRegistryStore();
123
124
  const type = options.type ?? "signal";
124
125
  const entries = registryStore._map(type);
126
+ const asyncDescriptors = registryStore._map("asyncSignal");
127
+ const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
125
128
  const registryCleanups = new Map();
126
129
  const runtimeContext = {};
127
130
  const boundEntries = new Set();
@@ -162,6 +165,7 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
162
165
 
163
166
  ensure(id, initial) {
164
167
  assertId(id);
168
+ materializeAsyncSignal(id);
165
169
  if (!entries.has(id)) {
166
170
  registry.register(id, createSignal(initial));
167
171
  }
@@ -169,18 +173,18 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
169
173
  },
170
174
 
171
175
  has(id) {
172
- return entries.has(id);
176
+ return entries.has(id) || asyncDescriptors.has(id);
173
177
  },
174
178
 
175
179
  get(path) {
176
- const parsed = parsePath(path, entries);
180
+ const parsed = parseRegistryPath(path);
177
181
  track(parsed.path);
178
182
  const entry = requireEntry(entries, parsed.id);
179
183
  return readEntry(entry, parsed.parts);
180
184
  },
181
185
 
182
186
  set(path, value) {
183
- const parsed = parsePath(path, entries);
187
+ const parsed = parseRegistryPath(path);
184
188
  const entry = requireEntry(entries, parsed.id);
185
189
  if (parsed.parts.length === 0) {
186
190
  return entry.set(value);
@@ -199,6 +203,7 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
199
203
 
200
204
  ref(id) {
201
205
  assertId(id);
206
+ materializeAsyncSignal(id);
202
207
  return createRef(registry, id);
203
208
  },
204
209
 
@@ -206,7 +211,7 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
206
211
  if (typeof fn !== "function") {
207
212
  throw new TypeError("subscribe(path, fn) requires a function.");
208
213
  }
209
- const parsed = parsePath(path, entries);
214
+ const parsed = parseRegistryPath(path);
210
215
  const entry = requireEntry(entries, parsed.id);
211
216
  const subscriptionId = ++subscriptionCounter;
212
217
  return entry.subscribe(() => {
@@ -312,6 +317,7 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
312
317
  },
313
318
 
314
319
  _entry(id) {
320
+ materializeAsyncSignal(id);
315
321
  return requireEntry(entries, id);
316
322
  },
317
323
 
@@ -351,6 +357,42 @@ export function createSignalRegistry(initialMap = {}, options = {}) {
351
357
  }
352
358
  }
353
359
 
360
+ function parseRegistryPath(path) {
361
+ if (typeof path !== "string" || path.length === 0) {
362
+ throw new TypeError("Signal path must be a non-empty string.");
363
+ }
364
+ const segments = path.split(".");
365
+ for (let end = segments.length; end > 0; end -= 1) {
366
+ const id = segments.slice(0, end).join(".");
367
+ if (entries.has(id) || asyncDescriptors.has(id)) {
368
+ materializeAsyncSignal(id);
369
+ return { id, parts: segments.slice(end), path };
370
+ }
371
+ }
372
+ const [id, ...parts] = segments;
373
+ return { id, parts, path };
374
+ }
375
+
376
+ function materializeAsyncSignal(id) {
377
+ if (entries.has(id) || !asyncDescriptors.has(id)) {
378
+ return;
379
+ }
380
+ const descriptor = asyncDescriptors.get(id);
381
+ if (!isLazyDescriptor(descriptor) && typeof descriptor !== "function") {
382
+ throw new TypeError(`Async signal "${id}" must be a function or lazy descriptor.`);
383
+ }
384
+ const loader = async function runLazyAsyncSignal(...args) {
385
+ const resolved = await lazyRegistry.resolve("asyncSignal", id, descriptor);
386
+ if (typeof resolved !== "function") {
387
+ throw new TypeError(`Async signal "${id}" did not resolve to a function.`);
388
+ }
389
+ return resolved.apply(this, args);
390
+ };
391
+ const entry = createAsyncSignal(id, loader);
392
+ entries.set(id, entry);
393
+ bindEntry(id, entry);
394
+ }
395
+
354
396
  function scheduleCallback(fn, options = {}) {
355
397
  const scheduler = options.scheduler;
356
398
  if (!scheduler || options.phase === "sync") {