@gyral/core 0.1.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @gyral/core
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 74054b3: Smaller and faster, with no public API change:
8
+
9
+ - **No Effect.** The command interpreter is now plain TypeScript with no runtime dependencies,
10
+ so `@gyral/core` depends only on Lit. An empty app drops from about 49 KiB to about 11 KiB
11
+ gzipped, starts faster and uses less memory; rendering speed is unchanged (ADR 0015).
12
+ - **Template whitespace minification.** `html` and `svg` remove indentation whitespace between
13
+ tags (once per template, on server and client alike), so lists build far fewer DOM nodes and
14
+ bulk list updates are 9–22% faster. Inline spacing is kept as one space; `<pre>`,
15
+ `<textarea>`, `<script>`, `<style>`, attributes and comments are unchanged. For exact
16
+ whitespace under CSS `white-space: pre*`, use `html` from `lit`.
17
+ - **lit-html leak guidance.** lit-html 3.3.1 and later leak a comment node per removed
18
+ `repeat()` item (lit/lit#5298). Development builds warn once when an affected version is
19
+ loaded; pin lit-html to 3.3.0 with an override until it is fixed upstream
20
+ (docs/references/consumer-setup.md). New `create-gyral` apps are pinned.
21
+
22
+ ### Patch Changes
23
+
24
+ - a7275e8: Copyright and package author are Mike Zupper.
25
+ - fa0156a: `focus()` returned by a reducer whose update renders inside a View Transition now runs after
26
+ the new view has rendered, instead of before it (it used to warn "matched no focusable element").
27
+
3
28
  ## 0.1.0
4
29
 
5
30
  ### Minor Changes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 The Zoop Troop, Inc.
3
+ Copyright (c) 2026 Mike Zupper
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/NOTICE CHANGED
@@ -1,5 +1,5 @@
1
1
  Gyral
2
- Copyright (c) 2026 The Zoop Troop, Inc.
2
+ Copyright (c) 2026 Mike Zupper
3
3
 
4
4
  Gyral is inspired by Cycle.js (https://github.com/cyclejs/cyclejs), released
5
5
  under the MIT License:
package/README.md CHANGED
@@ -46,5 +46,5 @@ consumer setup guide (peer dependencies, Vite preset, SSR checklist):
46
46
 
47
47
  ## License
48
48
 
49
- MIT © The Zoop Troop, Inc. See LICENSE and NOTICE (Cycle.js attribution). Gyral, gyraljs and
50
- the Gyral logo are trademarks of The Zoop Troop, Inc.
49
+ MIT © Mike Zupper. See LICENSE and NOTICE (Cycle.js attribution). Gyral, gyraljs and
50
+ the Gyral logo are trademarks of Mike Zupper.
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAqEA,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAoB,MAAuB;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,gFAAgF;AAChF,MAAM,UAAU,OAAO,CACrB,MAAuB,EACvB,KAAQ,EACR,QAAuC;IAEvC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAC;IACjD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,KAAK;QACL,gFAAgF;QAChF,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAW,CAAC;QACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,SAAS,CAAC,KAAU,CAAC,EAAE,CAAC;QAC5F,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CACvB,IAAgB;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,IAAgE,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC","sourcesContent":["// Effects as data (docs/design-docs/0006-effects-and-drivers.md). Plain TypeScript only:\nimport type { IntentRejected } from './types.js';\n// the Effect-based interpreter lives in ./internal/.\n\n/** How commands in the same lane interact. See ADR 0006 for the table. */\nexport type Concurrency = 'merge' | 'switch' | 'exhaust' | 'queue';\n\nexport interface RetryPolicy {\n /** Retries after the first failure. */\n readonly times: number;\n /** Delay before each retry (the base delay for exponential backoff). Default 0. */\n readonly delayMs?: number;\n readonly backoff?: 'fixed' | 'exponential';\n}\n\nexport interface DriverContext<O = unknown> {\n /** Aborts when the command is switched away or its component disconnects. */\n readonly signal: AbortSignal;\n /**\n * Delivers an extra result while the command runs (streaming drivers: routers, timers,\n * sockets). Each value goes through the command's `onSuccess`. Ignored once the command\n * has settled or been aborted. A streaming `run` usually never resolves; abort ends it.\n */\n readonly emit: (output: O) => void;\n}\n\n/** Performs one kind of side effect. A plain object: easy to fake in tests. */\nexport interface Driver<I, O, E = unknown> {\n readonly name: string;\n readonly run: (input: I, ctx: DriverContext<O>) => O | Promise<O>;\n /** Default policy for this driver's commands. Default `'merge'`. */\n readonly concurrency?: Concurrency;\n readonly retry?: RetryPolicy;\n /** Turns a thrown or rejected value into this driver's typed error. */\n readonly toError?: (cause: unknown) => E;\n}\n\n/** Any driver, with its input type erased. */\nexport type AnyDriver = Driver<never, unknown>;\n\n/** Drivers substituted by name (test fakes, configured instances). */\nexport type DriverOverrides = Readonly<Record<string, AnyDriver>>;\n\n/** A side effect described as data, plus pure mappers from its outcome to messages. */\nexport interface Command<M> {\n readonly driver: AnyDriver;\n readonly input: unknown;\n /** Concurrency lane. Default: the driver's name. */\n readonly key?: string;\n readonly concurrency?: Concurrency;\n readonly onSuccess: (output: unknown) => M | undefined;\n /** If omitted, failures are logged and dropped. */\n readonly onFailure?: (error: unknown) => M | undefined;\n}\n\nexport interface CommandHandlers<O, E, MS, MF = MS> {\n readonly onSuccess: (output: O) => MS | undefined;\n readonly onFailure?: (error: E) => MF | undefined;\n readonly key?: string;\n readonly concurrency?: Concurrency;\n}\n\n/**\n * What a reducer (or `init`) returns: new state, optionally with commands to run. Commands may\n * also answer with the framework message `IntentRejected` (e.g. `submitForm` in @gyral/http\n * when the server rejects a form), which goes to the optional `IntentRejected` reducer.\n */\nexport type Next<S, M> = S | readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n\n/** Identity helper so driver literals infer `I`, `O` and `E`. */\nexport function defineDriver<I, O, E = unknown>(driver: Driver<I, O, E>): Driver<I, O, E> {\n return driver;\n}\n\n/** Builds a command whose mappers are typed by the driver's input, output and error. */\n// Two message parameters so success and failure may produce different variants.\nexport function command<I, O, E, MS, MF = MS>(\n driver: Driver<I, O, E>,\n input: I,\n handlers: CommandHandlers<O, E, MS, MF>,\n): Command<MS | MF> {\n const { onFailure, key, concurrency } = handlers;\n return {\n driver: driver,\n input,\n // Sound: the interpreter only passes this driver's output and toError() result.\n onSuccess: (output) => handlers.onSuccess(output as O),\n ...(onFailure === undefined ? {} : { onFailure: (error: unknown) => onFailure(error as E) }),\n ...(key === undefined ? {} : { key }),\n ...(concurrency === undefined ? {} : { concurrency }),\n };\n}\n\n/** Splits a reducer result into state and commands. State is never an array (ADR 0006). */\nexport function splitNext<S, M>(\n next: Next<S, M>,\n): readonly [S, ReadonlyArray<Command<M | IntentRejected>>] {\n if (Array.isArray(next) && next.length === 2 && Array.isArray(next[1])) {\n return next as readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n }\n return [next as S, []];\n}\n"]}
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAqEA,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAoB,MAAuB;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,gFAAgF;AAChF,MAAM,UAAU,OAAO,CACrB,MAAuB,EACvB,KAAQ,EACR,QAAuC;IAEvC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAC;IACjD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,KAAK;QACL,gFAAgF;QAChF,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAW,CAAC;QACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,SAAS,CAAC,KAAU,CAAC,EAAE,CAAC;QAC5F,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CACvB,IAAgB;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,IAAgE,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC","sourcesContent":["// Effects as data (docs/design-docs/0006-effects-and-drivers.md). Plain TypeScript only:\nimport type { IntentRejected } from './types.js';\n// the interpreter lives in ./internal/ (ADR 0015).\n\n/** How commands in the same lane interact. See ADR 0006 for the table. */\nexport type Concurrency = 'merge' | 'switch' | 'exhaust' | 'queue';\n\nexport interface RetryPolicy {\n /** Retries after the first failure. */\n readonly times: number;\n /** Delay before each retry (the base delay for exponential backoff). Default 0. */\n readonly delayMs?: number;\n readonly backoff?: 'fixed' | 'exponential';\n}\n\nexport interface DriverContext<O = unknown> {\n /** Aborts when the command is switched away or its component disconnects. */\n readonly signal: AbortSignal;\n /**\n * Delivers an extra result while the command runs (streaming drivers: routers, timers,\n * sockets). Each value goes through the command's `onSuccess`. Ignored once the command\n * has settled or been aborted. A streaming `run` usually never resolves; abort ends it.\n */\n readonly emit: (output: O) => void;\n}\n\n/** Performs one kind of side effect. A plain object: easy to fake in tests. */\nexport interface Driver<I, O, E = unknown> {\n readonly name: string;\n readonly run: (input: I, ctx: DriverContext<O>) => O | Promise<O>;\n /** Default policy for this driver's commands. Default `'merge'`. */\n readonly concurrency?: Concurrency;\n readonly retry?: RetryPolicy;\n /** Turns a thrown or rejected value into this driver's typed error. */\n readonly toError?: (cause: unknown) => E;\n}\n\n/** Any driver, with its input type erased. */\nexport type AnyDriver = Driver<never, unknown>;\n\n/** Drivers substituted by name (test fakes, configured instances). */\nexport type DriverOverrides = Readonly<Record<string, AnyDriver>>;\n\n/** A side effect described as data, plus pure mappers from its outcome to messages. */\nexport interface Command<M> {\n readonly driver: AnyDriver;\n readonly input: unknown;\n /** Concurrency lane. Default: the driver's name. */\n readonly key?: string;\n readonly concurrency?: Concurrency;\n readonly onSuccess: (output: unknown) => M | undefined;\n /** If omitted, failures are logged and dropped. */\n readonly onFailure?: (error: unknown) => M | undefined;\n}\n\nexport interface CommandHandlers<O, E, MS, MF = MS> {\n readonly onSuccess: (output: O) => MS | undefined;\n readonly onFailure?: (error: E) => MF | undefined;\n readonly key?: string;\n readonly concurrency?: Concurrency;\n}\n\n/**\n * What a reducer (or `init`) returns: new state, optionally with commands to run. Commands may\n * also answer with the framework message `IntentRejected` (e.g. `submitForm` in @gyral/http\n * when the server rejects a form), which goes to the optional `IntentRejected` reducer.\n */\nexport type Next<S, M> = S | readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n\n/** Identity helper so driver literals infer `I`, `O` and `E`. */\nexport function defineDriver<I, O, E = unknown>(driver: Driver<I, O, E>): Driver<I, O, E> {\n return driver;\n}\n\n/** Builds a command whose mappers are typed by the driver's input, output and error. */\n// Two message parameters so success and failure may produce different variants.\nexport function command<I, O, E, MS, MF = MS>(\n driver: Driver<I, O, E>,\n input: I,\n handlers: CommandHandlers<O, E, MS, MF>,\n): Command<MS | MF> {\n const { onFailure, key, concurrency } = handlers;\n return {\n driver: driver,\n input,\n // Sound: the interpreter only passes this driver's output and toError() result.\n onSuccess: (output) => handlers.onSuccess(output as O),\n ...(onFailure === undefined ? {} : { onFailure: (error: unknown) => onFailure(error as E) }),\n ...(key === undefined ? {} : { key }),\n ...(concurrency === undefined ? {} : { concurrency }),\n };\n}\n\n/** Splits a reducer result into state and commands. State is never an array (ADR 0006). */\nexport function splitNext<S, M>(\n next: Next<S, M>,\n): readonly [S, ReadonlyArray<Command<M | IntentRejected>>] {\n if (Array.isArray(next) && next.length === 2 && Array.isArray(next[1])) {\n return next as readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n }\n return [next as S, []];\n}\n"]}
package/dist/define.js CHANGED
@@ -3,7 +3,7 @@ import { providedDriver } from './drivers-scope.js';
3
3
  import { DEVTOOLS_ENABLED, devCommands, devConnect, devHydrated, devOwner, devUpdate, } from '#devtools';
4
4
  import { splitNext, } from './command.js';
5
5
  import { dispatchOutput, EMIT } from './children.js';
6
- import { FOCUS, runFocus } from './focus.js';
6
+ import { FOCUS, focusAfterUpdate } from './focus.js';
7
7
  import { fillEmptyTextParts, takeSeed, writeSeed } from './hydration.js';
8
8
  import { markIsland, scheduleIsland } from './islands.js';
9
9
  import { runInit } from './init.js';
@@ -263,7 +263,7 @@ export function define(tag, spec) {
263
263
  else if (cmd.driver === STORE_SEND)
264
264
  this.#binding.send(cmd.input);
265
265
  else if (cmd.driver === FOCUS)
266
- this.#focusAfterUpdate(cmd.input);
266
+ focusAfterUpdate(this, tag, cmd.input);
267
267
  else if (this.#interpreter === undefined)
268
268
  this.#pending.push(cmd);
269
269
  else
@@ -311,13 +311,6 @@ export function define(tag, spec) {
311
311
  this.#apply([this.state, commands]);
312
312
  });
313
313
  }
314
- // After the render this reducer caused (also the first render, for init's commands).
315
- #focusAfterUpdate(input) {
316
- void this.updateComplete.then(() => {
317
- if (this.isConnected)
318
- runFocus(this.renderRoot, tag, input);
319
- });
320
- }
321
314
  // el.drivers → nearest provider → spec.drivers → the command's own (gyral-czi.35).
322
315
  #resolve;
323
316
  #onEvent;
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,WAAW,EACX,QAAQ,EACR,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,SAAS,GAKV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAmB,MAAM,YAAY,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,aAAa,EACb,OAAO,EACP,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAoB,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,GAEjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAA2D,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAYtD,6FAA6F;AAC7F,MAAM,QAAQ,GAAY,QAAQ,CAAC;AAEnC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAE1C;;;;GAIG;AACH,MAAM,SAAS,GAAG,GAAY,EAAE,CAC9B,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,GAAW,EACX,IAA4B;IAE5B,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAc,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC7D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CACV,IAAI,GAAG,8DAA8D;YACnE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,4CAA4C;YAClE,yDAAyD,CAC5D,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,MAA+D,CAAC;IACrF,uEAAuE;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAErB,CAAC;IAEF,MAAM,OAAQ,SAAQ,UAAU;QAAhC;;YAaE,YAAO,GAAoB,EAAE,CAAC;YAC9B,WAAM,GAAmB,EAAE,CAAC;YAC5B,oBAAe,GAAsB,EAAE,CAAC;YAExC,kEAAkE;YAClE,aAAQ,GAAG,IAAI,YAAY,CACzB,IAAI,EACJ,GAAG,EACH,IAAI,CAAC,MAAM,IAAI,EAAE,EACjB,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EACjB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;gBACrB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;YACzC,CAAC,CACF,CAAC;YAIF,eAAU,GAAG,KAAK,CAAC;YAEnB,aAAQ,GAAkC,EAAE,CAAC;YAC7C,iFAAiF;YACjF,oBAAe,GAA2C,EAAE,CAAC;YAC7D,oEAAoE;YACpE,oBAAe,GAAG,KAAK,CAAC;YACxB,yFAAyF;YACzF,eAAU,GAAG,KAAK,CAAC;YAGnB,oFAAoF;YACpF,gBAAW,GACT,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAiNzE,mFAAmF;YACnF,aAAQ,GAAG,CAAC,MAAiB,EAAa,EAAE,CAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;gBACzB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;gBACjC,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;gBAC3B,MAAM,CAAC;YAET,aAAQ,GAAG,CAAC,KAAY,EAAQ,EAAE;gBAChC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpE,CAAC,CAAC;YAEF,aAAQ,GAAG,CAAC,GAAuB,EAAQ,EAAE;gBAC3C,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW;oBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACjE,CAAC,CAAC;QAeJ,CAAC;iBAvRiB,eAAU,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,AAAnB,CAAoB;iBAC9B,WAAM,GAAG,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,AAA7B,CAA8B;iBACpC,SAAI,GAAG,IAAI,AAAP,CAAQ;iBACZ,YAAO,GAAG,GAAG,AAAN,CAAO;QAE9B,uFAAuF;QACvF,uEAAuE;QACvE,MAAM,KAAc,kBAAkB;YACpC,MAAM,IAAI,GAAG,KAAK,CAAC,kBAAkB,CAAC;YACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC;QAMD,kEAAkE;QAClE,QAAQ,CAQN;QACF,MAAM,CAA2B;QACjC,sFAAsF;QACtF,UAAU,CAAgB;QAC1B,UAAU,CAAS;QACnB,YAAY,CAA8C;QAC1D,QAAQ,CAAqC;QAC7C,iFAAiF;QACjF,eAAe,CAA8C;QAC7D,oEAAoE;QACpE,eAAe,CAAS;QACxB,yFAAyF;QACzF,UAAU,CAAS;QACnB,oEAAoE;QACpE,WAAW,CAA4B;QACvC,oFAAoF;QACpF,WAAW,CAC8D;QAEzE,IAAI,KAAK;YACP,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBACjD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,kCAAkC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC/E,CAAC;gBACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,eAAe;oBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACrE,CAAC;YACD,OAAQ,IAAI,CAAC,MAAuB,CAAC,KAAK,CAAC;QAC7C,CAAC;QAED,IAAI,CAAC,GAAM;YACT,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;QAED,SAAS,CAAC,GAAW,EAAE,MAAM,GAAG,IAAI;YAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,wFAAwF;gBACxF,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;oBAC/D,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,gCAAgC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;gBACpE,CAAC;gBACD,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7C,IAAI,gBAAgB;gBAAE,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAClE,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC1D,6EAA6E;gBAC7E,qEAAqE;gBACrE,IAAI,CAAC,WAAW,GAAG,kBAAkB,CAAC,KAAK,IAAI,EAAE;oBAC/C,IAAI,CAAC,aAAa,EAAE,CAAC;oBACrB,MAAM,KAAK,CAAC,iBAAiB,EAAE,CAAC;gBAClC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;oBAC1B,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,iCAAiC,EAAE,KAAK,CAAC,CAAC;gBACjE,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QAEkB,KAAK,CAAC,iBAAiB;YACxC,MAAM,IAAI,CAAC,WAAW,CAAC;YACvB,OAAO,KAAK,CAAC,iBAAiB,EAAE,CAAC;QACnC,CAAC;QAEQ,iBAAiB;YACxB,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,sFAAsF;YACtF,oFAAoF;YACpF,6EAA6E;YAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;YACnD,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE;gBAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC;YACvD,IAAI,OAAO,EAAE,CAAC;gBACZ,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,gDAAgD;gBACtE,OAAO;YACT,CAAC;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC;QAEQ,wBAAwB,CAAC,IAAY,EAAE,GAAkB,EAAE,KAAoB;YACtF,KAAK,CAAC,wBAAwB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACjD,IAAI,IAAI,KAAK,eAAe,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO;YAC5E,IAAI,CAAC,SAAS,EAAE;gBAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAAC,wCAAwC;YACrF,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC;QAED,QAAQ;YACN,mFAAmF;YACnF,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;gBAAE,IAAI,CAAC,aAAa,EAAE,CAAC;YAClD,IAAI,CAAC,YAAY,GAAG,eAAe,CACjC,IAAI,CAAC,QAAQ,EACb,CAAC,GAAG,EAAE,EAAE;gBACN,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC,EACD,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CACtE,CAAC;YACF,IAAI,gBAAgB;gBAAE,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC9B,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;YACnB,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtD,IAAI,IAAI,CAAC,UAAU;gBAAE,OAAO;YAC5B,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QAEQ,oBAAoB;YAC3B,KAAK,CAAC,oBAAoB,EAAE,CAAC;YAC7B,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAC9B,IAAI,gBAAgB;gBAAE,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC;QAEkB,UAAU,CAAC,OAAkC;YAC9D,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC1B,IAAI,QAAQ,EAAE,CAAC;gBACb,yEAAyE;gBACzE,+EAA+E;gBAC/E,MAAM,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAC1D,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC1F,IAAI,OAAO,CAAC,IAAI,CAAC;oBAAE,aAAa,CAAC,IAAI,CAAC,CAAC;gBACvC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;YAC7B,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,CAAC,sCAAsC;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,IAAI,SAAS,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC;gBAAE,OAAO;YAC9C,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;YACxB,uEAAuE;YACvE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAY,EAAE,KAAK,CAAC,CAAC;QACzE,CAAC;QAED,2FAA2F;QAC3F,yFAAyF;QACzF,0FAA0F;QAC1F,qFAAqF;QAClE,gBAAgB;YACjC,IAAI,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC/B,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBAC1C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACvB,OAAO,IAAI,CAAC,UAAU,CAAC;YACzB,CAAC;YACD,OAAO,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAClC,CAAC;QAEkB,MAAM,CAAC,OAAkC;YAC1D,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAI,IAAI,CAAC,WAAiC,CAAC,aAAa,CAAC;gBACrE,aAAa,CAAC,IAAI,CAAC,UAAwB,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC1F,CAAC;YACD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC9D,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW;YACpE,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QAEkB,MAAM;YACvB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,WAA6B,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QAEkB,OAAO,CAAC,OAAkC;YAC3D,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACvB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,CAAC,IAAgB;YACrB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC/B,IAAI,QAAQ;gBAAE,OAAO,CAAC,kEAAkE;YACxF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI;oBAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;qBACpD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU;oBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAuB,CAAC,CAAC;qBAC/E,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK;oBAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAmB,CAAC,CAAC;qBAC1E,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS;oBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;oBAC7D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED;;;;WAIG;QACH,eAAe;YACb,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO;YACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO;YAC/B,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACtE,0FAA0F;YAC1F,gFAAgF;YAChF,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,KAAW,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YACvE,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;YAChC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QAEkB,YAAY,CAAC,OAAkC;YAChE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,IAAI,CAAC,eAAe;gBAAE,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe;YAC9E,IAAI,gBAAgB;gBAAE,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC;YACtC,MAAM,aAAa,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC;YACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,aAAa;gBAAE,OAAO;YACpD,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;YAC1B,qFAAqF;YACrF,4EAA4E;YAC5E,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE;gBACjC,IAAI,aAAa,EAAE,CAAC;oBAClB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,CAAC,eAAe,EAAY,CAAC,CAAC;gBACvF,CAAC;gBACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;oBAAE,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;QACL,CAAC;QAED,qFAAqF;QACrF,iBAAiB,CAAC,KAAiB;YACjC,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE;gBACjC,IAAI,IAAI,CAAC,WAAW;oBAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC;QAED,mFAAmF;QACnF,QAAQ,CAIC;QAET,QAAQ,CAEN;QAEF,QAAQ,CAEN;QAEF,IAAI;YACF,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9E,CAAC;QAED,aAAa,CAAC,KAAe,EAAE,KAAc,EAAE,IAAa;YAC1D,IAAI,QAAQ,CAAC,cAAc,CAAC,KAAK,SAAS;gBAAE,IAAI,CAAC,aAAa,EAAE,CAAC;;gBAC5D,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAY,CAAC,CAAC;QAC1F,CAAC;QAED,MAAM;YACJ,6DAA6D;YAC7D,OAAO,SAAS,CAAC,IAAI,EAAE,SAAS,CAAM,CAAC;QACzC,CAAC;;IAGH,aAAa,CAAC,OAAO,CAAC,CAAC;IACvB,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;QAAE,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/E,uEAAuE;IACvE,OAAO,OAAmD,CAAC;AAC7D,CAAC","sourcesContent":["import { isServer, LitElement } from 'lit';\nimport { providedDriver } from './drivers-scope.js';\nimport {\n DEVTOOLS_ENABLED,\n devCommands,\n devConnect,\n devHydrated,\n devOwner,\n devUpdate,\n} from '#devtools';\nimport {\n splitNext,\n type AnyDriver,\n type Command,\n type DriverOverrides,\n type Next,\n} from './command.js';\nimport { dispatchOutput, EMIT } from './children.js';\nimport { FOCUS, runFocus, type FocusInput } from './focus.js';\nimport type { GyralElement, GyralElementClass } from './element-types.js';\nimport { fillEmptyTextParts, takeSeed, writeSeed } from './hydration.js';\nimport { markIsland, scheduleIsland } from './islands.js';\nimport { runInit } from './init.js';\nimport { handleIntent, intentNames, listenForIntents, markGyralHost } from './intent.js';\nimport {\n componentStyles,\n hydrateLight,\n hydrateShadow,\n isLight,\n markLightHost,\n} from './light-dom.js';\nimport { makeInterpreter, type Interpreter } from './internal/interpreter.js';\nimport {\n missingRequired,\n readProps,\n readRawProps,\n restoreProps,\n sameProps,\n shadowedBuiltins,\n type PropTable,\n} from './props.js';\nimport { attachStates, type StateSync } from './states.js';\nimport { STORE_SEND, type AnyStore, type StoreOverrides, type StoreSendInput } from './store.js';\nimport { StoreBinding } from './store-binding.js';\nimport { withViewTransition } from './transitions.js';\nimport type {\n ComponentSpec,\n Ctx,\n IntentNames,\n IntentParser,\n IntentRejected,\n Tagged,\n} from './types.js';\n\nexport type { GyralElement, GyralElementClass } from './element-types.js';\n\n// Lit types `isServer` per build condition; widen it so both branches type-check (ADR 0012).\nconst onServer: boolean = isServer;\n\nconst DEFER_HYDRATION = 'defer-hydration';\n\n/**\n * Has Lit's hydrate support patched LitElement? It then defers `defer-hydration` elements\n * itself. A bundler can evaluate Lit before `@gyral/ssr/hydrate`, so it may not have\n * (gyral-czi.41); the patch adds an own static `observedAttributes` to LitElement.\n */\nconst litDefers = (): boolean =>\n Object.prototype.hasOwnProperty.call(LitElement, 'observedAttributes');\n\n/**\n * Compiles a Model-View-Intent spec into a custom element and registers it under `tag`.\n * See docs/design-docs/0001-mvi-parsed-intent.md, 0006-effects-and-drivers.md, 0007-props.md\n * 0008-forms.md and 0012-ssr.md.\n */\nexport function define<S, M extends Tagged, P extends object = object, O extends Tagged = never>(\n tag: string,\n spec: ComponentSpec<S, M, P>,\n): GyralElementClass<S, M, P, O> {\n const propTable = (spec.props ?? {}) as PropTable;\n const propNames = Object.keys(propTable);\n const shadowed = onServer ? [] : shadowedBuiltins(propNames);\n if (shadowed.length > 0) {\n console.warn(\n `<${tag}> declares prop(s) that shadow built-in element properties: ` +\n `${shadowed.join(', ')}. Setting them changes platform behaviour ` +\n `(a prop named \"hidden\" hides the element). Rename them.`,\n );\n }\n const parsers = spec.intent as Readonly<Record<string, IntentParser<M> | undefined>>;\n // Sound: #dispatch() only calls the reducer whose key equals msg._tag.\n const reducers = spec.update as unknown as Readonly<\n Record<string, ((state: S, msg: Tagged, ctx: Ctx<P>) => Next<S, M>) | undefined>\n >;\n\n class Element extends LitElement implements GyralElement<S, M> {\n static override properties = spec.props ?? {};\n static override styles = componentStyles(spec, tag);\n static readonly spec = spec;\n static readonly tagName = tag;\n\n // Always observe `defer-hydration`, so Gyral can defer server-rendered children itself\n // when Lit's hydrate support isn't installed (gyral-czi.41, ADR 0012).\n static override get observedAttributes(): string[] {\n const base = super.observedAttributes;\n return base.includes(DEFER_HYDRATION) ? base : [...base, DEFER_HYDRATION];\n }\n\n drivers: DriverOverrides = {};\n stores: StoreOverrides = {};\n initialMessages: readonly Tagged[] = [];\n\n /** Reads, subscriptions and writes for spec.stores (ADR 0013). */\n #binding = new StoreBinding(\n this,\n tag,\n spec.stores ?? [],\n () => this.stores,\n (store, state, prev) => {\n this.#storeChanged(store, state, prev);\n },\n );\n #model: { value: S } | undefined;\n /** Props as of the last init or PropsChanged; the `prev` of the next PropsChanged. */\n #seenProps: P | undefined;\n #listening = false;\n #interpreter: Interpreter<M | IntentRejected> | undefined;\n #pending: Command<M | IntentRejected>[] = [];\n /** init's commands on the hydration path; started in firstUpdated (ADR 0012). */\n #afterHydration: readonly Command<M | IntentRejected>[] = [];\n /** Resumed from server markup; reported by the Hydrated message. */\n #serverRendered = false;\n /** The renderRoot is a server-rendered (declarative) shadow root to hydrate in place. */\n #ssrShadow = false;\n /** The latest view-transition update, awaited by updateComplete. */\n #transition: Promise<void> | undefined;\n /** Mirrors spec.states onto CSS custom states; only attached when the spec asks. */\n #syncStates: StateSync | undefined =\n spec.states === undefined || onServer ? undefined : attachStates(this);\n\n get state(): S {\n if (this.#model === undefined) {\n const missing = missingRequired(this, propTable);\n if (missing.length > 0) {\n console.warn(`<${tag}> is missing required prop(s): ${missing.join(', ')}.`);\n }\n this.#seenProps = this.#props();\n this.#apply(runInit(spec, this.#seenProps));\n for (const msg of this.initialMessages) this.#dispatch(msg, false);\n }\n return (this.#model as { value: S }).value;\n }\n\n send(msg: M): void {\n this.#dispatch(msg);\n }\n\n #dispatch(msg: Tagged, render = true): void {\n const reducer = reducers[msg._tag];\n if (reducer === undefined) {\n // Framework messages have optional reducers; props and stores stay readable as context.\n if (msg._tag !== 'PropsChanged' && msg._tag !== 'StoreChanged') {\n console.warn(`<${tag}> has no update for message \"${msg._tag}\".`);\n }\n return;\n }\n const prev = this.state;\n this.#apply(reducer(prev, msg, this.#ctx()));\n if (DEVTOOLS_ENABLED) devUpdate(this, tag, msg, prev, this.state);\n if (!render) return;\n if (spec.viewTransition?.(prev, this.state, msg) === true) {\n // The DOM change happens inside the transition callback (ADR 0001 addendum);\n // updateComplete waits for it, so tests and callers see the new DOM.\n this.#transition = withViewTransition(async () => {\n this.requestUpdate();\n await super.getUpdateComplete();\n }).catch((error: unknown) => {\n console.error(`<${tag}> view transition update failed`, error);\n });\n } else {\n this.requestUpdate();\n }\n }\n\n protected override async getUpdateComplete(): Promise<boolean> {\n await this.#transition;\n return super.getUpdateComplete();\n }\n\n override connectedCallback(): void {\n this.#resumeFromSeed();\n // A component server-rendered inside another's view waits (`defer-hydration`) for its\n // parent to hydrate first. Lit's hydrate support, when installed, defers LitElement\n // itself; otherwise Gyral holds back Lit's connect until the attribute goes.\n const waiting = this.hasAttribute(DEFER_HYDRATION);\n if (!waiting || litDefers()) super.connectedCallback();\n if (waiting) {\n scheduleIsland(this); // lazy islands release themselves (gyral-4k7.4)\n return;\n }\n this.#connect();\n }\n\n override attributeChangedCallback(name: string, old: string | null, value: string | null) {\n super.attributeChangedCallback(name, old, value);\n if (name !== DEFER_HYDRATION || value !== null || !this.isConnected) return;\n if (!litDefers()) super.connectedCallback(); // Lit's support would have connected it\n this.#connect();\n }\n\n #connect(): void {\n // Re-resolve on every connect: the nearest <gyral-stores> may differ after a move.\n if (this.#binding.connect()) this.requestUpdate();\n this.#interpreter = makeInterpreter<M | IntentRejected>(\n this.#resolve,\n (msg) => {\n this.#dispatch(msg);\n },\n DEVTOOLS_ENABLED ? devCommands(() => devOwner(this, tag)) : undefined,\n );\n if (DEVTOOLS_ENABLED) devConnect(this, tag, true);\n const pending = this.#pending;\n this.#pending = [];\n for (const cmd of pending) this.#interpreter.run(cmd);\n if (this.#listening) return;\n listenForIntents(this.renderRoot, spec.events ?? [], this.#onEvent);\n this.#listening = true;\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#binding.disconnect();\n this.#interpreter?.dispose();\n this.#interpreter = undefined;\n if (DEVTOOLS_ENABLED) devConnect(this, tag, false);\n }\n\n protected override willUpdate(changed: Map<PropertyKey, unknown>): void {\n super.willUpdate(changed);\n if (onServer) {\n // Server renders run constructor, willUpdate and render only (ADR 0012).\n // init is pure, so the client can recompute an unchanged state from the props.\n const [initial] = splitNext(runInit(spec, this.#props()));\n writeSeed(this, this.state, readRawProps(this, propTable), propTable, { value: initial });\n if (isLight(spec)) markLightHost(this);\n markIsland(this, spec.hydrate);\n return;\n }\n const prev = this.#seenProps;\n if (prev === undefined) return; // first render: init() sees the props\n const props = this.#props();\n if (sameProps(propNames, props, prev)) return;\n this.#seenProps = props;\n // Inside the update cycle, so the new state renders in this same pass.\n this.#dispatch({ _tag: 'PropsChanged', props, prev } as Tagged, false);\n }\n\n // Light-DOM mode (ADR 0014): render into the element itself. A server-rendered shadow root\n // (Declarative Shadow DOM) is reused as is and hydrated in update() (gyral-czi.41): this\n // bypasses Lit's hydrate-support patch on purpose, so hydration is correct whether or not\n // that patch was installed before Lit evaluated (bundlers can reorder it; ADR 0012).\n protected override createRenderRoot(): HTMLElement | DocumentFragment {\n if (isLight(spec)) return this;\n if (!onServer && this.shadowRoot !== null) {\n this.#ssrShadow = true;\n return this.shadowRoot;\n }\n return super.createRenderRoot();\n }\n\n protected override update(changed: Map<PropertyKey, unknown>): void {\n if (this.#ssrShadow && !this.hasUpdated) {\n const styles = (this.constructor as typeof LitElement).elementStyles;\n hydrateShadow(this.renderRoot as ShadowRoot, this.render(), this.renderOptions, styles);\n }\n if (isLight(spec) && !this.hasUpdated && this.#serverRendered) {\n hydrateLight(this, this.render(), this.renderOptions); // ADR 0014\n }\n super.update(changed);\n }\n\n protected override render(): unknown {\n return spec.view(this.state, intentNames as IntentNames<M>, this.#ctx());\n }\n\n protected override updated(changed: Map<PropertyKey, unknown>): void {\n super.updated(changed);\n if (spec.states !== undefined) this.#syncStates?.(spec.states(this.state));\n }\n\n #apply(next: Next<S, M>): void {\n const [state, commands] = splitNext(next);\n this.#model = { value: state };\n if (onServer) return; // commands never run on the server; the client's init starts them\n for (const cmd of commands) {\n if (cmd.driver === EMIT) dispatchOutput(this, cmd.input);\n else if (cmd.driver === STORE_SEND) this.#binding.send(cmd.input as StoreSendInput);\n else if (cmd.driver === FOCUS) this.#focusAfterUpdate(cmd.input as FocusInput);\n else if (this.#interpreter === undefined) this.#pending.push(cmd);\n else this.#interpreter.run(cmd);\n }\n }\n\n /**\n * Hydration (ADR 0012): use the server's state and restore props that only travelled in the\n * seed. init(props) runs again for its commands only, and they start after the hydrating\n * render: a driver that answers synchronously must not change state before hydration.\n */\n #resumeFromSeed(): void {\n if (this.#model !== undefined) return;\n const seed = takeSeed(this);\n if (seed === undefined) return;\n restoreProps(this, propNames, seed.props);\n this.#seenProps = this.#props();\n const [initial, commands] = splitNext(runInit(spec, this.#seenProps));\n // Sound: the seed is this component's own state, serialized by writeSeed() on the server;\n // when absent, the server's state was exactly init(props) (seed deduplication).\n this.#model = { value: 'state' in seed ? (seed.state as S) : initial };\n this.#afterHydration = commands;\n this.#serverRendered = true;\n }\n\n protected override firstUpdated(changed: Map<PropertyKey, unknown>): void {\n super.firstUpdated(changed);\n if (this.#serverRendered) fillEmptyTextParts(this.renderRoot); // gyral-4k7.12\n if (DEVTOOLS_ENABLED) devHydrated(this, tag, this.#serverRendered);\n const commands = this.#afterHydration;\n const wantsHydrated = reducers['Hydrated'] !== undefined;\n if (commands.length === 0 && !wantsHydrated) return;\n this.#afterHydration = [];\n // After the first (possibly hydrating) update has fully completed, so whatever these\n // change starts a fresh update instead of diverging from the server markup.\n void this.updateComplete.then(() => {\n if (wantsHydrated) {\n this.#dispatch({ _tag: 'Hydrated', serverRendered: this.#serverRendered } as Tagged);\n }\n if (commands.length > 0) this.#apply([this.state, commands]);\n });\n }\n\n // After the render this reducer caused (also the first render, for init's commands).\n #focusAfterUpdate(input: FocusInput): void {\n void this.updateComplete.then(() => {\n if (this.isConnected) runFocus(this.renderRoot, tag, input);\n });\n }\n\n // el.drivers → nearest provider → spec.drivers → the command's own (gyral-czi.35).\n #resolve = (driver: AnyDriver): AnyDriver =>\n this.drivers[driver.name] ??\n providedDriver(this, driver.name) ??\n spec.drivers?.[driver.name] ??\n driver;\n\n #onEvent = (event: Event): void => {\n handleIntent(event, this.renderRoot, parsers, tag, this.#deliver);\n };\n\n #deliver = (msg: Tagged | undefined): void => {\n if (msg !== undefined && this.isConnected) this.#dispatch(msg);\n };\n\n #ctx(): Ctx<P> {\n return { props: this.#props(), read: (store) => this.#binding.read(store) };\n }\n\n #storeChanged(store: AnyStore, state: unknown, prev: unknown): void {\n if (reducers['StoreChanged'] === undefined) this.requestUpdate();\n else this.#dispatch({ _tag: 'StoreChanged', store: store.name, state, prev } as Tagged);\n }\n\n #props(): P {\n // Sound: propNames are exactly the declared props, P's keys.\n return readProps(this, propTable) as P;\n }\n }\n\n markGyralHost(Element);\n if (customElements.get(tag) === undefined) customElements.define(tag, Element);\n // Sound: the declared props are reactive properties on every instance.\n return Element as unknown as GyralElementClass<S, M, P, O>;\n}\n"]}
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"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","sourcesContent":["import { isServer, LitElement } from 'lit';\nimport { providedDriver } from './drivers-scope.js';\nimport {\n DEVTOOLS_ENABLED,\n devCommands,\n devConnect,\n devHydrated,\n devOwner,\n devUpdate,\n} from '#devtools';\nimport {\n splitNext,\n type AnyDriver,\n type Command,\n type DriverOverrides,\n type Next,\n} from './command.js';\nimport { dispatchOutput, EMIT } from './children.js';\nimport { FOCUS, focusAfterUpdate, type FocusInput } from './focus.js';\nimport type { GyralElement, GyralElementClass } from './element-types.js';\nimport { fillEmptyTextParts, takeSeed, writeSeed } from './hydration.js';\nimport { markIsland, scheduleIsland } from './islands.js';\nimport { runInit } from './init.js';\nimport { handleIntent, intentNames, listenForIntents, markGyralHost } from './intent.js';\nimport {\n componentStyles,\n hydrateLight,\n hydrateShadow,\n isLight,\n markLightHost,\n} from './light-dom.js';\nimport { makeInterpreter, type Interpreter } from './internal/interpreter.js';\nimport {\n missingRequired,\n readProps,\n readRawProps,\n restoreProps,\n sameProps,\n shadowedBuiltins,\n type PropTable,\n} from './props.js';\nimport { attachStates, type StateSync } from './states.js';\nimport { STORE_SEND, type AnyStore, type StoreOverrides, type StoreSendInput } from './store.js';\nimport { StoreBinding } from './store-binding.js';\nimport { withViewTransition } from './transitions.js';\nimport type {\n ComponentSpec,\n Ctx,\n IntentNames,\n IntentParser,\n IntentRejected,\n Tagged,\n} from './types.js';\n\nexport type { GyralElement, GyralElementClass } from './element-types.js';\n\n// Lit types `isServer` per build condition; widen it so both branches type-check (ADR 0012).\nconst onServer: boolean = isServer;\n\nconst DEFER_HYDRATION = 'defer-hydration';\n\n/**\n * Has Lit's hydrate support patched LitElement? It then defers `defer-hydration` elements\n * itself. A bundler can evaluate Lit before `@gyral/ssr/hydrate`, so it may not have\n * (gyral-czi.41); the patch adds an own static `observedAttributes` to LitElement.\n */\nconst litDefers = (): boolean =>\n Object.prototype.hasOwnProperty.call(LitElement, 'observedAttributes');\n\n/**\n * Compiles a Model-View-Intent spec into a custom element and registers it under `tag`.\n * See docs/design-docs/0001-mvi-parsed-intent.md, 0006-effects-and-drivers.md, 0007-props.md\n * 0008-forms.md and 0012-ssr.md.\n */\nexport function define<S, M extends Tagged, P extends object = object, O extends Tagged = never>(\n tag: string,\n spec: ComponentSpec<S, M, P>,\n): GyralElementClass<S, M, P, O> {\n const propTable = (spec.props ?? {}) as PropTable;\n const propNames = Object.keys(propTable);\n const shadowed = onServer ? [] : shadowedBuiltins(propNames);\n if (shadowed.length > 0) {\n console.warn(\n `<${tag}> declares prop(s) that shadow built-in element properties: ` +\n `${shadowed.join(', ')}. Setting them changes platform behaviour ` +\n `(a prop named \"hidden\" hides the element). Rename them.`,\n );\n }\n const parsers = spec.intent as Readonly<Record<string, IntentParser<M> | undefined>>;\n // Sound: #dispatch() only calls the reducer whose key equals msg._tag.\n const reducers = spec.update as unknown as Readonly<\n Record<string, ((state: S, msg: Tagged, ctx: Ctx<P>) => Next<S, M>) | undefined>\n >;\n\n class Element extends LitElement implements GyralElement<S, M> {\n static override properties = spec.props ?? {};\n static override styles = componentStyles(spec, tag);\n static readonly spec = spec;\n static readonly tagName = tag;\n\n // Always observe `defer-hydration`, so Gyral can defer server-rendered children itself\n // when Lit's hydrate support isn't installed (gyral-czi.41, ADR 0012).\n static override get observedAttributes(): string[] {\n const base = super.observedAttributes;\n return base.includes(DEFER_HYDRATION) ? base : [...base, DEFER_HYDRATION];\n }\n\n drivers: DriverOverrides = {};\n stores: StoreOverrides = {};\n initialMessages: readonly Tagged[] = [];\n\n /** Reads, subscriptions and writes for spec.stores (ADR 0013). */\n #binding = new StoreBinding(\n this,\n tag,\n spec.stores ?? [],\n () => this.stores,\n (store, state, prev) => {\n this.#storeChanged(store, state, prev);\n },\n );\n #model: { value: S } | undefined;\n /** Props as of the last init or PropsChanged; the `prev` of the next PropsChanged. */\n #seenProps: P | undefined;\n #listening = false;\n #interpreter: Interpreter<M | IntentRejected> | undefined;\n #pending: Command<M | IntentRejected>[] = [];\n /** init's commands on the hydration path; started in firstUpdated (ADR 0012). */\n #afterHydration: readonly Command<M | IntentRejected>[] = [];\n /** Resumed from server markup; reported by the Hydrated message. */\n #serverRendered = false;\n /** The renderRoot is a server-rendered (declarative) shadow root to hydrate in place. */\n #ssrShadow = false;\n /** The latest view-transition update, awaited by updateComplete. */\n #transition: Promise<void> | undefined;\n /** Mirrors spec.states onto CSS custom states; only attached when the spec asks. */\n #syncStates: StateSync | undefined =\n spec.states === undefined || onServer ? undefined : attachStates(this);\n\n get state(): S {\n if (this.#model === undefined) {\n const missing = missingRequired(this, propTable);\n if (missing.length > 0) {\n console.warn(`<${tag}> is missing required prop(s): ${missing.join(', ')}.`);\n }\n this.#seenProps = this.#props();\n this.#apply(runInit(spec, this.#seenProps));\n for (const msg of this.initialMessages) this.#dispatch(msg, false);\n }\n return (this.#model as { value: S }).value;\n }\n\n send(msg: M): void {\n this.#dispatch(msg);\n }\n\n #dispatch(msg: Tagged, render = true): void {\n const reducer = reducers[msg._tag];\n if (reducer === undefined) {\n // Framework messages have optional reducers; props and stores stay readable as context.\n if (msg._tag !== 'PropsChanged' && msg._tag !== 'StoreChanged') {\n console.warn(`<${tag}> has no update for message \"${msg._tag}\".`);\n }\n return;\n }\n const prev = this.state;\n this.#apply(reducer(prev, msg, this.#ctx()));\n if (DEVTOOLS_ENABLED) devUpdate(this, tag, msg, prev, this.state);\n if (!render) return;\n if (spec.viewTransition?.(prev, this.state, msg) === true) {\n // The DOM change happens inside the transition callback (ADR 0001 addendum);\n // updateComplete waits for it, so tests and callers see the new DOM.\n this.#transition = withViewTransition(async () => {\n this.requestUpdate();\n await super.getUpdateComplete();\n }).catch((error: unknown) => {\n console.error(`<${tag}> view transition update failed`, error);\n });\n } else {\n this.requestUpdate();\n }\n }\n\n protected override async getUpdateComplete(): Promise<boolean> {\n await this.#transition;\n return super.getUpdateComplete();\n }\n\n override connectedCallback(): void {\n this.#resumeFromSeed();\n // A component server-rendered inside another's view waits (`defer-hydration`) for its\n // parent to hydrate first. Lit's hydrate support, when installed, defers LitElement\n // itself; otherwise Gyral holds back Lit's connect until the attribute goes.\n const waiting = this.hasAttribute(DEFER_HYDRATION);\n if (!waiting || litDefers()) super.connectedCallback();\n if (waiting) {\n scheduleIsland(this); // lazy islands release themselves (gyral-4k7.4)\n return;\n }\n this.#connect();\n }\n\n override attributeChangedCallback(name: string, old: string | null, value: string | null) {\n super.attributeChangedCallback(name, old, value);\n if (name !== DEFER_HYDRATION || value !== null || !this.isConnected) return;\n if (!litDefers()) super.connectedCallback(); // Lit's support would have connected it\n this.#connect();\n }\n\n #connect(): void {\n // Re-resolve on every connect: the nearest <gyral-stores> may differ after a move.\n if (this.#binding.connect()) this.requestUpdate();\n this.#interpreter = makeInterpreter<M | IntentRejected>(\n this.#resolve,\n (msg) => {\n this.#dispatch(msg);\n },\n DEVTOOLS_ENABLED ? devCommands(() => devOwner(this, tag)) : undefined,\n );\n if (DEVTOOLS_ENABLED) devConnect(this, tag, true);\n const pending = this.#pending;\n this.#pending = [];\n for (const cmd of pending) this.#interpreter.run(cmd);\n if (this.#listening) return;\n listenForIntents(this.renderRoot, spec.events ?? [], this.#onEvent);\n this.#listening = true;\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#binding.disconnect();\n this.#interpreter?.dispose();\n this.#interpreter = undefined;\n if (DEVTOOLS_ENABLED) devConnect(this, tag, false);\n }\n\n protected override willUpdate(changed: Map<PropertyKey, unknown>): void {\n super.willUpdate(changed);\n if (onServer) {\n // Server renders run constructor, willUpdate and render only (ADR 0012).\n // init is pure, so the client can recompute an unchanged state from the props.\n const [initial] = splitNext(runInit(spec, this.#props()));\n writeSeed(this, this.state, readRawProps(this, propTable), propTable, { value: initial });\n if (isLight(spec)) markLightHost(this);\n markIsland(this, spec.hydrate);\n return;\n }\n const prev = this.#seenProps;\n if (prev === undefined) return; // first render: init() sees the props\n const props = this.#props();\n if (sameProps(propNames, props, prev)) return;\n this.#seenProps = props;\n // Inside the update cycle, so the new state renders in this same pass.\n this.#dispatch({ _tag: 'PropsChanged', props, prev } as Tagged, false);\n }\n\n // Light-DOM mode (ADR 0014): render into the element itself. A server-rendered shadow root\n // (Declarative Shadow DOM) is reused as is and hydrated in update() (gyral-czi.41): this\n // bypasses Lit's hydrate-support patch on purpose, so hydration is correct whether or not\n // that patch was installed before Lit evaluated (bundlers can reorder it; ADR 0012).\n protected override createRenderRoot(): HTMLElement | DocumentFragment {\n if (isLight(spec)) return this;\n if (!onServer && this.shadowRoot !== null) {\n this.#ssrShadow = true;\n return this.shadowRoot;\n }\n return super.createRenderRoot();\n }\n\n protected override update(changed: Map<PropertyKey, unknown>): void {\n if (this.#ssrShadow && !this.hasUpdated) {\n const styles = (this.constructor as typeof LitElement).elementStyles;\n hydrateShadow(this.renderRoot as ShadowRoot, this.render(), this.renderOptions, styles);\n }\n if (isLight(spec) && !this.hasUpdated && this.#serverRendered) {\n hydrateLight(this, this.render(), this.renderOptions); // ADR 0014\n }\n super.update(changed);\n }\n\n protected override render(): unknown {\n return spec.view(this.state, intentNames as IntentNames<M>, this.#ctx());\n }\n\n protected override updated(changed: Map<PropertyKey, unknown>): void {\n super.updated(changed);\n if (spec.states !== undefined) this.#syncStates?.(spec.states(this.state));\n }\n\n #apply(next: Next<S, M>): void {\n const [state, commands] = splitNext(next);\n this.#model = { value: state };\n if (onServer) return; // commands never run on the server; the client's init starts them\n for (const cmd of commands) {\n if (cmd.driver === EMIT) dispatchOutput(this, cmd.input);\n else if (cmd.driver === STORE_SEND) this.#binding.send(cmd.input as StoreSendInput);\n else if (cmd.driver === FOCUS) focusAfterUpdate(this, tag, cmd.input as FocusInput);\n else if (this.#interpreter === undefined) this.#pending.push(cmd);\n else this.#interpreter.run(cmd);\n }\n }\n\n /**\n * Hydration (ADR 0012): use the server's state and restore props that only travelled in the\n * seed. init(props) runs again for its commands only, and they start after the hydrating\n * render: a driver that answers synchronously must not change state before hydration.\n */\n #resumeFromSeed(): void {\n if (this.#model !== undefined) return;\n const seed = takeSeed(this);\n if (seed === undefined) return;\n restoreProps(this, propNames, seed.props);\n this.#seenProps = this.#props();\n const [initial, commands] = splitNext(runInit(spec, this.#seenProps));\n // Sound: the seed is this component's own state, serialized by writeSeed() on the server;\n // when absent, the server's state was exactly init(props) (seed deduplication).\n this.#model = { value: 'state' in seed ? (seed.state as S) : initial };\n this.#afterHydration = commands;\n this.#serverRendered = true;\n }\n\n protected override firstUpdated(changed: Map<PropertyKey, unknown>): void {\n super.firstUpdated(changed);\n if (this.#serverRendered) fillEmptyTextParts(this.renderRoot); // gyral-4k7.12\n if (DEVTOOLS_ENABLED) devHydrated(this, tag, this.#serverRendered);\n const commands = this.#afterHydration;\n const wantsHydrated = reducers['Hydrated'] !== undefined;\n if (commands.length === 0 && !wantsHydrated) return;\n this.#afterHydration = [];\n // After the first (possibly hydrating) update has fully completed, so whatever these\n // change starts a fresh update instead of diverging from the server markup.\n void this.updateComplete.then(() => {\n if (wantsHydrated) {\n this.#dispatch({ _tag: 'Hydrated', serverRendered: this.#serverRendered } as Tagged);\n }\n if (commands.length > 0) this.#apply([this.state, commands]);\n });\n }\n\n // el.drivers → nearest provider → spec.drivers → the command's own (gyral-czi.35).\n #resolve = (driver: AnyDriver): AnyDriver =>\n this.drivers[driver.name] ??\n providedDriver(this, driver.name) ??\n spec.drivers?.[driver.name] ??\n driver;\n\n #onEvent = (event: Event): void => {\n handleIntent(event, this.renderRoot, parsers, tag, this.#deliver);\n };\n\n #deliver = (msg: Tagged | undefined): void => {\n if (msg !== undefined && this.isConnected) this.#dispatch(msg);\n };\n\n #ctx(): Ctx<P> {\n return { props: this.#props(), read: (store) => this.#binding.read(store) };\n }\n\n #storeChanged(store: AnyStore, state: unknown, prev: unknown): void {\n if (reducers['StoreChanged'] === undefined) this.requestUpdate();\n else this.#dispatch({ _tag: 'StoreChanged', store: store.name, state, prev } as Tagged);\n }\n\n #props(): P {\n // Sound: propNames are exactly the declared props, P's keys.\n return readProps(this, propTable) as P;\n }\n }\n\n markGyralHost(Element);\n if (customElements.get(tag) === undefined) customElements.define(tag, Element);\n // Sound: the declared props are reactive properties on every instance.\n return Element as unknown as GyralElementClass<S, M, P, O>;\n}\n"]}
package/dist/devtools.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // with the `development` condition; production builds get devtools-off.ts instead, so the
3
3
  // guarded call sites (`if (DEVTOOLS_ENABLED) …`) disappear from the bundle.
