rosefc 0.2.0 → 0.2.1

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.
@@ -64,7 +64,7 @@ function untrack<T>(fn: () => T): T {
64
64
  }
65
65
 
66
66
  // Trade-off: global signal map, shared across all inlined modules
67
- const signalMap: Map<string, SignalEntry> = shared('__rosefd_signalMap', () => new Map());
67
+ const signalMap: Map<string, SignalEntry> = shared('__rosefc_signalMap', () => new Map());
68
68
 
69
69
  // One get-or-create helper shared by state / setState / resumeState.
70
70
  function entry(key: string, initial: unknown): { value: unknown; subs: Set<Subscriber> } {
@@ -125,7 +125,7 @@ export function hasState(key: string): boolean {
125
125
  }
126
126
 
127
127
  // === Per-RENDER $data seeding === The compiled `$data(() => loader())` prologue is const __seeded = isSeeded("k"); const [get.
128
- const seededKeys: Set<string> = shared('__rosefd_seededKeys', () => new Set());
128
+ const seededKeys: Set<string> = shared('__rosefc_seededKeys', () => new Set());
129
129
 
130
130
  export function isSeeded(key: string): boolean {
131
131
  return renderMaps().seeded.has(key);
@@ -155,12 +155,12 @@ export function setState<T>(key: string, value: T): void {
155
155
  if (persistedKeys.has(key)) writeStorage(key, value);
156
156
  }
157
157
 
158
- // F9 ($persist): keys declared with $persist survive reloads — every write lands in localStorage (JSON-encoded under a rosefd: prefix) and every client.
159
- const persistedKeys: Set<string> = shared('__rosefd_persistedKeys', () => new Set());
158
+ // F9 ($persist): keys declared with $persist survive reloads — every write lands in localStorage (JSON-encoded under a rosefc: prefix) and every client.
159
+ const persistedKeys: Set<string> = shared('__rosefc_persistedKeys', () => new Set());
160
160
 
161
161
  function writeStorage(key: string, v: unknown): void {
162
162
  try {
163
- (globalThis as { localStorage?: Storage }).localStorage?.setItem('rosefd:' + key, JSON.stringify(v));
163
+ (globalThis as { localStorage?: Storage }).localStorage?.setItem('rosefc:' + key, JSON.stringify(v));
164
164
  } catch { }
165
165
  }
166
166
 
@@ -170,7 +170,7 @@ export function persistState<T>(key: string, initial: T): [() => T, (v: T) => vo
170
170
  let seeded = initial;
171
171
  let hasStored = false;
172
172
  try {
173
- const raw = (globalThis as { localStorage?: Storage }).localStorage?.getItem('rosefd:' + key);
173
+ const raw = (globalThis as { localStorage?: Storage }).localStorage?.getItem('rosefc:' + key);
174
174
  if (raw != null) {
175
175
  seeded = JSON.parse(raw) as T;
176
176
  hasStored = true;
@@ -184,7 +184,7 @@ export function persistState<T>(key: string, initial: T): [() => T, (v: T) => vo
184
184
  }
185
185
 
186
186
  // F12: the error a {#boundary} just caught — a custom {:fallback} reads it ({$boundaryError()?.message}) to render its own failure UX.
187
- const lastBoundaryError = sharedCell<Error | null>('__rosefd_lastBoundaryError', null);
187
+ const lastBoundaryError = sharedCell<Error | null>('__rosefc_lastBoundaryError', null);
188
188
 
189
189
  export function $setBoundaryError(e: unknown): void {
190
190
  lastBoundaryError.value = e instanceof Error ? e : new Error(String(e));
@@ -195,12 +195,12 @@ export function $boundaryError(): Error | null {
195
195
  }
196
196
 
197
197
 
198
- const cleanups: Set<Cleanup> = shared('__rosefd_cleanups', () => new Set());
198
+ const cleanups: Set<Cleanup> = shared('__rosefc_cleanups', () => new Set());
199
199
 
200
200
 
201
201
  let fxOwner: object | null = null;
202
- const fxByOwner: WeakMap<object, Set<Cleanup>> = shared('__rosefd_fxByOwner', () => new WeakMap());
203
- const keysByOwner: WeakMap<object, Set<string>> = shared('__rosefd_keysByOwner', () => new WeakMap());
202
+ const fxByOwner: WeakMap<object, Set<Cleanup>> = shared('__rosefc_fxByOwner', () => new WeakMap());
203
+ const keysByOwner: WeakMap<object, Set<string>> = shared('__rosefc_keysByOwner', () => new WeakMap());
204
204
  const fxSet = (): Set<Cleanup> => {
205
205
  if (!fxOwner) return cleanups;
206
206
  let s = fxByOwner.get(fxOwner);
@@ -412,7 +412,7 @@ interface EachRow {
412
412
  // attribute (apps use it for active-row styling), its value is part of the html
413
413
  // the reuse check compares, and it carries no closure - forbidding it forced a
414
414
  // full rebuild of every such list on any selection change.
415
- const EACH_REUSE_FORBID = /data-on-|data-use|data-bind-this|data-transition|data-animate|data-rosefd-c|data-rv-island/;
415
+ const EACH_REUSE_FORBID = /data-on-|data-use|data-bind-this|data-transition|data-animate|data-rosefc-c|data-rv-island/;
416
416
  const EACH_HTML_NORM = (h: string): string => h.replace(/\u27e6\/?[a-z]+:\d+\u27e7/g, '').replace(/data-b\d+/g, 'data-b');
417
417
  const normHtml = (row: EachRow): string => (row.norm ??= EACH_HTML_NORM(row.html));
418
418
 
@@ -481,12 +481,12 @@ export function slotBlockMark(
481
481
  const MARK_RE = /^\u27e6([milh]):(\d+)\u27e7$/;
482
482
  const END_RE = /^\u27e6\/([milh]):(\d+)\u27e7$/;
483
483
 
484
- const wired: WeakSet<Node> = shared('__rosefd_wired', () => new WeakSet());
484
+ const wired: WeakSet<Node> = shared('__rosefc_wired', () => new WeakSet());
485
485
 
486
- const pendingIslandObservers: Set<IntersectionObserver> = shared('__rosefd_pendingIslandObservers', () => new Set());
486
+ const pendingIslandObservers: Set<IntersectionObserver> = shared('__rosefc_pendingIslandObservers', () => new Set());
487
487
 
488
488
  const TPL_CACHE_MAX = 512;
489
- const tplCache: Map<string, HTMLTemplateElement> = shared('__rosefd_tplCache', () => new Map());
489
+ const tplCache: Map<string, HTMLTemplateElement> = shared('__rosefc_tplCache', () => new Map());
490
490
 
491
491
  /** Parse an html string into a DocumentFragment. Parsed templates are cached per string and cloned per call, so repeated renders (keyed each rows, if-branch re-entry) skip the innerHTML parse. */
492
492
  export function tpl(html: string): DocumentFragment {
@@ -503,21 +503,21 @@ export function tpl(html: string): DocumentFragment {
503
503
  return t.content.cloneNode(true) as DocumentFragment;
504
504
  }
505
505
 
506
- const HEAD_ATTR = 'data-rosefd-head';
506
+ const HEAD_ATTR = 'data-rosefc-head';
507
507
 
508
508
  export const handlers: Record<string, (e: Event) => void> =
509
- (globalThis as { __rosefd_handlers?: Record<string, (e: Event) => void> }).__rosefd_handlers ??= {};
509
+ (globalThis as { __rosefc_handlers?: Record<string, (e: Event) => void> }).__rosefc_handlers ??= {};
510
510
 
511
511
  // The action dispatcher the client entry installs. Shared, not per-copy: the
512
512
  // elements it dispatches for are in the one document every copy wires, so the
513
513
  // last entry to install wins for all of them - the same rule `handlers` and
514
514
  // `directives` already follow.
515
- const actionDispatchCell = sharedCell<((name: string, e: Event) => void) | null>('__rosefd_actionDispatch', null);
515
+ const actionDispatchCell = sharedCell<((name: string, e: Event) => void) | null>('__rosefc_actionDispatch', null);
516
516
  export function setActionDispatcher(fn: (name: string, e: Event) => void): void {
517
517
  actionDispatchCell.value = fn;
518
518
  }
519
519
 
520
- const directBound: WeakMap<Element, Set<string>> = shared('__rosefd_directBound', () => new WeakMap());
520
+ const directBound: WeakMap<Element, Set<string>> = shared('__rosefc_directBound', () => new WeakMap());
521
521
 
522
522
  /**
523
523
  * F5: `use:NAME` element actions - the plugin point for DOM behaviors
@@ -529,10 +529,10 @@ const directBound: WeakMap<Element, Set<string>> = shared('__rosefd_directBound'
529
529
  * of the reactive graph keeps the action contract one argument.
530
530
  */
531
531
  export const directives: Record<string, (el: Element, param?: unknown) => void | (() => void) | { update?: (p: unknown) => void; destroy?: () => void }> =
532
- (globalThis as { __rosefd_directives?: Record<string, (el: Element, param?: unknown) => void | (() => void) | { update?: (p: unknown) => void; destroy?: () => void }> })
533
- .__rosefd_directives ??= {};
532
+ (globalThis as { __rosefc_directives?: Record<string, (el: Element, param?: unknown) => void | (() => void) | { update?: (p: unknown) => void; destroy?: () => void }> })
533
+ .__rosefc_directives ??= {};
534
534
 
535
- const actionRecords: WeakMap<Element, { update?: (p: unknown) => void; destroy?: () => void }> = shared('__rosefd_actionRecords', () => new WeakMap());
535
+ const actionRecords: WeakMap<Element, { update?: (p: unknown) => void; destroy?: () => void }> = shared('__rosefc_actionRecords', () => new WeakMap());
536
536
 
537
537
  /**
538
538
  * Apply one route's whole <head> (every block of its chain, page first) to the
@@ -603,7 +603,7 @@ function linkIdentitySuffix(rel: string, el: Element): string {
603
603
  return `@${el.getAttribute('href') ?? el.getAttribute('type') ?? el.getAttribute('as') ?? ''}`;
604
604
  }
605
605
 
606
- /** Drop every Rosefd-managed head element (routes without <head> blocks). */
606
+ /** Drop every Rosefc-managed head element (routes without <head> blocks). */
607
607
  export function clearHead(): void {
608
608
  document.head.querySelectorAll(`[${HEAD_ATTR}]`).forEach((el) => el.remove());
609
609
  }
@@ -1031,7 +1031,7 @@ export function wire(root: Node, closes: Array<unknown>, scope?: unknown): Clean
1031
1031
  }, duration + delay);
1032
1032
  });
1033
1033
  } else {
1034
- const custom = (globalThis as any).__rosefdTransitions?.[name];
1034
+ const custom = (globalThis as any).__rosefcTransitions?.[name];
1035
1035
  if (custom) {
1036
1036
  const ret = custom(el, duration, 'out');
1037
1037
  if (typeof ret === 'function') created.push(ret);
@@ -1077,7 +1077,7 @@ export function wire(root: Node, closes: Array<unknown>, scope?: unknown): Clean
1077
1077
  }, duration + delay);
1078
1078
  });
1079
1079
  } else {
1080
- const custom = (globalThis as any).__rosefdTransitions?.[name];
1080
+ const custom = (globalThis as any).__rosefcTransitions?.[name];
1081
1081
  if (custom) {
1082
1082
  const ret = custom(el, duration, dir || 'both');
1083
1083
  if (typeof ret === 'function') created.push(ret);
@@ -1491,7 +1491,7 @@ export function $data<T>(fn: () => T | Promise<T>): Promise<T> {
1491
1491
  return p as Promise<T>;
1492
1492
  }
1493
1493
 
1494
- const bffCache: Map<string, Promise<unknown>> = shared('__rosefd_bffCache', () => new Map());
1494
+ const bffCache: Map<string, Promise<unknown>> = shared('__rosefc_bffCache', () => new Map());
1495
1495
 
1496
1496
  export function loader<T>(key: string, fn: () => Promise<T>): Promise<T> {
1497
1497
  let p = bffCache.get(key) as Promise<T> | undefined;
@@ -1597,7 +1597,7 @@ export async function isolateRender<T>(fn: () => Promise<T>): Promise<T> {
1597
1597
  // The tail of the fallback render queue; see isolateRender. Shared across
1598
1598
  // bundle copies because the tables the renders swap are shared; a module-level
1599
1599
  // mutex would order one copy's renders and let another copy's interleave.
1600
- const renderMutexCell = sharedCell<Promise<void>>('__rosefd_renderMutex', Promise.resolve());
1600
+ const renderMutexCell = sharedCell<Promise<void>>('__rosefc_renderMutex', Promise.resolve());
1601
1601
 
1602
1602
  let requestQuery: Record<string, string> = {};
1603
1603
  /** R248: the request-scoped query slot in the context-store bag. */
@@ -1712,7 +1712,7 @@ type RenderMaps = { data: Map<string, Promise<unknown>>; signals: Map<string, Si
1712
1712
  // same single-render maps, so a $data promise or a seeded key written by one
1713
1713
  // copy's render is visible to the next one's - which is exactly what a
1714
1714
  // client-side navigation between two separately-bundled routes needs.
1715
- const fallbackRenderMaps: RenderMaps = shared('__rosefd_fallbackRenderMaps', () => ({ data: new Map(), signals: signalMap, seeded: seededKeys }));
1715
+ const fallbackRenderMaps: RenderMaps = shared('__rosefc_fallbackRenderMaps', () => ({ data: new Map(), signals: signalMap, seeded: seededKeys }));
1716
1716
 
1717
1717
  function renderMaps(): RenderMaps {
1718
1718
  // The store's map-of-maps is typed Record<string, unknown> but carries symbol
@@ -1859,8 +1859,8 @@ export function resumeState(json: string): void {
1859
1859
  }
1860
1860
 
1861
1861
  const SDUI_TAGS = ['div', 'span', 'a', 'p', 'img', 'ul', 'li', 'button', 'input', 'section', 'h1', 'h2', 'h3', 'nav', 'header', 'footer', 'form', 'label', 'strong', 'em', 'table', 'tr', 'td', 'code', 'pre', 'article', 'main', 'aside'];
1862
- const sduiTagToId: Map<string, number> = shared('__rosefd_sduiTagToId', () => new Map(SDUI_TAGS.map((t, i) => [t, i])));
1863
- const sduiIdToTag: Map<number, string> = shared('__rosefd_sduiIdToTag', () => new Map(SDUI_TAGS.map((t, i) => [i, t])));
1862
+ const sduiTagToId: Map<string, number> = shared('__rosefc_sduiTagToId', () => new Map(SDUI_TAGS.map((t, i) => [t, i])));
1863
+ const sduiIdToTag: Map<number, string> = shared('__rosefc_sduiIdToTag', () => new Map(SDUI_TAGS.map((t, i) => [i, t])));
1864
1864
 
1865
1865
  export interface SduiNode {
1866
1866
  t: string;
@@ -1889,7 +1889,7 @@ export function decodeSdui(json: string): SduiNode {
1889
1889
 
1890
1890
  // The SWR refresh hook the client entry installs - shared like the action
1891
1891
  // dispatcher: it drives the one document every copy wired, last install wins.
1892
- const refreshHookCell = sharedCell<(() => Promise<void>) | null>('__rosefd_refreshHook', null);
1892
+ const refreshHookCell = sharedCell<(() => Promise<void>) | null>('__rosefc_refreshHook', null);
1893
1893
 
1894
1894
  export function setRefreshHook(fn: () => Promise<void>): void {
1895
1895
  refreshHookCell.value = fn;
@@ -1900,7 +1900,7 @@ export function refresh(): Promise<void> {
1900
1900
  return hook ? hook() : Promise.resolve();
1901
1901
  }
1902
1902
 
1903
- const mountQueue: Array<() => void> = shared('__rosefd_mountQueue', () => []);
1903
+ const mountQueue: Array<() => void> = shared('__rosefc_mountQueue', () => []);
1904
1904
 
1905
1905
  export function onMount(fn: () => void): void {
1906
1906
  if (typeof document === 'undefined') return;
@@ -1942,7 +1942,7 @@ const localeCell = sharedCell<{
1942
1942
  dicts: Record<string, Record<string, string>>;
1943
1943
  def: string;
1944
1944
  packs: string[];
1945
- }>('__rosefd_localeCell', { dicts: {}, def: 'en', packs: [] });
1945
+ }>('__rosefc_localeCell', { dicts: {}, def: 'en', packs: [] });
1946
1946
 
1947
1947
  export function setLocales(dicts: Record<string, Record<string, string>>, def: string): void {
1948
1948
  const c = localeCell.value;
@@ -2015,7 +2015,7 @@ export function bestLocale(header: string | null | undefined): string | null {
2015
2015
 
2016
2016
  /** The signal `redirect()` throws. */
2017
2017
  export class RoseRedirect extends Error {
2018
- readonly __rosefd = 'redirect';
2018
+ readonly __rosefc = 'redirect';
2019
2019
  constructor(readonly path: string, readonly status = 307) {
2020
2020
  super(`redirect to ${path}`);
2021
2021
  }
@@ -2023,7 +2023,7 @@ export class RoseRedirect extends Error {
2023
2023
 
2024
2024
  /** The signal `notFound()` throws. */
2025
2025
  export class RoseNotFound extends Error {
2026
- readonly __rosefd = 'notFound';
2026
+ readonly __rosefc = 'notFound';
2027
2027
  constructor() {
2028
2028
  super('notFound()');
2029
2029
  }
@@ -2066,7 +2066,7 @@ export function localeDir(lang: string): string {
2066
2066
  const ICU_RE = /\{([\w$]+),\s*(plural|selectordinal|select),\s*((?:\s*[=\w]+\s*\{(?:[^{}]|\{[^{}]*\})*\})*)\}/g;
2067
2067
  const BRANCH_RE = /([=\w]+)\s*\{((?:[^{}]|\{[^{}]*\})*)\}/g;
2068
2068
 
2069
- const PLURAL_RULES: Map<string, Intl.PluralRules> = shared('__rosefd_pluralRules', () => new Map());
2069
+ const PLURAL_RULES: Map<string, Intl.PluralRules> = shared('__rosefc_pluralRules', () => new Map());
2070
2070
 
2071
2071
  /** The CLDR category of `n` in `locale`, or null where Intl has no answer. */
2072
2072
  function pluralCategory(locale: string, ordinal: boolean, n: number): string | null {
@@ -2115,11 +2115,11 @@ export function $t(key: string, vars?: Record<string, string | number>): string
2115
2115
  return out;
2116
2116
  }
2117
2117
 
2118
- const STORES: Map<string, { value: unknown; subs: Set<Subscriber> }> = shared('__rosefd_stores', () => new Map());
2118
+ const STORES: Map<string, { value: unknown; subs: Set<Subscriber> }> = shared('__rosefc_stores', () => new Map());
2119
2119
  // The SSE transport for $store - shared like the dispatcher and the refresh
2120
2120
  // hook: it is installed once per page by the client entry, and every copy's
2121
2121
  // store writes must travel over the one channel that entry opened.
2122
- const storeTransportCell = sharedCell<((name: string, value: unknown) => void) | null>('__rosefd_storeTransport', null);
2122
+ const storeTransportCell = sharedCell<((name: string, value: unknown) => void) | null>('__rosefc_storeTransport', null);
2123
2123
 
2124
2124
  // A store write (or an applied patch) fans out to whoever read the store inside a
2125
2125
  // tracked scope - the wire() effects that render {store.get()} / {#each store.get()}.
@@ -2198,7 +2198,7 @@ export function $store<T>(name: string, initial: T): { get(): T; set(v: T): void
2198
2198
  // === Network listeners (isomorphic) ===
2199
2199
  // $udp / $tcp / $listen / $websocket let an app open a socket and react to the
2200
2200
  // outside world (LoRa gateways, MQTT-ish feeds, raw TCP devices, live dashboards)
2201
- // from inside a Rosefd app. They only REGISTER a spec here; the host binds them
2201
+ // from inside a Rosefc app. They only REGISTER a spec here; the host binds them
2202
2202
  // via __startNetwork() at serve-worker boot - so a build, a prerender, or a dev
2203
2203
  // hot-re-import never opens a socket. The registries live on globalThis: a dev
2204
2204
  // re-import re-registers specs against the same live handles.
@@ -2212,10 +2212,10 @@ export function $store<T>(name: string, initial: T): { get(): T; set(v: T): void
2212
2212
  // ReferenceError.
2213
2213
  type NetOpts = Record<string, unknown>;
2214
2214
  const __netSpecs: { kind: string; port: number; host?: string; opts: NetOpts }[] =
2215
- (globalThis as any).__rosefd_netSpecs || ((globalThis as any).__rosefd_netSpecs = []);
2216
- const __netHandles: any[] = (globalThis as any).__rosefd_netHandles || ((globalThis as any).__rosefd_netHandles = []);
2215
+ (globalThis as any).__rosefc_netSpecs || ((globalThis as any).__rosefc_netSpecs = []);
2216
+ const __netHandles: any[] = (globalThis as any).__rosefc_netHandles || ((globalThis as any).__rosefc_netHandles = []);
2217
2217
  /** key ('kind:port:host') -> live handle. A Map, not a Set: re-registering a key (per-request render or dev hot re-import) swaps the callbacks in place. */
2218
- const __netBound: Map<string, any> = (globalThis as any).__rosefd_netBound || ((globalThis as any).__rosefd_netBound = new Map());
2218
+ const __netBound: Map<string, any> = (globalThis as any).__rosefc_netBound || ((globalThis as any).__rosefc_netBound = new Map());
2219
2219
 
2220
2220
  function __netBind(spec: { kind: string; port: number; host?: string; opts: NetOpts }): void {
2221
2221
  const key = spec.kind + ':' + spec.port + ':' + (spec.host || '');
@@ -2227,18 +2227,18 @@ function __netBind(spec: { kind: string; port: number; host?: string; opts: NetO
2227
2227
  }
2228
2228
  const o = spec.opts || {};
2229
2229
  if (spec.kind === 'udp') {
2230
- const dg = (globalThis as any).__rosefd_dgram;
2230
+ const dg = (globalThis as any).__rosefc_dgram;
2231
2231
  if (!dg) return;
2232
2232
  const handle = { opts: o, sock: null as any };
2233
2233
  const sock = dg.createSocket('udp4');
2234
2234
  handle.sock = sock;
2235
2235
  sock.on('message', (msg: Buffer, rinfo: unknown) => (handle.opts.onMessage as (m: Buffer, r: unknown) => void)?.(msg, rinfo));
2236
- sock.on('error', (err: Error) => { if (handle.opts.onError) (handle.opts.onError as (e: Error) => void)(err); else console.error('[rosefd] UDP error:', err); });
2236
+ sock.on('error', (err: Error) => { if (handle.opts.onError) (handle.opts.onError as (e: Error) => void)(err); else console.error('[rosefc] UDP error:', err); });
2237
2237
  sock.bind(spec.port, spec.host || '0.0.0.0');
2238
2238
  __netHandles.push(sock);
2239
2239
  __netBound.set(key, handle);
2240
2240
  } else if (spec.kind === 'tcp') {
2241
- const nt = (globalThis as any).__rosefd_net;
2241
+ const nt = (globalThis as any).__rosefc_net;
2242
2242
  if (!nt) return;
2243
2243
  const handle = { opts: o, srv: null as any };
2244
2244
  const idleMs = ((o.idleTimeout as number) ?? 0) * 1000;
@@ -2260,13 +2260,13 @@ function __netBind(spec: { kind: string; port: number; host?: string; opts: NetO
2260
2260
  socket.on('error', () => {});
2261
2261
  });
2262
2262
  handle.srv = srv;
2263
- srv.on('error', (err: Error) => console.error('[rosefd] TCP server error:', err));
2263
+ srv.on('error', (err: Error) => console.error('[rosefc] TCP server error:', err));
2264
2264
  srv.listen(spec.port, spec.host || '0.0.0.0');
2265
2265
  __netHandles.push(srv);
2266
2266
  __netBound.set(key, handle);
2267
2267
  } else if (spec.kind === 'ws') {
2268
- const http = (globalThis as any).__rosefd_http;
2269
- const cryptoMod = (globalThis as any).__rosefd_crypto;
2268
+ const http = (globalThis as any).__rosefc_http;
2269
+ const cryptoMod = (globalThis as any).__rosefc_crypto;
2270
2270
  if (!http || !cryptoMod) return;
2271
2271
  const clients = new Set<any>();
2272
2272
  const handle = {
@@ -2296,7 +2296,7 @@ function __netBind(spec: { kind: string; port: number; host?: string; opts: NetO
2296
2296
  clients.add(wrapped);
2297
2297
  (opts.onConnection as (ws: unknown) => void)?.(wrapped);
2298
2298
  });
2299
- srv.on('error', (err: Error) => { if (handle.opts.onError) (handle.opts.onError as (e: Error) => void)(err); else console.error('[rosefd] WebSocket server error:', err); });
2299
+ srv.on('error', (err: Error) => { if (handle.opts.onError) (handle.opts.onError as (e: Error) => void)(err); else console.error('[rosefc] WebSocket server error:', err); });
2300
2300
  srv.listen(spec.port, spec.host || '0.0.0.0');
2301
2301
  // Keepalive: a half-open peer (network partition, crashed device) never
2302
2302
  // sends FIN, so its socket would sit in `clients` forever and every
@@ -2317,7 +2317,7 @@ function __netBind(spec: { kind: string; port: number; host?: string; opts: NetO
2317
2317
  __netHandles.push(handle);
2318
2318
  __netBound.set(key, handle);
2319
2319
  } else if (spec.kind === 'mqtt') {
2320
- const nt = (globalThis as any).__rosefd_net;
2320
+ const nt = (globalThis as any).__rosefc_net;
2321
2321
  if (!nt) return;
2322
2322
  const client = __mqttConnect(nt, spec.port, spec.host || 'localhost', o);
2323
2323
  __netHandles.push(client);
@@ -2327,7 +2327,7 @@ function __netBind(spec: { kind: string; port: number; host?: string; opts: NetO
2327
2327
  function __netReg(kind: string, port: number, host: string | undefined, opts: NetOpts): void {
2328
2328
  const spec = { kind, port, host, opts };
2329
2329
  __netSpecs.push(spec);
2330
- if ((globalThis as any).__rosefd_netStarted === true) __netBind(spec);
2330
+ if ((globalThis as any).__rosefc_netStarted === true) __netBind(spec);
2331
2331
  }
2332
2332
  /** Drop every pending spec for a key and close its live handle (app-level close()). */
2333
2333
  function __netUnreg(kind: string, port: number, host: string | undefined): void {
@@ -2345,7 +2345,7 @@ function __netUnreg(kind: string, port: number, host: string | undefined): void
2345
2345
  }
2346
2346
  /** Bind every registered listener. Called once at serve-worker boot by the host. */
2347
2347
  export function __startNetwork(): void {
2348
- (globalThis as any).__rosefd_netStarted = true;
2348
+ (globalThis as any).__rosefc_netStarted = true;
2349
2349
  for (const spec of __netSpecs) __netBind(spec);
2350
2350
  }
2351
2351
  /** Close every bound listener. Called on worker shutdown. */
@@ -2505,7 +2505,7 @@ export function $tcp(portOrHost: number | string, opts: NetOpts = {}): any {
2505
2505
  __netReg('tcp', port, opts.host as string | undefined, opts);
2506
2506
  return { ...__NOOP_NET, address: () => ({ port, host: (opts.host as string) || '0.0.0.0' }) };
2507
2507
  }
2508
- const nt = (globalThis as any).__rosefd_net;
2508
+ const nt = (globalThis as any).__rosefc_net;
2509
2509
  if (!nt) return { ...__NOOP_NET };
2510
2510
  const host = typeof portOrHost === 'string' ? portOrHost : (opts.host as string);
2511
2511
  const sock = nt.createConnection({ host, port: opts.port as number });
@@ -2569,7 +2569,7 @@ export function $mqtt(broker: string, opts: NetOpts = {}): any {
2569
2569
  if (h) return (h.connect as () => Promise<void>)();
2570
2570
  __netBind({ kind: 'mqtt', port, host, opts });
2571
2571
  const h2 = bound();
2572
- return h2 ? (h2.connect as () => Promise<void>)() : Promise.reject(new Error('[rosefd] MQTT client unavailable (node:net not injected)'));
2572
+ return h2 ? (h2.connect as () => Promise<void>)() : Promise.reject(new Error('[rosefc] MQTT client unavailable (node:net not injected)'));
2573
2573
  },
2574
2574
  disconnect: () => { (bound() as { disconnect?: () => void } | undefined)?.disconnect?.(); },
2575
2575
  subscribe: (topic: string, sopts: NetOpts = {}) => { (bound() as { subscribe?: (t: string, s: NetOpts) => void } | undefined)?.subscribe?.(topic, sopts); },
@@ -2749,7 +2749,7 @@ function __mqttConnect(nt: any, port: number, host: string, o: NetOpts): any {
2749
2749
  const password = opts.password as string | undefined;
2750
2750
  if (username) flags |= 0x80;
2751
2751
  if (password) flags |= 0x40;
2752
- const parts: Buffer[] = [__mqttStr('MQTT'), Buffer.from([4]), Buffer.from([flags]), __mqttU16((opts.keepalive as number) ?? 60), __mqttStr((opts.clientId as string) || ('rosefd_' + Math.random().toString(16).slice(2, 10)))];
2752
+ const parts: Buffer[] = [__mqttStr('MQTT'), Buffer.from([4]), Buffer.from([flags]), __mqttU16((opts.keepalive as number) ?? 60), __mqttStr((opts.clientId as string) || ('rosefc_' + Math.random().toString(16).slice(2, 10)))];
2753
2753
  if (username) parts.push(__mqttStr(username));
2754
2754
  if (password) parts.push(__mqttStr(password));
2755
2755
  s.write(__mqttPacket(1, 0, Buffer.concat(parts)));
@@ -2777,7 +2777,7 @@ function __mqttConnect(nt: any, port: number, host: string, o: NetOpts): any {
2777
2777
  resolveAck();
2778
2778
  (client.opts.onConnect as (() => void) | undefined)?.();
2779
2779
  } else {
2780
- const err = new Error('[rosefd] MQTT broker refused the connection (code ' + (pkt.body[1] ?? '?') + ')');
2780
+ const err = new Error('[rosefc] MQTT broker refused the connection (code ' + (pkt.body[1] ?? '?') + ')');
2781
2781
  rejectAck(err);
2782
2782
  (client.opts.onError as ((e: Error) => void) | undefined)?.(err);
2783
2783
  }
@@ -2803,7 +2803,7 @@ function __mqttConnect(nt: any, port: number, host: string, o: NetOpts): any {
2803
2803
  client.connected = false;
2804
2804
  if (client.__keepalive) { clearInterval(client.__keepalive); client.__keepalive = null; }
2805
2805
  if (client.__closed) return; // an app-level disconnect() is final
2806
- rejectAck(new Error('[rosefd] MQTT connection closed before CONNACK'));
2806
+ rejectAck(new Error('[rosefc] MQTT connection closed before CONNACK'));
2807
2807
  scheduleReconnect();
2808
2808
  });
2809
2809
  s.on('error', (err: Error) => {
@@ -2870,7 +2870,7 @@ export function $actionErrorOrThrow(): void {
2870
2870
 
2871
2871
  /** The {#boundary} fallback: the action's own message when the failure is a business one. */
2872
2872
  export function $boundaryFallback(err: unknown): string {
2873
- if (err instanceof ActionError) return `<p class="rosefd-action-error">${esc(err.message)}</p>`;
2873
+ if (err instanceof ActionError) return `<p class="rosefc-action-error">${esc(err.message)}</p>`;
2874
2874
  return '<p>This section failed to render.</p>';
2875
2875
  }
2876
2876
 
@@ -2889,7 +2889,7 @@ export const $prepend = (value: unknown): StateDelta => new StateDelta('p', valu
2889
2889
  /** Shallow-merge an object into an object-valued state key. */
2890
2890
  export const $merge = (value: Record<string, unknown>): StateDelta => new StateDelta('m', value);
2891
2891
 
2892
- const pendingDeltas: Map<string, StateDelta> = shared('__rosefd_pendingDeltas', () => new Map());
2892
+ const pendingDeltas: Map<string, StateDelta> = shared('__rosefc_pendingDeltas', () => new Map());
2893
2893
 
2894
2894
  /** Record the deltas of the action that just ran (the dispatch calls this). */
2895
2895
  export function setStateDelta(key: string, delta: StateDelta): void {
@@ -2961,10 +2961,10 @@ export function $sessionCookie(
2961
2961
  export function reportVitals(): void {
2962
2962
  if (typeof window === 'undefined' || typeof performance === 'undefined') return;
2963
2963
  const live: Record<string, number> = {};
2964
- (window as unknown as { __rosefd_vitals?: Record<string, number> }).__rosefd_vitals = live;
2964
+ (window as unknown as { __rosefc_vitals?: Record<string, number> }).__rosefc_vitals = live;
2965
2965
  const send = (name: string, value: number) => {
2966
2966
  live[name] = value;
2967
- window.dispatchEvent(new CustomEvent('rosefd:vitals', { detail: { name, value, route: location.pathname } }));
2967
+ window.dispatchEvent(new CustomEvent('rosefc:vitals', { detail: { name, value, route: location.pathname } }));
2968
2968
  };
2969
2969
  const nav = performance.getEntriesByType?.('navigation')?.[0] as PerformanceNavigationTiming | undefined;
2970
2970
  if (nav) send('ttfb', Math.round(nav.responseStart));
@@ -19,12 +19,12 @@ declare function $setState<T>(key: string, value: T): void;
19
19
  /**
20
20
  * Computed state: compiles to a plain getter over the signals the expression
21
21
  * reads, so every read recomputes - no subscription machinery, never
22
- * serialized. Top-level declarations only. The expression is Rosefd syntax
22
+ * serialized. Top-level declarations only. The expression is Rosefc syntax
23
23
  * (bare state reads are callified at compile time), so it checks as `any`.
24
24
  */
25
25
  declare function $derived(expr: any): Getter<any>;
26
26
 
27
- /** Like $state, but every write mirrors to localStorage (JSON, `rosefd:` prefix) and boot re-seeds from it. */
27
+ /** Like $state, but every write mirrors to localStorage (JSON, `rosefc:` prefix) and boot re-seeds from it. */
28
28
  declare function $persist<T>(initial: T): Getter<T>;
29
29
 
30
30
  /**
@@ -116,9 +116,9 @@ declare function bestLocale(header: string | null | undefined): string | null;
116
116
 
117
117
  // === Globals the compiler injects when a route uses them ===
118
118
  // These live on globalThis in the built bundle; declaring them here keeps
119
- // `rosefd check` honest about code that reaches for them directly.
119
+ // `rosefc check` honest about code that reaches for them directly.
120
120
 
121
- declare var __rosefdAuth: {
121
+ declare var __rosefcAuth: {
122
122
  hashPassword(pw: string): string;
123
123
  verifyPassword(pw: string, hash: string): boolean;
124
124
  signSession(data: Record<string, unknown>, secret: string, ttlSeconds?: number): string;
@@ -127,7 +127,7 @@ declare var __rosefdAuth: {
127
127
  verifyCsrf(token: string, secret: string, sessionId: string): boolean;
128
128
  };
129
129
 
130
- declare var __rosefdKvApi: {
130
+ declare var __rosefcKvApi: {
131
131
  kv: {
132
132
  get(key: string): unknown;
133
133
  set(key: string, value: unknown, ttlSeconds?: number): void;
@@ -137,7 +137,7 @@ declare var __rosefdKvApi: {
137
137
  kvMemo: typeof $kv;
138
138
  };
139
139
 
140
- interface RosefdContentEntry {
140
+ interface RosefcContentEntry {
141
141
  slug: string;
142
142
  title?: string;
143
143
  date?: string;
@@ -151,13 +151,13 @@ interface RosefdContentEntry {
151
151
  [key: string]: unknown;
152
152
  }
153
153
 
154
- declare var __rosefdContent: {
154
+ declare var __rosefcContent: {
155
155
  list(coll: string, filter?: { tag: string }): Array<Record<string, unknown>>;
156
156
  rss(coll: string, opts: { title: string; origin: string; description: string }): string;
157
- get(coll: string, slug: string, vars?: Record<string, string>): RosefdContentEntry | null;
157
+ get(coll: string, slug: string, vars?: Record<string, string>): RosefcContentEntry | null;
158
158
  };
159
159
 
160
- declare var __rosefdExtras: {
160
+ declare var __rosefcExtras: {
161
161
  v: {
162
162
  validate(body: Record<string, unknown>, rules: Record<string, string[]>): {
163
163
  ok: boolean;
@@ -167,8 +167,8 @@ declare var __rosefdExtras: {
167
167
  };
168
168
  };
169
169
 
170
- /** Named view transitions: `globalThis.__rosefdTransitions.pulse = (el, ms) => () => {}` - used by `transition:pulse` in templates. */
171
- declare var __rosefdTransitions: Record<string, (el: HTMLElement, duration: number) => () => void>;
170
+ /** Named view transitions: `globalThis.__rosefcTransitions.pulse = (el, ms) => () => {}` - used by `transition:pulse` in templates. */
171
+ declare var __rosefcTransitions: Record<string, (el: HTMLElement, duration: number) => () => void>;
172
172
 
173
173
 
174
174
  declare function redirect(path: string, status?: 301 | 302 | 303 | 307 | 308): never;
@@ -198,7 +198,7 @@ interface UDPSocket {
198
198
  }
199
199
 
200
200
  /**
201
- * Listen for UDP datagrams (LoRa gateways, sensor feeds) from inside a Rosefd
201
+ * Listen for UDP datagrams (LoRa gateways, sensor feeds) from inside a Rosefc
202
202
  * app. Only registers the spec; the framework binds it at serve-worker boot, so
203
203
  * a build/prerender never opens a socket.
204
204
  */
File without changes