4
4
  import { DEVTOOLS_GLOBAL, } from './devtools-events.js';
5
+ import { warnLeakingLitHtmlOnce } from './lit-html-check.js';
5
6
  export const DEVTOOLS_ENABLED = true;
6
7
  const hook = () => globalThis[DEVTOOLS_GLOBAL];
7
8
  const ids = new WeakMap();
@@ -34,6 +35,7 @@ export function devLiveComponents() {
34
35
  return [...live].map(([element, tag]) => ref(element, tag));
35
36
  }
36
37
  export function devConnect(element, tag, connected) {
38
+ warnLeakingLitHtmlOnce();
37
39
  if (connected)
38
40
  live.set(element, tag);
39
41
  else
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"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","sourcesContent":["// Development build of the devtools hook (ADR 0017). Resolved through the `#devtools` import\n// with the `development` condition; production builds get devtools-off.ts instead, so the\n// guarded call sites (`if (DEVTOOLS_ENABLED) …`) disappear from the bundle.\nimport {\n DEVTOOLS_GLOBAL,\n type CommandTrace,\n type DevComponentRef,\n type DevEvent,\n type DevtoolsHook,\n} from './devtools-events.js';\nimport type { Tagged } from './types.js';\n\nexport const DEVTOOLS_ENABLED: boolean = true;\n\nconst hook = (): DevtoolsHook | undefined =>\n (globalThis as Record<string, unknown>)[DEVTOOLS_GLOBAL] as DevtoolsHook | undefined;\n\nconst ids = new WeakMap<Element, number>();\nlet nextId = 1;\n\nfunction ref(element: Element, tag: string): DevComponentRef {\n let id = ids.get(element);\n if (id === undefined) {\n id = nextId++;\n ids.set(element, id);\n }\n return { tag, id, element };\n}\n\n// Builds nothing unless a listener is installed: zero cost in dev without devtools.\nfunction emit(make: () => DevEvent): void {\n const listener = hook();\n if (listener === undefined) return;\n try {\n listener.emit(make());\n } catch (error) {\n console.error('gyral devtools listener threw', error);\n }\n}\n\nconst now = (): number => performance.now();\n\n/** Connected components, so a panel loaded later still lists them (dev builds only). */\nconst live = new Map<Element, string>();\n\n/** Components connected right now, for a panel that starts after they did. */\nexport function devLiveComponents(): readonly DevComponentRef[] {\n return [...live].map(([element, tag]) => ref(element, tag));\n}\n\nexport function devConnect(element: Element, tag: string, connected: boolean): void {\n if (connected) live.set(element, tag);\n else live.delete(element);\n emit(() => ({\n kind: connected ? 'connect' : 'disconnect',\n component: ref(element, tag),\n at: now(),\n }));\n}\n\nexport function devUpdate(\n element: Element,\n tag: string,\n msg: Tagged,\n prev: unknown,\n next: unknown,\n) {\n emit(() => ({ kind: 'update', component: ref(element, tag), msg, prev, next, at: now() }));\n}\n\nexport function devHydrated(element: Element, tag: string, serverRendered: boolean): void {\n emit(() => ({ kind: 'hydrated', component: ref(element, tag), serverRendered, at: now() }));\n}\n\nexport function devStore(store: string, msg: Tagged, prev: unknown, next: unknown): void {\n emit(() => ({ kind: 'store', store, msg, prev, next, at: now() }));\n}\n\n/** A command tracer for one owner (`<tag>#id` or `store:name`). */\nexport function devCommands(owner: () => string): CommandTrace {\n return (event) => {\n emit(() => ({ kind: 'command', owner: owner(), ...event, at: now() }));\n };\n}\n\n/** The label the panel shows for a component's commands. */\nexport function devOwner(element: Element, tag: string): string {\n return `<${tag}>#${String(ref(element, tag).id)}`;\n}\n"]}
1
+ {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"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","sourcesContent":["// Development build of the devtools hook (ADR 0017). Resolved through the `#devtools` import\n// with the `development` condition; production builds get devtools-off.ts instead, so the\n// guarded call sites (`if (DEVTOOLS_ENABLED) …`) disappear from the bundle.\nimport {\n DEVTOOLS_GLOBAL,\n type CommandTrace,\n type DevComponentRef,\n type DevEvent,\n type DevtoolsHook,\n} from './devtools-events.js';\nimport { warnLeakingLitHtmlOnce } from './lit-html-check.js';\nimport type { Tagged } from './types.js';\n\nexport const DEVTOOLS_ENABLED: boolean = true;\n\nconst hook = (): DevtoolsHook | undefined =>\n (globalThis as Record<string, unknown>)[DEVTOOLS_GLOBAL] as DevtoolsHook | undefined;\n\nconst ids = new WeakMap<Element, number>();\nlet nextId = 1;\n\nfunction ref(element: Element, tag: string): DevComponentRef {\n let id = ids.get(element);\n if (id === undefined) {\n id = nextId++;\n ids.set(element, id);\n }\n return { tag, id, element };\n}\n\n// Builds nothing unless a listener is installed: zero cost in dev without devtools.\nfunction emit(make: () => DevEvent): void {\n const listener = hook();\n if (listener === undefined) return;\n try {\n listener.emit(make());\n } catch (error) {\n console.error('gyral devtools listener threw', error);\n }\n}\n\nconst now = (): number => performance.now();\n\n/** Connected components, so a panel loaded later still lists them (dev builds only). */\nconst live = new Map<Element, string>();\n\n/** Components connected right now, for a panel that starts after they did. */\nexport function devLiveComponents(): readonly DevComponentRef[] {\n return [...live].map(([element, tag]) => ref(element, tag));\n}\n\nexport function devConnect(element: Element, tag: string, connected: boolean): void {\n warnLeakingLitHtmlOnce();\n if (connected) live.set(element, tag);\n else live.delete(element);\n emit(() => ({\n kind: connected ? 'connect' : 'disconnect',\n component: ref(element, tag),\n at: now(),\n }));\n}\n\nexport function devUpdate(\n element: Element,\n tag: string,\n msg: Tagged,\n prev: unknown,\n next: unknown,\n) {\n emit(() => ({ kind: 'update', component: ref(element, tag), msg, prev, next, at: now() }));\n}\n\nexport function devHydrated(element: Element, tag: string, serverRendered: boolean): void {\n emit(() => ({ kind: 'hydrated', component: ref(element, tag), serverRendered, at: now() }));\n}\n\nexport function devStore(store: string, msg: Tagged, prev: unknown, next: unknown): void {\n emit(() => ({ kind: 'store', store, msg, prev, next, at: now() }));\n}\n\n/** A command tracer for one owner (`<tag>#id` or `store:name`). */\nexport function devCommands(owner: () => string): CommandTrace {\n return (event) => {\n emit(() => ({ kind: 'command', owner: owner(), ...event, at: now() }));\n };\n}\n\n/** The label the panel shows for a component's commands. */\nexport function devOwner(element: Element, tag: string): string {\n return `<${tag}>#${String(ref(element, tag).id)}`;\n}\n"]}
package/dist/focus.d.ts CHANGED
@@ -25,3 +25,15 @@ export interface FocusInput extends FocusOptions {
25
25
  export declare function focus(selector: string, options?: FocusOptions): Command<never>;
26
26
  /** Runs a focus command against `root` (called by define() after the update completes). */
27
27
  export declare function runFocus(root: ParentNode, tag: string, input: FocusInput): void;
28
+ /** What focusAfterUpdate needs from a component: Lit's update promise and its render root. */
29
+ export interface FocusHost {
30
+ readonly updateComplete: Promise<boolean>;
31
+ readonly isConnected: boolean;
32
+ readonly renderRoot: ParentNode;
33
+ }
34
+ /**
35
+ * Runs a focus command after the render the same update caused, view transition included:
36
+ * define() runs commands before #dispatch registers that update's view transition, so wait a
37
+ * microtask first, then for updateComplete, which waits for the transition (gyral-6zz).
38
+ */
39
+ export declare function focusAfterUpdate(host: FocusHost, tag: string, input: FocusInput): void;
package/dist/focus.js CHANGED
@@ -27,4 +27,16 @@ export function runFocus(root, tag, input) {
27
27
  target.select();
28
28
  }
29
29
  }
30
+ /**
31
+ * Runs a focus command after the render the same update caused, view transition included:
32
+ * define() runs commands before #dispatch registers that update's view transition, so wait a
33
+ * microtask first, then for updateComplete, which waits for the transition (gyral-6zz).
34
+ */
35
+ export function focusAfterUpdate(host, tag, input) {
36
+ void Promise.resolve().then(async () => {
37
+ await host.updateComplete;
38
+ if (host.isConnected)
39
+ runFocus(host.renderRoot, tag, input);
40
+ });
41
+ }
30
42
  //# sourceMappingURL=focus.js.map
package/dist/focus.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"focus.js","sourceRoot":"","sources":["../src/focus.ts"],"names":[],"mappings":"AAKA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,cAAc;IACpB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;CACZ,CAAC;AAcX;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CAAC,QAAgB,EAAE,UAAwB,EAAE;IAChE,MAAM,KAAK,GAAe,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IACnD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAC9D,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,QAAQ,CAAC,IAAgB,EAAE,GAAW,EAAE,KAAiB;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,MAAM,YAAY,WAAW,IAAI,MAAM,YAAY,UAAU,CAAC,EAAE,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,YAAY,KAAK,CAAC,QAAQ,kCAAkC,CAAC,CAAC;QAClF,OAAO;IACT,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK,EAAE,CAAC,CAAC;IAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACtF,MAA2B,CAAC,MAAM,EAAE,CAAC;IACxC,CAAC;AACH,CAAC","sourcesContent":["// Focus management from the model (gyral-czi.28). Moving focus is a side effect, so the model\n// asks for it with a command; define() runs it against the component's own render root after\n// the render that the same reducer caused, so the element to focus already exists.\nimport type { Command } from './command.js';\n\n/** Marker driver: `define()` handles focus commands itself. */\nexport const FOCUS = {\n name: '@gyral/focus',\n run: () => undefined,\n} as const;\n\nexport interface FocusOptions {\n /** Don't scroll the element into view (default `false`: the browser scrolls if needed). */\n readonly preventScroll?: boolean;\n /** Also select the text of an input or textarea. */\n readonly select?: boolean;\n}\n\n/** The input of a `focus()` command. */\nexport interface FocusInput extends FocusOptions {\n readonly selector: string;\n}\n\n/**\n * A command that focuses the first element matching `selector` inside this component (its\n * shadow root, or its own children in light-DOM mode) once the current update has rendered:\n *\n * NextPage: (s, m) => [{ ...s, page: m.page }, [focus('h2', { preventScroll: true })]]\n *\n * Non-focusable targets (a heading) need `tabindex=\"-1\"` in the view.\n */\nexport function focus(selector: string, options: FocusOptions = {}): Command<never> {\n const input: FocusInput = { selector, ...options };\n return { driver: FOCUS, input, onSuccess: () => undefined };\n}\n\n/** Runs a focus command against `root` (called by define() after the update completes). */\nexport function runFocus(root: ParentNode, tag: string, input: FocusInput): void {\n const target = root.querySelector(input.selector);\n if (!(target instanceof HTMLElement || target instanceof SVGElement)) {\n console.warn(`<${tag}> focus(\"${input.selector}\") matched no focusable element.`);\n return;\n }\n target.focus({ preventScroll: input.preventScroll ?? false });\n if (input.select === true && 'select' in target && typeof target.select === 'function') {\n (target as HTMLInputElement).select();\n }\n}\n"]}
1
+ {"version":3,"file":"focus.js","sourceRoot":"","sources":["../src/focus.ts"],"names":[],"mappings":"AAKA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,cAAc;IACpB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;CACZ,CAAC;AAcX;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CAAC,QAAgB,EAAE,UAAwB,EAAE;IAChE,MAAM,KAAK,GAAe,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IACnD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAC9D,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,QAAQ,CAAC,IAAgB,EAAE,GAAW,EAAE,KAAiB;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,MAAM,YAAY,WAAW,IAAI,MAAM,YAAY,UAAU,CAAC,EAAE,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,YAAY,KAAK,CAAC,QAAQ,kCAAkC,CAAC,CAAC;QAClF,OAAO;IACT,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK,EAAE,CAAC,CAAC;IAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACtF,MAA2B,CAAC,MAAM,EAAE,CAAC;IACxC,CAAC;AACH,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAe,EAAE,GAAW,EAAE,KAAiB;IAC9E,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACrC,MAAM,IAAI,CAAC,cAAc,CAAC;QAC1B,IAAI,IAAI,CAAC,WAAW;YAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["// Focus management from the model (gyral-czi.28). Moving focus is a side effect, so the model\n// asks for it with a command; define() runs it against the component's own render root after\n// the render that the same reducer caused, so the element to focus already exists.\nimport type { Command } from './command.js';\n\n/** Marker driver: `define()` handles focus commands itself. */\nexport const FOCUS = {\n name: '@gyral/focus',\n run: () => undefined,\n} as const;\n\nexport interface FocusOptions {\n /** Don't scroll the element into view (default `false`: the browser scrolls if needed). */\n readonly preventScroll?: boolean;\n /** Also select the text of an input or textarea. */\n readonly select?: boolean;\n}\n\n/** The input of a `focus()` command. */\nexport interface FocusInput extends FocusOptions {\n readonly selector: string;\n}\n\n/**\n * A command that focuses the first element matching `selector` inside this component (its\n * shadow root, or its own children in light-DOM mode) once the current update has rendered:\n *\n * NextPage: (s, m) => [{ ...s, page: m.page }, [focus('h2', { preventScroll: true })]]\n *\n * Non-focusable targets (a heading) need `tabindex=\"-1\"` in the view.\n */\nexport function focus(selector: string, options: FocusOptions = {}): Command<never> {\n const input: FocusInput = { selector, ...options };\n return { driver: FOCUS, input, onSuccess: () => undefined };\n}\n\n/** Runs a focus command against `root` (called by define() after the update completes). */\nexport function runFocus(root: ParentNode, tag: string, input: FocusInput): void {\n const target = root.querySelector(input.selector);\n if (!(target instanceof HTMLElement || target instanceof SVGElement)) {\n console.warn(`<${tag}> focus(\"${input.selector}\") matched no focusable element.`);\n return;\n }\n target.focus({ preventScroll: input.preventScroll ?? false });\n if (input.select === true && 'select' in target && typeof target.select === 'function') {\n (target as HTMLInputElement).select();\n }\n}\n\n/** What focusAfterUpdate needs from a component: Lit's update promise and its render root. */\nexport interface FocusHost {\n readonly updateComplete: Promise<boolean>;\n readonly isConnected: boolean;\n readonly renderRoot: ParentNode;\n}\n\n/**\n * Runs a focus command after the render the same update caused, view transition included:\n * define() runs commands before #dispatch registers that update's view transition, so wait a\n * microtask first, then for updateComplete, which waits for the transition (gyral-6zz).\n */\nexport function focusAfterUpdate(host: FocusHost, tag: string, input: FocusInput): void {\n void Promise.resolve().then(async () => {\n await host.updateComplete;\n if (host.isConnected) runFocus(host.renderRoot, tag, input);\n });\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -33,7 +33,8 @@ export type { Styles } from './styles.js';
33
33
  export { HIDDEN_MARKER, HYDRATE_KEY, isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';
34
34
  export type { GyralElement, GyralElementClass } from './define.js';
35
35
  export type { ComponentSpec, Ctx, FieldIssue, FormFields, IntentInput, IntentNames, IntentParser, IntentRejected, Intents, PropDeclarations, Hydrated, PropsChanged, Stateless, StoreReader, Tagged, Update, } from './types.js';
36
- export { css, html, nothing, svg, unsafeCSS } from 'lit';
36
+ export { css, nothing, unsafeCSS } from 'lit';
37
+ export { html, svg } from './templates.js';
37
38
  export { directive } from 'lit/directive.js';
38
39
  export { classMap } from 'lit/directives/class-map.js';
39
40
  export { keyed } from 'lit/directives/keyed.js';
package/dist/index.js CHANGED
@@ -24,8 +24,10 @@ export { DEVTOOLS_ENABLED as devtoolsEnabled, devLiveComponents as devtoolsLiveC
24
24
  export { DEVTOOLS_GLOBAL } from './devtools-events.js';
25
25
  // Used by @gyral/ssr to render light-DOM components as plain children (ADR 0014).
26
26
  export { HIDDEN_MARKER, HYDRATE_KEY, isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';
27
- // The view layer is Lit. Re-exported so most apps need a single import.
28
- export { css, html, nothing, svg, unsafeCSS } from 'lit';
27
+ // The view layer is Lit. Re-exported so most apps need a single import. `html` and `svg` are
28
+ // Lit's tags with indentation whitespace removed (template-whitespace.ts, gyral-9rf).
29
+ export { css, nothing, unsafeCSS } from 'lit';
30
+ export { html, svg } from './templates.js';
29
31
  export { directive } from 'lit/directive.js';
30
32
  export { classMap } from 'lit/directives/class-map.js';
31
33
  export { keyed } from 'lit/directives/keyed.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAYrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAexD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,mFAAmF;AACnF,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAoB,MAAM,aAAa,CAAC;AACvF,OAAO,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,wEAAwE;AACxE,OAAO,EACL,gBAAgB,IAAI,eAAe,EACnC,iBAAiB,IAAI,sBAAsB,GAC5C,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAUvD,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqB/F,wEAAwE;AACxE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { command, defineDriver } from './command.js';\nexport type {\n AnyDriver,\n Command,\n CommandHandlers,\n Concurrency,\n Driver,\n DriverContext,\n DriverOverrides,\n Next,\n RetryPolicy,\n} from './command.js';\nexport { child, emit } from './children.js';\nexport { focus } from './focus.js';\nexport type { FocusOptions } from './focus.js';\nexport { invokersSupported } from './invokers.js';\nexport type { CommandInfo } from './invokers.js';\nexport { ISLAND_ATTRIBUTE } from './islands.js';\nexport type { HydrateStrategy } from './islands.js';\nexport type { ChildSource, OutputSource } from './children.js';\nexport { define } from './define.js';\nexport { changed, defineStore, send } from './store.js';\nexport type {\n AnyStore,\n AnyStoreInstance,\n Store,\n StoreChanged,\n StoreInstance,\n StoreOverrides,\n StoreRef,\n StoreSendInput,\n SeedCheck,\n StoreResolver,\n StoreSpec,\n StoreUpdate,\n} from './store.js';\nexport { STORE_SEND } from './store.js';\n// Store scoping: used by @gyral/ssr (server scope + page seed) and @gyral/testing.\nexport {\n resetDocumentStores,\n scriptSafeJson,\n STORE_SEED_ATTRIBUTE,\n StoreRegistry,\n STORES_ELEMENT,\n withStoreScope,\n} from './store-scope.js';\nexport { defineStoresProvider } from './stores-provider.js';\nexport { DRIVERS_ELEMENT, provideDrivers } from './drivers-scope.js';\nexport { jsonHazard, warnJsonHazard } from './json-safety.js';\nexport { random, randomDriver, randomInt, toInt, type RandomInput } from './random.js';\nexport {\n defineForm,\n field,\n fieldErrors,\n form,\n formDataToObject,\n formFields,\n intentRejectedSchema,\n redirectedTo,\n validateForm,\n} from './forms.js';\nexport type { FormDefinition, FormRedirected, FormResult, FormValue } from './forms.js';\nexport { ElementDirective } from './element-directive.js';\nexport { runInit } from './init.js';\nexport { invalid } from './invalid.js';\nexport { findInScope, labelledBy } from './accessible-name.js';\nexport { liveBoolean } from './live-boolean.js';\nexport { textarea, textareaMarkup } from './textarea.js';\n// Devtools event stream (ADR 0017): emitted in development builds only.\nexport {\n DEVTOOLS_ENABLED as devtoolsEnabled,\n devLiveComponents as devtoolsLiveComponents,\n} from '#devtools';\nexport { DEVTOOLS_GLOBAL } from './devtools-events.js';\nexport type {\n CommandPhase,\n CommandTraceEvent,\n DevComponentRef,\n DevEvent,\n DevtoolsHook,\n} from './devtools-events.js';\nexport type { TextareaAttribute, TextareaOptions } from './textarea.js';\nexport type { Styles } from './styles.js';\n// Used by @gyral/ssr to render light-DOM components as plain children (ADR 0014).\nexport { HIDDEN_MARKER, HYDRATE_KEY, isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';\nexport type { GyralElement, GyralElementClass } from './define.js';\nexport type {\n ComponentSpec,\n Ctx,\n FieldIssue,\n FormFields,\n IntentInput,\n IntentNames,\n IntentParser,\n IntentRejected,\n Intents,\n PropDeclarations,\n Hydrated,\n PropsChanged,\n Stateless,\n StoreReader,\n Tagged,\n Update,\n} from './types.js';\n\n// The view layer is Lit. Re-exported so most apps need a single import.\nexport { css, html, nothing, svg, unsafeCSS } from 'lit';\nexport { directive } from 'lit/directive.js';\nexport { classMap } from 'lit/directives/class-map.js';\nexport { keyed } from 'lit/directives/keyed.js';\nexport { live } from 'lit/directives/live.js';\nexport { repeat } from 'lit/directives/repeat.js';\nexport { styleMap } from 'lit/directives/style-map.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAYrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAexD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,mFAAmF;AACnF,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAoB,MAAM,aAAa,CAAC;AACvF,OAAO,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzD,wEAAwE;AACxE,OAAO,EACL,gBAAgB,IAAI,eAAe,EACnC,iBAAiB,IAAI,sBAAsB,GAC5C,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAUvD,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqB/F,6FAA6F;AAC7F,sFAAsF;AACtF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { command, defineDriver } from './command.js';\nexport type {\n AnyDriver,\n Command,\n CommandHandlers,\n Concurrency,\n Driver,\n DriverContext,\n DriverOverrides,\n Next,\n RetryPolicy,\n} from './command.js';\nexport { child, emit } from './children.js';\nexport { focus } from './focus.js';\nexport type { FocusOptions } from './focus.js';\nexport { invokersSupported } from './invokers.js';\nexport type { CommandInfo } from './invokers.js';\nexport { ISLAND_ATTRIBUTE } from './islands.js';\nexport type { HydrateStrategy } from './islands.js';\nexport type { ChildSource, OutputSource } from './children.js';\nexport { define } from './define.js';\nexport { changed, defineStore, send } from './store.js';\nexport type {\n AnyStore,\n AnyStoreInstance,\n Store,\n StoreChanged,\n StoreInstance,\n StoreOverrides,\n StoreRef,\n StoreSendInput,\n SeedCheck,\n StoreResolver,\n StoreSpec,\n StoreUpdate,\n} from './store.js';\nexport { STORE_SEND } from './store.js';\n// Store scoping: used by @gyral/ssr (server scope + page seed) and @gyral/testing.\nexport {\n resetDocumentStores,\n scriptSafeJson,\n STORE_SEED_ATTRIBUTE,\n StoreRegistry,\n STORES_ELEMENT,\n withStoreScope,\n} from './store-scope.js';\nexport { defineStoresProvider } from './stores-provider.js';\nexport { DRIVERS_ELEMENT, provideDrivers } from './drivers-scope.js';\nexport { jsonHazard, warnJsonHazard } from './json-safety.js';\nexport { random, randomDriver, randomInt, toInt, type RandomInput } from './random.js';\nexport {\n defineForm,\n field,\n fieldErrors,\n form,\n formDataToObject,\n formFields,\n intentRejectedSchema,\n redirectedTo,\n validateForm,\n} from './forms.js';\nexport type { FormDefinition, FormRedirected, FormResult, FormValue } from './forms.js';\nexport { ElementDirective } from './element-directive.js';\nexport { runInit } from './init.js';\nexport { invalid } from './invalid.js';\nexport { findInScope, labelledBy } from './accessible-name.js';\nexport { liveBoolean } from './live-boolean.js';\nexport { textarea, textareaMarkup } from './textarea.js';\n// Devtools event stream (ADR 0017): emitted in development builds only.\nexport {\n DEVTOOLS_ENABLED as devtoolsEnabled,\n devLiveComponents as devtoolsLiveComponents,\n} from '#devtools';\nexport { DEVTOOLS_GLOBAL } from './devtools-events.js';\nexport type {\n CommandPhase,\n CommandTraceEvent,\n DevComponentRef,\n DevEvent,\n DevtoolsHook,\n} from './devtools-events.js';\nexport type { TextareaAttribute, TextareaOptions } from './textarea.js';\nexport type { Styles } from './styles.js';\n// Used by @gyral/ssr to render light-DOM components as plain children (ADR 0014).\nexport { HIDDEN_MARKER, HYDRATE_KEY, isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';\nexport type { GyralElement, GyralElementClass } from './define.js';\nexport type {\n ComponentSpec,\n Ctx,\n FieldIssue,\n FormFields,\n IntentInput,\n IntentNames,\n IntentParser,\n IntentRejected,\n Intents,\n PropDeclarations,\n Hydrated,\n PropsChanged,\n Stateless,\n StoreReader,\n Tagged,\n Update,\n} from './types.js';\n\n// The view layer is Lit. Re-exported so most apps need a single import. `html` and `svg` are\n// Lit's tags with indentation whitespace removed (template-whitespace.ts, gyral-9rf).\nexport { css, nothing, unsafeCSS } from 'lit';\nexport { html, svg } from './templates.js';\nexport { directive } from 'lit/directive.js';\nexport { classMap } from 'lit/directives/class-map.js';\nexport { keyed } from 'lit/directives/keyed.js';\nexport { live } from 'lit/directives/live.js';\nexport { repeat } from 'lit/directives/repeat.js';\nexport { styleMap } from 'lit/directives/style-map.js';\n"]}
@@ -1,66 +1,100 @@
1
- import { Data, Duration, Effect, Fiber, Schedule } from 'effect';
2
1
  import { DEVTOOLS_ENABLED } from '#devtools';
3
- import { fork } from './runtime.js';
4
- class DriverFailure extends Data.TaggedError('DriverFailure') {
2
+ /** Internal rejection for an aborted task; callers check `signal.aborted`, never this. */
3
+ class Interrupted extends Error {
5
4
  }
6
- const isRunning = (fiber) => fiber !== undefined && fiber.unsafePoll() === null;
7
- const withRetry = (attempt, policy) => {
8
- if (policy === undefined || policy.times <= 0)
9
- return attempt;
10
- const base = Duration.millis(policy.delayMs ?? 0);
11
- const delays = policy.backoff === 'exponential' ? Schedule.exponential(base) : Schedule.spaced(base);
12
- return Effect.retry(attempt, delays.pipe(Schedule.intersect(Schedule.recurs(policy.times))));
5
+ /** Rejects when `signal` aborts. Marked handled so a losing race never reports it. */
6
+ const aborted = (signal) => {
7
+ const promise = new Promise((_resolve, reject) => {
8
+ const interrupt = () => {
9
+ reject(new Interrupted('gyral: command interrupted'));
10
+ };
11
+ if (signal.aborted)
12
+ interrupt();
13
+ else
14
+ signal.addEventListener('abort', interrupt, { once: true });
15
+ });
16
+ promise.catch(() => undefined);
17
+ return promise;
18
+ };
19
+ /** Waits `ms`, or rejects as soon as `signal` aborts. */
20
+ const sleep = (ms, signal) => Promise.race([
21
+ new Promise((resolve) => {
22
+ const timer = setTimeout(resolve, ms);
23
+ signal.addEventListener('abort', () => {
24
+ clearTimeout(timer);
25
+ }, { once: true });
26
+ }),
27
+ aborted(signal),
28
+ ]);
29
+ /** Delay before retry number `retry` (0-based): fixed, or doubling from `delayMs`. */
30
+ const delayFor = (policy, retry) => {
31
+ const base = policy.delayMs ?? 0;
32
+ return policy.backoff === 'exponential' ? base * 2 ** retry : base;
13
33
  };
14
- const execute = (driver, cmd, dispatch, report) => {
15
- const deliver = (output) => {
34
+ async function attemptWithRetry(driver, cmd, signal, emit) {
35
+ const policy = driver.retry;
36
+ for (let retry = 0;; retry += 1) {
16
37
  try {
17
- const msg = cmd.onSuccess(output);
18
- if (msg !== undefined)
19
- dispatch(msg);
20
- }
21
- catch (defect) {
22
- console.error('gyral: command mapper threw', defect);
23
- }
24
- };
25
- const attempt = Effect.tryPromise({
26
- try: (signal) => {
27
- let settled = false;
28
- const emit = (output) => {
29
- if (!settled && !signal.aborted)
30
- deliver(output);
31
- };
32
38
  // The input type was erased by command(); it was built for this driver's name.
33
39
  const result = Promise.resolve(driver.run(cmd.input, { signal, emit }));
34
- return result.finally(() => {
35
- settled = true;
36
- });
37
- },
38
- catch: (cause) => new DriverFailure({ cause }),
39
- });
40
- const traced = DEVTOOLS_ENABLED && report !== undefined
41
- ? withRetry(attempt, driver.retry).pipe(Effect.onInterrupt(() => Effect.sync(() => {
42
- report('interrupted');
43
- })))
44
- : withRetry(attempt, driver.retry);
45
- return traced.pipe(Effect.matchEffect({
46
- onSuccess: (output) => {
47
- if (DEVTOOLS_ENABLED)
48
- report?.('settled', output);
49
- return Effect.succeed(cmd.onSuccess(output));
50
- },
51
- onFailure: (failure) => {
52
- const error = driver.toError === undefined ? failure.cause : driver.toError(failure.cause);
53
- if (DEVTOOLS_ENABLED)
54
- report?.('failed', error);
55
- return cmd.onFailure === undefined
56
- ? Effect.logWarning(`gyral: unhandled failure from driver "${driver.name}"`, error).pipe(Effect.as(undefined))
57
- : Effect.succeed(cmd.onFailure(error));
58
- },
59
- }), Effect.flatMap((msg) => Effect.sync(() => {
40
+ return await Promise.race([result, aborted(signal)]);
41
+ }
42
+ catch (cause) {
43
+ if (signal.aborted || policy === undefined || retry >= policy.times)
44
+ throw cause;
45
+ await sleep(delayFor(policy, retry), signal);
46
+ }
47
+ }
48
+ }
49
+ /** Maps a result to a message and dispatches it; a throwing mapper is logged, not fatal. */
50
+ const deliver = (map, dispatch) => {
51
+ try {
52
+ const msg = map();
60
53
  if (msg !== undefined)
61
54
  dispatch(msg);
62
- })), Effect.catchAllDefect((defect) => Effect.logError('gyral: command mapper threw', defect)));
55
+ }
56
+ catch (defect) {
57
+ console.error('gyral: command mapper threw', defect);
58
+ }
63
59
  };
60
+ async function execute(driver, cmd, signal, dispatch, report) {
61
+ let settled = false;
62
+ const emit = (output) => {
63
+ if (!settled && !signal.aborted)
64
+ deliver(() => cmd.onSuccess(output), dispatch);
65
+ };
66
+ if (DEVTOOLS_ENABLED && report !== undefined) {
67
+ signal.addEventListener('abort', () => {
68
+ if (!settled)
69
+ report('interrupted');
70
+ }, { once: true });
71
+ }
72
+ let output;
73
+ try {
74
+ output = await attemptWithRetry(driver, cmd, signal, emit);
75
+ }
76
+ catch (cause) {
77
+ if (signal.aborted)
78
+ return; // interrupted: reported by the abort listener
79
+ settled = true;
80
+ const error = driver.toError === undefined ? cause : driver.toError(cause);
81
+ if (DEVTOOLS_ENABLED)
82
+ report?.('failed', error);
83
+ if (cmd.onFailure === undefined) {
84
+ console.warn(`gyral: unhandled failure from driver "${driver.name}"`, error);
85
+ return;
86
+ }
87
+ const onFailure = cmd.onFailure;
88
+ deliver(() => onFailure(error), dispatch);
89
+ return;
90
+ }
91
+ if (signal.aborted)
92
+ return;
93
+ settled = true;
94
+ if (DEVTOOLS_ENABLED)
95
+ report?.('settled', output);
96
+ deliver(() => cmd.onSuccess(output), dispatch);
97
+ }
64
98
  export function makeInterpreter(resolve, dispatch, trace) {
65
99
  const reporter = (driver, lane, policy, input) => trace === undefined
66
100
  ? undefined
@@ -81,16 +115,24 @@ export function makeInterpreter(resolve, dispatch, trace) {
81
115
  if (active)
82
116
  dispatch(msg);
83
117
  };
84
- const track = (lane, fiber) => {
85
- if (!isRunning(fiber))
86
- return;
87
- lanes.set(lane, fiber);
88
- all.add(fiber);
89
- fiber.addObserver(() => {
90
- all.delete(fiber);
91
- if (lanes.get(lane) === fiber)
118
+ const start = (lane, driver, cmd, report, after) => {
119
+ const controller = new AbortController();
120
+ const task = { controller, running: true, done: Promise.resolve() };
121
+ const body = async () => {
122
+ if (after !== undefined)
123
+ await after.done;
124
+ if (!controller.signal.aborted) {
125
+ await execute(driver, cmd, controller.signal, guardedDispatch, report);
126
+ }
127
+ };
128
+ task.done = body().finally(() => {
129
+ task.running = false;
130
+ all.delete(task);
131
+ if (lanes.get(lane) === task)
92
132
  lanes.delete(lane);
93
133
  });
134
+ lanes.set(lane, task);
135
+ all.add(task);
94
136
  };
95
137
  const run = (cmd) => {
96
138
  if (!active)
@@ -99,9 +141,8 @@ export function makeInterpreter(resolve, dispatch, trace) {
99
141
  const lane = cmd.key ?? driver.name;
100
142
  const policy = cmd.concurrency ?? driver.concurrency ?? 'merge';
101
143
  const previous = lanes.get(lane);
102
- const inFlight = isRunning(previous) ? previous : undefined;
144
+ const inFlight = previous?.running === true ? previous : undefined;
103
145
  const report = DEVTOOLS_ENABLED ? reporter(driver.name, lane, policy, cmd.input) : undefined;
104
- let program = execute(driver, cmd, guardedDispatch, report);
105
146
  if (inFlight !== undefined && policy === 'exhaust') {
106
147
  if (DEVTOOLS_ENABLED)
107
148
  report?.('dropped');
@@ -109,17 +150,14 @@ export function makeInterpreter(resolve, dispatch, trace) {
109
150
  }
110
151
  if (DEVTOOLS_ENABLED)
111
152
  report?.('issued');
112
- if (inFlight !== undefined) {
113
- if (policy === 'switch')
114
- program = Effect.zipRight(Fiber.interrupt(inFlight), program);
115
- if (policy === 'queue')
116
- program = Effect.zipRight(Fiber.await(inFlight), program);
117
- }
118
- track(lane, fork(program));
153
+ if (inFlight !== undefined && policy === 'switch')
154
+ inFlight.controller.abort();
155
+ start(lane, driver, cmd, report, inFlight !== undefined && policy === 'queue' ? inFlight : undefined);
119
156
  };
120
157
  const dispose = () => {
121
158
  active = false;
122
- fork(Fiber.interruptAll([...all]));
159
+ for (const task of all)
160
+ task.controller.abort();
123
161
  all.clear();
124
162
  lanes.clear();
125
163
  };
@@ -1 +1 @@
1
- {"version":3,"file":"interpreter.js","sourceRoot":"","sources":["../../src/internal/interpreter.ts"],"names":[],"mappings":"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","sourcesContent":["import { Data, Duration, Effect, Fiber, Schedule } from 'effect';\nimport { DEVTOOLS_ENABLED } from '#devtools';\nimport type { AnyDriver, Command, Concurrency, RetryPolicy } from '../command.js';\nimport type { CommandPhase, CommandTrace } from '../devtools-events.js';\nimport { fork } from './runtime.js';\n\nclass DriverFailure extends Data.TaggedError('DriverFailure')<{ readonly cause: unknown }> {}\n\ntype Running = Fiber.RuntimeFiber<void>;\n\n/** Runs commands for one connected element. Disposed on disconnect. */\nexport interface Interpreter<M> {\n readonly run: (cmd: Command<M>) => void;\n readonly dispose: () => void;\n}\n\nconst isRunning = (fiber: Running | undefined): fiber is Running =>\n fiber !== undefined && fiber.unsafePoll() === null;\n\nconst withRetry = <A>(\n attempt: Effect.Effect<A, DriverFailure>,\n policy: RetryPolicy | undefined,\n): Effect.Effect<A, DriverFailure> => {\n if (policy === undefined || policy.times <= 0) return attempt;\n const base = Duration.millis(policy.delayMs ?? 0);\n const delays: Schedule.Schedule<unknown> =\n policy.backoff === 'exponential' ? Schedule.exponential(base) : Schedule.spaced(base);\n return Effect.retry(attempt, delays.pipe(Schedule.intersect(Schedule.recurs(policy.times))));\n};\n\n/** Reports one command's lifecycle to devtools (ADR 0017); undefined in production. */\ntype Report = ((phase: CommandPhase, result?: unknown) => void) | undefined;\n\nconst execute = <M>(\n driver: AnyDriver,\n cmd: Command<M>,\n dispatch: (msg: M) => void,\n report: Report,\n): Effect.Effect<void> => {\n const deliver = (output: unknown): void => {\n try {\n const msg = cmd.onSuccess(output);\n if (msg !== undefined) dispatch(msg);\n } catch (defect) {\n console.error('gyral: command mapper threw', defect);\n }\n };\n const attempt = Effect.tryPromise({\n try: (signal) => {\n let settled = false;\n const emit = (output: unknown): void => {\n if (!settled && !signal.aborted) deliver(output);\n };\n // The input type was erased by command(); it was built for this driver's name.\n const result = Promise.resolve(driver.run(cmd.input as never, { signal, emit }));\n return result.finally(() => {\n settled = true;\n });\n },\n catch: (cause) => new DriverFailure({ cause }),\n });\n const traced =\n DEVTOOLS_ENABLED && report !== undefined\n ? withRetry(attempt, driver.retry).pipe(\n Effect.onInterrupt(() =>\n Effect.sync(() => {\n report('interrupted');\n }),\n ),\n )\n : withRetry(attempt, driver.retry);\n return traced.pipe(\n Effect.matchEffect({\n onSuccess: (output) => {\n if (DEVTOOLS_ENABLED) report?.('settled', output);\n return Effect.succeed(cmd.onSuccess(output));\n },\n onFailure: (failure) => {\n const error = driver.toError === undefined ? failure.cause : driver.toError(failure.cause);\n if (DEVTOOLS_ENABLED) report?.('failed', error);\n return cmd.onFailure === undefined\n ? Effect.logWarning(`gyral: unhandled failure from driver \"${driver.name}\"`, error).pipe(\n Effect.as(undefined),\n )\n : Effect.succeed(cmd.onFailure(error));\n },\n }),\n Effect.flatMap((msg) =>\n Effect.sync(() => {\n if (msg !== undefined) dispatch(msg);\n }),\n ),\n Effect.catchAllDefect((defect) => Effect.logError('gyral: command mapper threw', defect)),\n );\n};\n\nexport function makeInterpreter<M>(\n resolve: (driver: AnyDriver) => AnyDriver,\n dispatch: (msg: M) => void,\n trace?: CommandTrace,\n): Interpreter<M> {\n const reporter = (driver: string, lane: string, policy: Concurrency, input: unknown): Report =>\n trace === undefined\n ? undefined\n : (phase, result) => {\n trace({\n phase,\n driver,\n lane,\n policy,\n input,\n ...(result === undefined ? {} : { result }),\n });\n };\n const lanes = new Map<string, Running>();\n const all = new Set<Running>();\n let active = true;\n const guardedDispatch = (msg: M): void => {\n if (active) dispatch(msg);\n };\n\n const track = (lane: string, fiber: Running): void => {\n if (!isRunning(fiber)) return;\n lanes.set(lane, fiber);\n all.add(fiber);\n fiber.addObserver(() => {\n all.delete(fiber);\n if (lanes.get(lane) === fiber) lanes.delete(lane);\n });\n };\n\n const run = (cmd: Command<M>): void => {\n if (!active) return;\n const driver = resolve(cmd.driver);\n const lane = cmd.key ?? driver.name;\n const policy = cmd.concurrency ?? driver.concurrency ?? 'merge';\n const previous = lanes.get(lane);\n const inFlight = isRunning(previous) ? previous : undefined;\n const report = DEVTOOLS_ENABLED ? reporter(driver.name, lane, policy, cmd.input) : undefined;\n let program = execute(driver, cmd, guardedDispatch, report);\n if (inFlight !== undefined && policy === 'exhaust') {\n if (DEVTOOLS_ENABLED) report?.('dropped');\n return;\n }\n if (DEVTOOLS_ENABLED) report?.('issued');\n if (inFlight !== undefined) {\n if (policy === 'switch') program = Effect.zipRight(Fiber.interrupt(inFlight), program);\n if (policy === 'queue') program = Effect.zipRight(Fiber.await(inFlight), program);\n }\n track(lane, fork(program));\n };\n\n const dispose = (): void => {\n active = false;\n fork(Fiber.interruptAll([...all]));\n all.clear();\n lanes.clear();\n };\n\n return { run, dispose };\n}\n"]}
1
+ {"version":3,"file":"interpreter.js","sourceRoot":"","sources":["../../src/internal/interpreter.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEVTOOLS_ENABLED } from '#devtools';\nimport type { AnyDriver, Command, Concurrency, RetryPolicy } from '../command.js';\nimport type { CommandPhase, CommandTrace } from '../devtools-events.js';\n\n// The command interpreter (ADR 0015: hand-written, no runtime dependencies). Each running command is a task with its own AbortController; lanes hold the\n// latest task per key. Interruption is `controller.abort()`, retry schedules are timers\n// that cancel on abort, and `queue` chains on the previous task's promise.\n\n/** Runs commands for one connected element. Disposed on disconnect. */\nexport interface Interpreter<M> {\n readonly run: (cmd: Command<M>) => void;\n readonly dispose: () => void;\n}\n\ninterface Task {\n readonly controller: AbortController;\n done: Promise<void>;\n running: boolean;\n}\n\n/** Reports one command's lifecycle to devtools (ADR 0017); undefined in production. */\ntype Report = ((phase: CommandPhase, result?: unknown) => void) | undefined;\n\n/** Internal rejection for an aborted task; callers check `signal.aborted`, never this. */\nclass Interrupted extends Error {}\n\n/** Rejects when `signal` aborts. Marked handled so a losing race never reports it. */\nconst aborted = (signal: AbortSignal): Promise<never> => {\n const promise = new Promise<never>((_resolve, reject) => {\n const interrupt = (): void => {\n reject(new Interrupted('gyral: command interrupted'));\n };\n if (signal.aborted) interrupt();\n else signal.addEventListener('abort', interrupt, { once: true });\n });\n promise.catch(() => undefined);\n return promise;\n};\n\n/** Waits `ms`, or rejects as soon as `signal` aborts. */\nconst sleep = (ms: number, signal: AbortSignal): Promise<void> =>\n Promise.race([\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n },\n { once: true },\n );\n }),\n aborted(signal),\n ]);\n\n/** Delay before retry number `retry` (0-based): fixed, or doubling from `delayMs`. */\nconst delayFor = (policy: RetryPolicy, retry: number): number => {\n const base = policy.delayMs ?? 0;\n return policy.backoff === 'exponential' ? base * 2 ** retry : base;\n};\n\nasync function attemptWithRetry(\n driver: AnyDriver,\n cmd: Command<unknown>,\n signal: AbortSignal,\n emit: (output: unknown) => void,\n): Promise<unknown> {\n const policy = driver.retry;\n for (let retry = 0; ; retry += 1) {\n try {\n // The input type was erased by command(); it was built for this driver's name.\n const result = Promise.resolve(driver.run(cmd.input as never, { signal, emit }));\n return await Promise.race([result, aborted(signal)]);\n } catch (cause) {\n if (signal.aborted || policy === undefined || retry >= policy.times) throw cause;\n await sleep(delayFor(policy, retry), signal);\n }\n }\n}\n\n/** Maps a result to a message and dispatches it; a throwing mapper is logged, not fatal. */\nconst deliver = <M>(map: () => M | undefined, dispatch: (msg: M) => void): void => {\n try {\n const msg = map();\n if (msg !== undefined) dispatch(msg);\n } catch (defect) {\n console.error('gyral: command mapper threw', defect);\n }\n};\n\nasync function execute<M>(\n driver: AnyDriver,\n cmd: Command<M>,\n signal: AbortSignal,\n dispatch: (msg: M) => void,\n report: Report,\n): Promise<void> {\n let settled = false;\n const emit = (output: unknown): void => {\n if (!settled && !signal.aborted) deliver(() => cmd.onSuccess(output), dispatch);\n };\n if (DEVTOOLS_ENABLED && report !== undefined) {\n signal.addEventListener(\n 'abort',\n () => {\n if (!settled) report('interrupted');\n },\n { once: true },\n );\n }\n let output: unknown;\n try {\n output = await attemptWithRetry(driver, cmd, signal, emit);\n } catch (cause) {\n if (signal.aborted) return; // interrupted: reported by the abort listener\n settled = true;\n const error = driver.toError === undefined ? cause : driver.toError(cause);\n if (DEVTOOLS_ENABLED) report?.('failed', error);\n if (cmd.onFailure === undefined) {\n console.warn(`gyral: unhandled failure from driver \"${driver.name}\"`, error);\n return;\n }\n const onFailure = cmd.onFailure;\n deliver(() => onFailure(error), dispatch);\n return;\n }\n if (signal.aborted) return;\n settled = true;\n if (DEVTOOLS_ENABLED) report?.('settled', output);\n deliver(() => cmd.onSuccess(output), dispatch);\n}\n\nexport function makeInterpreter<M>(\n resolve: (driver: AnyDriver) => AnyDriver,\n dispatch: (msg: M) => void,\n trace?: CommandTrace,\n): Interpreter<M> {\n const reporter = (driver: string, lane: string, policy: Concurrency, input: unknown): Report =>\n trace === undefined\n ? undefined\n : (phase, result) => {\n trace({\n phase,\n driver,\n lane,\n policy,\n input,\n ...(result === undefined ? {} : { result }),\n });\n };\n const lanes = new Map<string, Task>();\n const all = new Set<Task>();\n let active = true;\n const guardedDispatch = (msg: M): void => {\n if (active) dispatch(msg);\n };\n\n const start = (\n lane: string,\n driver: AnyDriver,\n cmd: Command<M>,\n report: Report,\n after?: Task,\n ): void => {\n const controller = new AbortController();\n const task: Task = { controller, running: true, done: Promise.resolve() };\n const body = async (): Promise<void> => {\n if (after !== undefined) await after.done;\n if (!controller.signal.aborted) {\n await execute(driver, cmd, controller.signal, guardedDispatch, report);\n }\n };\n task.done = body().finally(() => {\n task.running = false;\n all.delete(task);\n if (lanes.get(lane) === task) lanes.delete(lane);\n });\n lanes.set(lane, task);\n all.add(task);\n };\n\n const run = (cmd: Command<M>): void => {\n if (!active) return;\n const driver = resolve(cmd.driver);\n const lane = cmd.key ?? driver.name;\n const policy = cmd.concurrency ?? driver.concurrency ?? 'merge';\n const previous = lanes.get(lane);\n const inFlight = previous?.running === true ? previous : undefined;\n const report = DEVTOOLS_ENABLED ? reporter(driver.name, lane, policy, cmd.input) : undefined;\n if (inFlight !== undefined && policy === 'exhaust') {\n if (DEVTOOLS_ENABLED) report?.('dropped');\n return;\n }\n if (DEVTOOLS_ENABLED) report?.('issued');\n if (inFlight !== undefined && policy === 'switch') inFlight.controller.abort();\n start(\n lane,\n driver,\n cmd,\n report,\n inFlight !== undefined && policy === 'queue' ? inFlight : undefined,\n );\n };\n\n const dispose = (): void => {\n active = false;\n for (const task of all) task.controller.abort();\n all.clear();\n lanes.clear();\n };\n\n return { run, dispose };\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /** The loaded lit-html versions that have the repeat() leak. */
2
+ export declare function leakingLitHtml(versions: readonly string[]): string[];
3
+ /** Warns once per page when a leaking lit-html is loaded. */
4
+ export declare function warnLeakingLitHtmlOnce(): void;
5
+ /** Test hook: allow the warning again. */
6
+ export declare function resetLitHtmlWarningForTests(): void;
@@ -0,0 +1,48 @@
1
+ // Development-only warning for the lit-html >= 3.3.1 repeat() leak (gyral-9y6, ADR 0015,
2
+ // lit/lit#5298): removing repeat() items leaves one comment node per item in the DOM.
3
+ // lit-html records every loaded copy's version in `globalThis.litHtmlVersions`. Called from
4
+ // devtools.ts, which production builds replace, so this costs nothing in production.
5
+ /** First lit-html release with the leak. Raise the upper bound once upstream ships a fix. */
6
+ const FIRST_LEAKING = [3, 3, 1];
7
+ function parse(version) {
8
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
9
+ return match === null ? undefined : [Number(match[1]), Number(match[2]), Number(match[3])];
10
+ }
11
+ function atLeast(version, floor) {
12
+ for (let i = 0; i < floor.length; i++) {
13
+ const a = version[i] ?? 0;
14
+ const b = floor[i] ?? 0;
15
+ if (a !== b)
16
+ return a > b;
17
+ }
18
+ return true;
19
+ }
20
+ /** The loaded lit-html versions that have the repeat() leak. */
21
+ export function leakingLitHtml(versions) {
22
+ return versions.filter((v) => {
23
+ const parsed = parse(v);
24
+ return parsed !== undefined && parsed[0] === 3 && atLeast(parsed, FIRST_LEAKING);
25
+ });
26
+ }
27
+ let warned = false;
28
+ /** Warns once per page when a leaking lit-html is loaded. */
29
+ export function warnLeakingLitHtmlOnce() {
30
+ if (warned)
31
+ return;
32
+ warned = true;
33
+ const versions = globalThis.litHtmlVersions;
34
+ if (!Array.isArray(versions))
35
+ return;
36
+ const leaking = leakingLitHtml(versions.filter((v) => typeof v === 'string'));
37
+ if (leaking.length === 0)
38
+ return;
39
+ console.warn(`gyral: lit-html ${leaking.join(', ')} leaks one DOM comment per removed repeat() item ` +
40
+ '(https://github.com/lit/lit/issues/5298). Pin lit-html to 3.3.0 with an override: ' +
41
+ 'https://github.com/gyraljs/gyral/blob/main/docs/references/consumer-setup.md' +
42
+ '#known-issue-lit-html-331-list-leak (development builds only).');
43
+ }
44
+ /** Test hook: allow the warning again. */
45
+ export function resetLitHtmlWarningForTests() {
46
+ warned = false;
47
+ }
48
+ //# sourceMappingURL=lit-html-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lit-html-check.js","sourceRoot":"","sources":["../src/lit-html-check.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,sFAAsF;AACtF,4FAA4F;AAC5F,qFAAqF;AAErF,6FAA6F;AAC7F,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;AAEzC,SAAS,KAAK,CAAC,OAAe;IAC5B,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,OAAO,CAAC,OAA0B,EAAE,KAAwB;IACnE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,QAA2B;IACxD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB,6DAA6D;AAC7D,MAAM,UAAU,sBAAsB;IACpC,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,GAAG,IAAI,CAAC;IACd,MAAM,QAAQ,GAAI,UAA4C,CAAC,eAAe,CAAC;IAC/E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO;IACrC,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;IAC3F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,OAAO,CAAC,IAAI,CACV,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,mDAAmD;QACtF,oFAAoF;QACpF,8EAA8E;QAC9E,gEAAgE,CACnE,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,2BAA2B;IACzC,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC","sourcesContent":["// Development-only warning for the lit-html >= 3.3.1 repeat() leak (gyral-9y6, ADR 0015,\n// lit/lit#5298): removing repeat() items leaves one comment node per item in the DOM.\n// lit-html records every loaded copy's version in `globalThis.litHtmlVersions`. Called from\n// devtools.ts, which production builds replace, so this costs nothing in production.\n\n/** First lit-html release with the leak. Raise the upper bound once upstream ships a fix. */\nconst FIRST_LEAKING = [3, 3, 1] as const;\n\nfunction parse(version: string): readonly number[] | undefined {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n return match === null ? undefined : [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\nfunction atLeast(version: readonly number[], floor: readonly number[]): boolean {\n for (let i = 0; i < floor.length; i++) {\n const a = version[i] ?? 0;\n const b = floor[i] ?? 0;\n if (a !== b) return a > b;\n }\n return true;\n}\n\n/** The loaded lit-html versions that have the repeat() leak. */\nexport function leakingLitHtml(versions: readonly string[]): string[] {\n return versions.filter((v) => {\n const parsed = parse(v);\n return parsed !== undefined && parsed[0] === 3 && atLeast(parsed, FIRST_LEAKING);\n });\n}\n\nlet warned = false;\n\n/** Warns once per page when a leaking lit-html is loaded. */\nexport function warnLeakingLitHtmlOnce(): void {\n if (warned) return;\n warned = true;\n const versions = (globalThis as { litHtmlVersions?: unknown }).litHtmlVersions;\n if (!Array.isArray(versions)) return;\n const leaking = leakingLitHtml(versions.filter((v): v is string => typeof v === 'string'));\n if (leaking.length === 0) return;\n console.warn(\n `gyral: lit-html ${leaking.join(', ')} leaks one DOM comment per removed repeat() item ` +\n '(https://github.com/lit/lit/issues/5298). Pin lit-html to 3.3.0 with an override: ' +\n 'https://github.com/gyraljs/gyral/blob/main/docs/references/consumer-setup.md' +\n '#known-issue-lit-html-331-list-leak (development builds only).',\n );\n}\n\n/** Test hook: allow the warning again. */\nexport function resetLitHtmlWarningForTests(): void {\n warned = false;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /** Minified copy of template strings (same length, bindings unmoved). Idempotent. */
2
+ export declare function minifyStrings(strings: readonly string[]): string[];
3
+ /**
4
+ * The minified TemplateStringsArray for a call site. Cached by identity, so Lit's own template
5
+ * cache (keyed by the strings array) still hits once per call site.
6
+ */
7
+ export declare function minifyTemplate(strings: TemplateStringsArray): TemplateStringsArray;
@@ -0,0 +1,192 @@
1
+ // Template whitespace minification (gyral-9rf, ADR 0016 addendum).
2
+ //
3
+ // lit-html keeps every newline and indent between tags as a DOM text node; compilers (Svelte,
4
+ // Solid, JSX) drop them. In a benchmark table row that is 12 extra nodes out of 25, and removing
5
+ // them made create/replace/clear 11-24% faster (gyral-1kq). Gyral's `html` and `svg` tags run the
6
+ // template strings through `minifyTemplate()` once per call site (cached by strings identity),
7
+ // so the server and the browser always build the same template: hydration digests match in any
8
+ // toolchain (Vite, tsx, plain Node), with no build step.
9
+ //
10
+ // Rules (the same text everywhere, documented in docs/references/consumer-setup.md):
11
+ // - Whitespace-only text that contains a newline is removed when it sits next to a template
12
+ // edge, a block-level tag, or the inside edge of a <button>/<select> (CSS never renders it
13
+ // there). Between two inline neighbours (phrasing elements, custom elements, bindings,
14
+ // comments) it collapses to one space.
15
+ // - Other runs of whitespace in text collapse to one space; a leading or trailing run with a
16
+ // newline next to one of those edges is removed.
17
+ // - <pre>, <textarea>, <script>, <style> and <title> contents, tags and attribute values,
18
+ // and comments are copied unchanged. Binding positions never move: the result has the same
19
+ // number of strings, so Lit parts and SSR hydration markers line up.
20
+ // The function is idempotent: minifying minified strings changes nothing.
21
+ /** Elements whose box is block-level (or never laid out inline) by default. */
22
+ const BLOCK = new Set(('address article aside blockquote body caption col colgroup datalist dd details dialog div ' +
23
+ 'dl dt fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html ' +
24
+ 'legend li main menu nav ol optgroup option p pre search section select summary table tbody ' +
25
+ 'td tfoot th thead tr ul ' +
26
+ // SVG containers and shapes: whitespace between them is never rendered.
27
+ 'circle clippath defs ellipse g line lineargradient marker mask path pattern polygon ' +
28
+ 'polyline radialgradient rect stop symbol use').split(' '));
29
+ /** Elements whose text content is copied verbatim (whitespace is meaningful or raw text). */
30
+ const RAW_TEXT = new Set(['script', 'style', 'textarea', 'title']);
31
+ const PRESERVE = 'pre';
32
+ /**
33
+ * Inline-block controls: they sit inline among their neighbours, but whitespace at the start or
34
+ * end of their own content is never rendered (it is at the edge of their own line box).
35
+ */
36
+ const INLINE_BOX = new Set(['button', 'select']);
37
+ /**
38
+ * The edge a tag makes for the text next to it. `inside` is true when the text is inside the
39
+ * element (after its start tag, or before its end tag).
40
+ */
41
+ function edgeOfTag(name, inside) {
42
+ const lower = name.toLowerCase();
43
+ if (BLOCK.has(lower))
44
+ return 'block';
45
+ return inside && INLINE_BOX.has(lower) ? 'block' : 'inline';
46
+ }
47
+ const WS = /^[ \t\n\r\f]*$/;
48
+ const RUN = /[ \t\n\r\f]+/g;
49
+ function minifyChunk(text, left, right) {
50
+ if (text === '')
51
+ return text;
52
+ if (WS.test(text)) {
53
+ const removable = /[\n\r]/.test(text) &&
54
+ (left === 'template' || right === 'template' || left === 'block' || right === 'block');
55
+ return removable ? '' : ' ';
56
+ }
57
+ // Mixed text: a leading or trailing run with a newline next to a removable edge goes too.
58
+ const dropStart = left !== 'inline' && /^[ \t\f]*[\n\r]/.test(text);
59
+ const dropEnd = right !== 'inline' && /[\n\r][ \t\n\r\f]*$/.test(text);
60
+ let body = text.replace(RUN, ' ');
61
+ if (dropStart)
62
+ body = body.replace(/^ /, '');
63
+ if (dropEnd)
64
+ body = body.replace(/ $/, '');
65
+ return body;
66
+ }
67
+ const Mode = { Text: 0, Tag: 1, Comment: 2, RawText: 3 };
68
+ const TAG_START = /^<(\/?)([A-Za-z!?][^\s/>]*)/;
69
+ /** Minify one segment, carrying the HTML scan state across bindings. */
70
+ function minifySegment(segment, state, last) {
71
+ let out = '';
72
+ let chunk = '';
73
+ const flushText = (right) => {
74
+ out += state.preDepth > 0 ? chunk : minifyChunk(chunk, state.left, right);
75
+ chunk = '';
76
+ };
77
+ let i = 0;
78
+ while (i < segment.length) {
79
+ const ch = segment.charAt(i);
80
+ if (state.mode === Mode.Comment) {
81
+ if (segment.startsWith('-->', i)) {
82
+ out += '-->';
83
+ i += 3;
84
+ state.mode = Mode.Text;
85
+ state.left = 'inline';
86
+ }
87
+ else {
88
+ out += ch;
89
+ i += 1;
90
+ }
91
+ continue;
92
+ }
93
+ if (state.mode === Mode.RawText) {
94
+ const close = `</${state.rawName}`;
95
+ if (segment.slice(i, i + close.length).toLowerCase() === close) {
96
+ state.mode = Mode.Text;
97
+ state.left = 'inline';
98
+ continue; // the closing tag is read in Text mode
99
+ }
100
+ out += ch;
101
+ i += 1;
102
+ continue;
103
+ }
104
+ if (state.mode === Mode.Tag) {
105
+ out += ch;
106
+ i += 1;
107
+ if (state.quote !== '') {
108
+ if (ch === state.quote)
109
+ state.quote = '';
110
+ }
111
+ else if (ch === '"' || ch === "'") {
112
+ state.quote = ch;
113
+ }
114
+ else if (ch === '>') {
115
+ endTag(state, out.endsWith('/>'));
116
+ }
117
+ continue;
118
+ }
119
+ // Text mode.
120
+ if (ch === '<' && segment.startsWith('<!--', i)) {
121
+ flushText('inline');
122
+ state.mode = Mode.Comment;
123
+ out += '<!--';
124
+ i += 4;
125
+ continue;
126
+ }
127
+ const tag = ch === '<' ? TAG_START.exec(segment.slice(i)) : null;
128
+ if (tag !== null) {
129
+ const name = (tag[2] ?? '').toLowerCase();
130
+ const closing = tag[1] === '/';
131
+ flushText(edgeOfTag(name, closing));
132
+ state.closing = closing;
133
+ state.tagName = name;
134
+ state.mode = Mode.Tag;
135
+ out += tag[0];
136
+ i += tag[0].length;
137
+ continue;
138
+ }
139
+ chunk += ch;
140
+ i += 1;
141
+ }
142
+ if (state.mode === Mode.Text) {
143
+ flushText(last ? 'template' : 'inline');
144
+ state.left = 'inline'; // the next segment starts after a binding
145
+ }
146
+ return out;
147
+ }
148
+ function endTag(state, selfClosing) {
149
+ const name = state.tagName;
150
+ state.mode = Mode.Text;
151
+ state.left = edgeOfTag(name, !state.closing && !selfClosing);
152
+ if (name === PRESERVE) {
153
+ if (state.closing)
154
+ state.preDepth = Math.max(0, state.preDepth - 1);
155
+ else if (!selfClosing)
156
+ state.preDepth += 1;
157
+ }
158
+ if (!state.closing && !selfClosing && RAW_TEXT.has(name)) {
159
+ state.mode = Mode.RawText;
160
+ state.rawName = name;
161
+ }
162
+ }
163
+ /** Minified copy of template strings (same length, bindings unmoved). Idempotent. */
164
+ export function minifyStrings(strings) {
165
+ const state = {
166
+ mode: Mode.Text,
167
+ quote: '',
168
+ tagName: '',
169
+ closing: false,
170
+ rawName: '',
171
+ preDepth: 0,
172
+ left: 'template',
173
+ };
174
+ return strings.map((segment, index) => minifySegment(segment, state, index === strings.length - 1));
175
+ }
176
+ const cache = new WeakMap();
177
+ /**
178
+ * The minified TemplateStringsArray for a call site. Cached by identity, so Lit's own template
179
+ * cache (keyed by the strings array) still hits once per call site.
180
+ */
181
+ export function minifyTemplate(strings) {
182
+ const cached = cache.get(strings);
183
+ if (cached !== undefined)
184
+ return cached;
185
+ const minified = minifyStrings(strings);
186
+ // Lit accepts only arrays with an own `raw` field (its anti-spoofing check), as lit/static-html
187
+ // builds them. Cooked strings are what Lit reads; `raw` mirrors them.
188
+ const result = Object.freeze(Object.assign(minified, { raw: Object.freeze([...minified]) }));
189
+ cache.set(strings, result);
190
+ return result;
191
+ }
192
+ //# sourceMappingURL=template-whitespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-whitespace.js","sourceRoot":"","sources":["../src/template-whitespace.ts"],"names":[],"mappings":"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","sourcesContent":["// Template whitespace minification (gyral-9rf, ADR 0016 addendum).\n//\n// lit-html keeps every newline and indent between tags as a DOM text node; compilers (Svelte,\n// Solid, JSX) drop them. In a benchmark table row that is 12 extra nodes out of 25, and removing\n// them made create/replace/clear 11-24% faster (gyral-1kq). Gyral's `html` and `svg` tags run the\n// template strings through `minifyTemplate()` once per call site (cached by strings identity),\n// so the server and the browser always build the same template: hydration digests match in any\n// toolchain (Vite, tsx, plain Node), with no build step.\n//\n// Rules (the same text everywhere, documented in docs/references/consumer-setup.md):\n// - Whitespace-only text that contains a newline is removed when it sits next to a template\n// edge, a block-level tag, or the inside edge of a <button>/<select> (CSS never renders it\n// there). Between two inline neighbours (phrasing elements, custom elements, bindings,\n// comments) it collapses to one space.\n// - Other runs of whitespace in text collapse to one space; a leading or trailing run with a\n// newline next to one of those edges is removed.\n// - <pre>, <textarea>, <script>, <style> and <title> contents, tags and attribute values,\n// and comments are copied unchanged. Binding positions never move: the result has the same\n// number of strings, so Lit parts and SSR hydration markers line up.\n// The function is idempotent: minifying minified strings changes nothing.\n\n/** Elements whose box is block-level (or never laid out inline) by default. */\nconst BLOCK = new Set(\n (\n 'address article aside blockquote body caption col colgroup datalist dd details dialog div ' +\n 'dl dt fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html ' +\n 'legend li main menu nav ol optgroup option p pre search section select summary table tbody ' +\n 'td tfoot th thead tr ul ' +\n // SVG containers and shapes: whitespace between them is never rendered.\n 'circle clippath defs ellipse g line lineargradient marker mask path pattern polygon ' +\n 'polyline radialgradient rect stop symbol use'\n ).split(' '),\n);\n\n/** Elements whose text content is copied verbatim (whitespace is meaningful or raw text). */\nconst RAW_TEXT = new Set(['script', 'style', 'textarea', 'title']);\nconst PRESERVE = 'pre';\n\n/** What sits on one side of a text chunk. */\ntype Edge = 'template' | 'inline' | 'block';\n\n/**\n * Inline-block controls: they sit inline among their neighbours, but whitespace at the start or\n * end of their own content is never rendered (it is at the edge of their own line box).\n */\nconst INLINE_BOX = new Set(['button', 'select']);\n\n/**\n * The edge a tag makes for the text next to it. `inside` is true when the text is inside the\n * element (after its start tag, or before its end tag).\n */\nfunction edgeOfTag(name: string, inside: boolean): Edge {\n const lower = name.toLowerCase();\n if (BLOCK.has(lower)) return 'block';\n return inside && INLINE_BOX.has(lower) ? 'block' : 'inline';\n}\n\nconst WS = /^[ \\t\\n\\r\\f]*$/;\nconst RUN = /[ \\t\\n\\r\\f]+/g;\n\nfunction minifyChunk(text: string, left: Edge, right: Edge): string {\n if (text === '') return text;\n if (WS.test(text)) {\n const removable =\n /[\\n\\r]/.test(text) &&\n (left === 'template' || right === 'template' || left === 'block' || right === 'block');\n return removable ? '' : ' ';\n }\n // Mixed text: a leading or trailing run with a newline next to a removable edge goes too.\n const dropStart = left !== 'inline' && /^[ \\t\\f]*[\\n\\r]/.test(text);\n const dropEnd = right !== 'inline' && /[\\n\\r][ \\t\\n\\r\\f]*$/.test(text);\n let body = text.replace(RUN, ' ');\n if (dropStart) body = body.replace(/^ /, '');\n if (dropEnd) body = body.replace(/ $/, '');\n return body;\n}\n\nconst Mode = { Text: 0, Tag: 1, Comment: 2, RawText: 3 } as const;\ntype Mode = (typeof Mode)[keyof typeof Mode];\n\ninterface Scan {\n mode: Mode;\n /** Quote char while inside an attribute value in a tag, else ''. */\n quote: string;\n /** Name of the tag being read (lower case), and whether it is a closing tag. */\n tagName: string;\n closing: boolean;\n /** Raw-text element we are inside (its contents are copied verbatim). */\n rawName: string;\n /** Depth of open <pre> elements: text is copied verbatim while > 0. */\n preDepth: number;\n /** Edge type of the boundary before the current text chunk. */\n left: Edge;\n}\n\nconst TAG_START = /^<(\\/?)([A-Za-z!?][^\\s/>]*)/;\n\n/** Minify one segment, carrying the HTML scan state across bindings. */\nfunction minifySegment(segment: string, state: Scan, last: boolean): string {\n let out = '';\n let chunk = '';\n const flushText = (right: Edge): void => {\n out += state.preDepth > 0 ? chunk : minifyChunk(chunk, state.left, right);\n chunk = '';\n };\n let i = 0;\n while (i < segment.length) {\n const ch = segment.charAt(i);\n if (state.mode === Mode.Comment) {\n if (segment.startsWith('-->', i)) {\n out += '-->';\n i += 3;\n state.mode = Mode.Text;\n state.left = 'inline';\n } else {\n out += ch;\n i += 1;\n }\n continue;\n }\n if (state.mode === Mode.RawText) {\n const close = `</${state.rawName}`;\n if (segment.slice(i, i + close.length).toLowerCase() === close) {\n state.mode = Mode.Text;\n state.left = 'inline';\n continue; // the closing tag is read in Text mode\n }\n out += ch;\n i += 1;\n continue;\n }\n if (state.mode === Mode.Tag) {\n out += ch;\n i += 1;\n if (state.quote !== '') {\n if (ch === state.quote) state.quote = '';\n } else if (ch === '\"' || ch === \"'\") {\n state.quote = ch;\n } else if (ch === '>') {\n endTag(state, out.endsWith('/>'));\n }\n continue;\n }\n // Text mode.\n if (ch === '<' && segment.startsWith('<!--', i)) {\n flushText('inline');\n state.mode = Mode.Comment;\n out += '<!--';\n i += 4;\n continue;\n }\n const tag = ch === '<' ? TAG_START.exec(segment.slice(i)) : null;\n if (tag !== null) {\n const name = (tag[2] ?? '').toLowerCase();\n const closing = tag[1] === '/';\n flushText(edgeOfTag(name, closing));\n state.closing = closing;\n state.tagName = name;\n state.mode = Mode.Tag;\n out += tag[0];\n i += tag[0].length;\n continue;\n }\n chunk += ch;\n i += 1;\n }\n if (state.mode === Mode.Text) {\n flushText(last ? 'template' : 'inline');\n state.left = 'inline'; // the next segment starts after a binding\n }\n return out;\n}\n\nfunction endTag(state: Scan, selfClosing: boolean): void {\n const name = state.tagName;\n state.mode = Mode.Text;\n state.left = edgeOfTag(name, !state.closing && !selfClosing);\n if (name === PRESERVE) {\n if (state.closing) state.preDepth = Math.max(0, state.preDepth - 1);\n else if (!selfClosing) state.preDepth += 1;\n }\n if (!state.closing && !selfClosing && RAW_TEXT.has(name)) {\n state.mode = Mode.RawText;\n state.rawName = name;\n }\n}\n\n/** Minified copy of template strings (same length, bindings unmoved). Idempotent. */\nexport function minifyStrings(strings: readonly string[]): string[] {\n const state: Scan = {\n mode: Mode.Text,\n quote: '',\n tagName: '',\n closing: false,\n rawName: '',\n preDepth: 0,\n left: 'template',\n };\n return strings.map((segment, index) =>\n minifySegment(segment, state, index === strings.length - 1),\n );\n}\n\nconst cache = new WeakMap<TemplateStringsArray, TemplateStringsArray>();\n\n/**\n * The minified TemplateStringsArray for a call site. Cached by identity, so Lit's own template\n * cache (keyed by the strings array) still hits once per call site.\n */\nexport function minifyTemplate(strings: TemplateStringsArray): TemplateStringsArray {\n const cached = cache.get(strings);\n if (cached !== undefined) return cached;\n const minified = minifyStrings(strings);\n // Lit accepts only arrays with an own `raw` field (its anti-spoofing check), as lit/static-html\n // builds them. Cooked strings are what Lit reads; `raw` mirrors them.\n const result = Object.freeze(\n Object.assign(minified, { raw: Object.freeze([...minified]) }),\n ) as unknown as TemplateStringsArray;\n cache.set(strings, result);\n return result;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { type TemplateResult } from 'lit';
2
+ /** Lit's `html`, with indentation whitespace between tags removed. */
3
+ export declare function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult<1>;
4
+ /** Lit's `svg`, with indentation whitespace between elements removed. */
5
+ export declare function svg(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult<2>;
@@ -0,0 +1,15 @@
1
+ // Gyral's `html` and `svg` template tags: Lit's, with template whitespace minified once per call
2
+ // site (template-whitespace.ts, gyral-9rf). Server and browser run the same code, so the
3
+ // templates (and their hydration digests) are identical in every toolchain. For exact
4
+ // whitespace outside <pre>/<textarea>, import `html` from 'lit' instead.
5
+ import { html as litHtml, svg as litSvg } from 'lit';
6
+ import { minifyTemplate } from './template-whitespace.js';
7
+ /** Lit's `html`, with indentation whitespace between tags removed. */
8
+ export function html(strings, ...values) {
9
+ return litHtml(minifyTemplate(strings), ...values);
10
+ }
11
+ /** Lit's `svg`, with indentation whitespace between elements removed. */
12
+ export function svg(strings, ...values) {
13
+ return litSvg(minifyTemplate(strings), ...values);
14
+ }
15
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,yFAAyF;AACzF,sFAAsF;AACtF,yEAAyE;AACzE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,GAAG,IAAI,MAAM,EAAuB,MAAM,KAAK,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,sEAAsE;AACtE,MAAM,UAAU,IAAI,CAAC,OAA6B,EAAE,GAAG,MAAiB;IACtE,OAAO,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;AACrD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,GAAG,CAAC,OAA6B,EAAE,GAAG,MAAiB;IACrE,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;AACpD,CAAC","sourcesContent":["// Gyral's `html` and `svg` template tags: Lit's, with template whitespace minified once per call\n// site (template-whitespace.ts, gyral-9rf). Server and browser run the same code, so the\n// templates (and their hydration digests) are identical in every toolchain. For exact\n// whitespace outside <pre>/<textarea>, import `html` from 'lit' instead.\nimport { html as litHtml, svg as litSvg, type TemplateResult } from 'lit';\nimport { minifyTemplate } from './template-whitespace.js';\n\n/** Lit's `html`, with indentation whitespace between tags removed. */\nexport function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult<1> {\n return litHtml(minifyTemplate(strings), ...values);\n}\n\n/** Lit's `svg`, with indentation whitespace between elements removed. */\nexport function svg(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult<2> {\n return litSvg(minifyTemplate(strings), ...values);\n}\n"]}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@gyral/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Gyral core: Model-View-Intent web components on Lit and the web platform. Inspired by Cycle.js.",
5
5
  "license": "MIT",
6
- "author": "The Zoop Troop, Inc.",
6
+ "author": "Mike Zupper",
7
7
  "homepage": "https://github.com/gyraljs/gyral/tree/main/packages/core#readme",
8
8
  "repository": {
9
9
  "type": "git",
@@ -39,8 +39,7 @@
39
39
  "CHANGELOG.md"
40
40
  ],
41
41
  "dependencies": {
42
- "@standard-schema/spec": "1.1.0",
43
- "effect": "3.22.2"
42
+ "@standard-schema/spec": "1.1.0"
44
43
  },
45
44
  "peerDependencies": {
46
45
  "lit": "^3.3.0"
@@ -1,7 +0,0 @@
1
- import { Effect, type Fiber } from 'effect';
2
- /**
3
- * The single place Gyral starts Effect programs (ADR 0006). There are no services to
4
- * provide yet, so the default runtime is enough; a ManagedRuntime with layers goes here
5
- * when drivers need shared resources.
6
- */
7
- export declare const fork: <A>(program: Effect.Effect<A>) => Fiber.RuntimeFiber<A>;
@@ -1,8 +0,0 @@
1
- import { Effect } from 'effect';
2
- /**
3
- * The single place Gyral starts Effect programs (ADR 0006). There are no services to
4
- * provide yet, so the default runtime is enough; a ManagedRuntime with layers goes here
5
- * when drivers need shared resources.
6
- */
7
- export const fork = (program) => Effect.runFork(program);
8
- //# sourceMappingURL=runtime.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/internal/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,OAAyB,EAAyB,EAAE,CAC1E,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC","sourcesContent":["import { Effect, type Fiber } from 'effect';\n\n/**\n * The single place Gyral starts Effect programs (ADR 0006). There are no services to\n * provide yet, so the default runtime is enough; a ManagedRuntime with layers goes here\n * when drivers need shared resources.\n */\nexport const fork = <A>(program: Effect.Effect<A>): Fiber.RuntimeFiber<A> =>\n Effect.runFork(program);\n"]}