@gyral/core 0.3.1 → 0.3.3

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,27 @@
1
1
  # @gyral/core
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - No changes to `@gyral/core`; it is released at 0.3.3 with the other packages, for the
8
+ `@gyral/mcp` fix.
9
+
10
+ ## 0.3.2
11
+
12
+ ### Patch Changes
13
+
14
+ - Export the helper types `Reducer`, `Variant`, `ParseResult` and `StandardSchemaV1` (type-only),
15
+ so reducers and parsers written outside a component spec can be typed without copying
16
+ Gyral's internal definitions.
17
+ - The development template locator no longer adds branches to your coverage reports: call
18
+ sites now go through a virtual helper module instead of an inline `.at?.() ?? html` fallback.
19
+ - The components build metadata (`.vite/gyral-components.json`) gains `chunkModules`, which maps
20
+ each emitted chunk to the root-relative source modules it contains, including workspace
21
+ modules Vite merged into a shared chunk. Offline caches and service workers can use it to
22
+ know which chunk serves which module.
23
+ - Public doc comments no longer point at repository paths or tracker IDs.
24
+
3
25
  ## 0.3.1
4
26
 
5
27
  ### Patch Changes
package/README.md CHANGED
@@ -48,7 +48,7 @@ Reads validate persisted values with Standard Schema; watches report invalid dat
48
48
  listening. Driver writes and native other-tab events produce changes. Clear respects the
49
49
  prefix namespace. Start initial watches in `subscriptions` so SSR state hydrates first;
50
50
  imports and factories do not access browser globals. This optional entry is absent from
51
- apps that do not import it. See [the storage guide](../../skills/gyral/references/browser-storage.md).
51
+ apps that do not import it. See [the storage guide](https://github.com/gyraljs/gyral/blob/main/skills/gyral/references/browser-storage.md).
52
52
 
53
53
  `@gyral/core/indexeddb` (0.3.1) exports `makeIndexedDBDriver(stores, { database, version,
54
54
  upgrade?, name?, factory?, encode? })`: typed Read/Put/Delete, bounded Query and atomic
@@ -59,10 +59,13 @@ an already committed write cannot be undone. This entry is also optional and SSR
59
59
 
60
60
  Guides and API reference: **[gyral.dev](https://gyral.dev)**. Source, issues and the
61
61
  consumer setup guide (packages, Vite preset, template compiler):
62
- [github.com/gyraljs/gyral](https://github.com/gyraljs/gyral). Upgrading from 0.2:
63
- [docs/references/migrating-0.2-to-0.3.md](https://github.com/gyraljs/gyral/blob/main/docs/references/migrating-0.2-to-0.3.md).
62
+ [github.com/gyraljs/gyral](https://github.com/gyraljs/gyral). Upgrading:
63
+ [from 0.3.0 to 0.3.1](https://github.com/gyraljs/gyral/blob/main/docs/references/migrating-0.3.0-to-0.3.1.md),
64
+ [from 0.2 to 0.3](https://github.com/gyraljs/gyral/blob/main/docs/references/migrating-0.2-to-0.3.md).
64
65
 
65
- > Status: pre-alpha. APIs change between 0.x releases.
66
+ > Status: 0.x, on npm. Breaking changes to the
67
+ > [public API](https://github.com/gyraljs/gyral/blob/main/docs/references/public-api-0.3.1.md)
68
+ > come only in a new minor version, with a migration guide.
66
69
 
67
70
  ## Diagnostic codes
68
71
 
@@ -3,7 +3,7 @@
3
3
  // imports a tag's module once the tag is in the page, and writes the manifest
4
4
  // `@gyral/ssr/static`'s `componentsFromManifest` reads. ../vite.ts loads it on the plugin's
5
5
  // first hook, as it loads the compiler.
6
- import { relative, resolve, sep } from 'node:path';
6
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
7
7
  import { staticallyReachable } from './component-entries.js';
8
8
  import { loaderSource } from './loader-source.js';
9
9
  import { discoverComponents } from './tag-map.js';
@@ -152,11 +152,18 @@ export function createComponents(options) {
152
152
  // that chunk (often the entry), so Vite's manifest has no key for it; the server finds
153
153
  // the chunk by file instead (`componentAssets`).
154
154
  const chunkOf = new Map();
155
+ const chunkModules = {};
155
156
  for (const chunk of Object.values(bundle)) {
156
157
  if (chunk.type !== 'chunk')
157
158
  continue;
158
159
  for (const id of chunk.moduleIds)
159
160
  chunkOf.set(id, chunk.fileName);
161
+ // Actual emitted ownership includes merged workspace modules which have no
162
+ // separate src/isDynamicEntry record in Vite's manifest. Keep paths portable.
163
+ chunkModules[chunk.fileName] = chunk.moduleIds
164
+ .filter((id) => !id.startsWith('\0') && isAbsolute(id))
165
+ .map((id) => relative(root, id).split(sep).join('/'))
166
+ .sort();
160
167
  }
161
168
  const chunks = {};
162
169
  for (const [tag, url] of tags) {
@@ -169,6 +176,7 @@ export function createComponents(options) {
169
176
  loaderImports: loader.imports,
170
177
  modules,
171
178
  chunks,
179
+ chunkModules,
172
180
  entryTags,
173
181
  };
174
182
  this.emitFile({
@@ -1 +1 @@
1
- {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/compiler/components.ts"],"names":[],"mappings":"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","sourcesContent":["// Automatic component loading (`gyralVitePreset({ components })`, ADR 0016 addendum \"Automatic\n// component loading\"): finds the app's components, adds a loader to the client build that\n// imports a tag's module once the tag is in the page, and writes the manifest\n// `@gyral/ssr/static`'s `componentsFromManifest` reads. ../vite.ts loads it on the plugin's\n// first hook, as it loads the compiler.\nimport { relative, resolve, sep } from 'node:path';\nimport type { Plugin } from 'vite';\nimport { staticallyReachable } from './component-entries.js';\nimport type { ComponentsOptions } from '../vite.js';\nimport { loaderSource } from './loader-source.js';\nimport { discoverComponents } from './tag-map.js';\n\n/** The loader module's id, and Vite's id for it (a virtual module). */\nconst COMPONENTS_ID = 'virtual:gyral/components';\nconst COMPONENTS_RESOLVED = `\\0${COMPONENTS_ID}`;\n/** The file the build writes next to `.vite/manifest.json` (read by `@gyral/ssr/static`). */\nconst COMPONENTS_MANIFEST = '.vite/gyral-components.json';\n\nconst SOURCE_FILE = /\\.[cm]?[jt]sx?$/;\n\n/**\n * Tag → root-relative URL (`/src/…`) of the module defining it: ./tag-map.ts's scan, then the\n * `modules` the app lists for components the scan can't see (defined through a wrapper).\n */\nfunction scanComponents(\n root: string,\n include: readonly string[],\n listed: Readonly<Record<string, string>> = {},\n): Map<string, string> {\n const tags = new Map<string, string>();\n for (const [tag, path] of Object.entries(listed)) {\n tags.set(tag, `/${relative(root, resolve(root, path)).split(sep).join('/')}`);\n }\n for (const { tag, file } of discoverComponents(root, include)) {\n const url = `/${relative(root, file).split(sep).join('/')}`;\n const known = tags.get(tag);\n if (known === undefined) tags.set(tag, url);\n else if (known !== url && listed[tag] === undefined) {\n console.warn(`gyral: <${tag}> is defined in ${known} and ${url}; loading ${known}`);\n }\n }\n return tags;\n}\n\ntype Input = string | readonly string[] | Readonly<Record<string, string>> | undefined;\n\n/** The app's own inputs (everything but the loader), as module specifiers. */\nfunction inputsOf(input: Input): string[] {\n if (input === undefined) return [];\n if (typeof input === 'string') return [input];\n if (Array.isArray(input)) return [...(input as readonly string[])];\n return Object.values(input as Readonly<Record<string, string>>);\n}\n\n/** The client build's input with the loader added. */\nfunction withLoader(input: Input): string[] | Record<string, string> {\n if (input === undefined) return { components: COMPONENTS_ID };\n if (typeof input === 'string') {\n const name = input.replace(/^.*\\//, '').replace(/\\.[^.]*$/, '');\n return withLoader({ [name]: input });\n }\n if (Array.isArray(input)) return [...(input as readonly string[]), COMPONENTS_ID];\n const entries = input as Readonly<Record<string, string>>;\n let name = 'components';\n while (name in entries) name += '-loader';\n return { ...entries, [name]: COMPONENTS_ID };\n}\n\n/** The API `gyralDevServer` reads from this plugin (the tags it found, fresh). */\ninterface ComponentsPluginApi {\n readonly tags: () => ReadonlyMap<string, string>;\n}\n\n/** Automatic component loading (`gyralVitePreset({ components })`). */\nexport function createComponents(options: ComponentsOptions): Plugin {\n const include = options.include ?? ['src'];\n let root = process.cwd();\n let tags = new Map<string, string>();\n /** The app's own build inputs, recorded before the loader is added. */\n let appInputs: string[] = [];\n /** Tags the app's entry defines itself (statically imported): not loaded by the loader. */\n let entryTags: string[] = [];\n let loaderFile = '';\n const scan = (): Map<string, string> => (tags = scanComponents(root, include, options.modules));\n const api: ComponentsPluginApi = { tags: scan };\n return {\n name: 'gyral:components',\n api,\n config(config, env) {\n root = resolve(config.root ?? process.cwd());\n if (env.command !== 'build' || env.isSsrBuild === true) return;\n type Build = {\n manifest?: boolean | string;\n rollupOptions?: { input?: Input };\n rolldownOptions?: { input?: Input };\n };\n const build = ((config as { build?: Build }).build ??= {});\n const key = build.rolldownOptions !== undefined ? 'rolldownOptions' : 'rollupOptions';\n const bundler = (build[key] ??= {});\n const input = bundler.input ?? config.input;\n appInputs = inputsOf(input);\n bundler.input = withLoader(input);\n build.manifest ??= true;\n },\n configResolved(config) {\n root = config.root;\n if (config.command !== 'build' || config.build.ssr !== false) return;\n const preload = config.build.modulePreload;\n if (preload === false) return;\n const previous = preload.resolveDependencies;\n preload.resolveDependencies = (file, deps, context) =>\n context.hostType === 'js' && context.hostId === loaderFile\n ? []\n : (previous?.(file, deps, context) ?? deps);\n },\n buildStart() {\n scan();\n },\n resolveId(source) {\n return source === COMPONENTS_ID ? COMPONENTS_RESOLVED : null;\n },\n async load(id) {\n if (id !== COMPONENTS_RESOLVED) return null;\n if (this.environment.config.command === 'serve') {\n scan();\n return loaderSource(tags);\n }\n // A production build: components the app's entry imports are already defined at startup,\n // and every other one becomes a dynamic import. The dependency filter removes repeated\n // JS preload lists (renderPage already preloads them); Vite still loads their CSS.\n const inEntry = await staticallyReachable(this, appInputs);\n entryTags = [];\n const lazy = new Map<string, string>();\n for (const [tag, url] of tags) {\n const file = resolve(root, url.slice(1));\n if (inEntry.has(file)) {\n entryTags.push(tag);\n continue;\n }\n lazy.set(tag, url);\n }\n return loaderSource(lazy, { known: entryTags });\n },\n configureServer(server) {\n // A new or edited component module changes the loader's table.\n const refresh = (file: string): void => {\n if (!SOURCE_FILE.test(file)) return;\n const module = server.environments.client.moduleGraph.getModuleById(COMPONENTS_RESOLVED);\n if (module !== undefined) server.environments.client.moduleGraph.invalidateModule(module);\n };\n server.watcher.on('add', refresh);\n server.watcher.on('unlink', refresh);\n server.watcher.on('change', refresh);\n },\n generateBundle(_output, bundle) {\n if (this.environment.config.consumer !== 'client') return;\n const loader = Object.values(bundle).find(\n (chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === COMPONENTS_RESOLVED,\n );\n if (loader?.type !== 'chunk') return;\n loaderFile = loader.fileName;\n const modules = Object.fromEntries([...tags].map(([tag, url]) => [tag, url.slice(1)]));\n // Each tag's chunk by file. A module another chunk imports statically is bundled into\n // that chunk (often the entry), so Vite's manifest has no key for it; the server finds\n // the chunk by file instead (`componentAssets`).\n const chunkOf = new Map<string, string>();\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk') continue;\n for (const id of chunk.moduleIds) chunkOf.set(id, chunk.fileName);\n }\n const chunks: Record<string, string> = {};\n for (const [tag, url] of tags) {\n const file = chunkOf.get(resolve(root, url.slice(1)));\n if (file !== undefined) chunks[tag] = file;\n }\n const written = {\n loader: loader.fileName,\n loaderImports: loader.imports,\n modules,\n chunks,\n entryTags,\n };\n this.emitFile({\n type: 'asset',\n fileName: COMPONENTS_MANIFEST,\n source: `${JSON.stringify(written, null, 2)}\\n`,\n });\n },\n };\n}\n"]}
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/compiler/components.ts"],"names":[],"mappings":"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","sourcesContent":["// Automatic component loading (`gyralVitePreset({ components })`, ADR 0016 addendum \"Automatic\n// component loading\"): finds the app's components, adds a loader to the client build that\n// imports a tag's module once the tag is in the page, and writes the manifest\n// `@gyral/ssr/static`'s `componentsFromManifest` reads. ../vite.ts loads it on the plugin's\n// first hook, as it loads the compiler.\nimport { isAbsolute, relative, resolve, sep } from 'node:path';\nimport type { Plugin } from 'vite';\nimport { staticallyReachable } from './component-entries.js';\nimport type { ComponentsOptions } from '../vite.js';\nimport { loaderSource } from './loader-source.js';\nimport { discoverComponents } from './tag-map.js';\n\n/** The loader module's id, and Vite's id for it (a virtual module). */\nconst COMPONENTS_ID = 'virtual:gyral/components';\nconst COMPONENTS_RESOLVED = `\\0${COMPONENTS_ID}`;\n/** The file the build writes next to `.vite/manifest.json` (read by `@gyral/ssr/static`). */\nconst COMPONENTS_MANIFEST = '.vite/gyral-components.json';\n\nconst SOURCE_FILE = /\\.[cm]?[jt]sx?$/;\n\n/**\n * Tag → root-relative URL (`/src/…`) of the module defining it: ./tag-map.ts's scan, then the\n * `modules` the app lists for components the scan can't see (defined through a wrapper).\n */\nfunction scanComponents(\n root: string,\n include: readonly string[],\n listed: Readonly<Record<string, string>> = {},\n): Map<string, string> {\n const tags = new Map<string, string>();\n for (const [tag, path] of Object.entries(listed)) {\n tags.set(tag, `/${relative(root, resolve(root, path)).split(sep).join('/')}`);\n }\n for (const { tag, file } of discoverComponents(root, include)) {\n const url = `/${relative(root, file).split(sep).join('/')}`;\n const known = tags.get(tag);\n if (known === undefined) tags.set(tag, url);\n else if (known !== url && listed[tag] === undefined) {\n console.warn(`gyral: <${tag}> is defined in ${known} and ${url}; loading ${known}`);\n }\n }\n return tags;\n}\n\ntype Input = string | readonly string[] | Readonly<Record<string, string>> | undefined;\n\n/** The app's own inputs (everything but the loader), as module specifiers. */\nfunction inputsOf(input: Input): string[] {\n if (input === undefined) return [];\n if (typeof input === 'string') return [input];\n if (Array.isArray(input)) return [...(input as readonly string[])];\n return Object.values(input as Readonly<Record<string, string>>);\n}\n\n/** The client build's input with the loader added. */\nfunction withLoader(input: Input): string[] | Record<string, string> {\n if (input === undefined) return { components: COMPONENTS_ID };\n if (typeof input === 'string') {\n const name = input.replace(/^.*\\//, '').replace(/\\.[^.]*$/, '');\n return withLoader({ [name]: input });\n }\n if (Array.isArray(input)) return [...(input as readonly string[]), COMPONENTS_ID];\n const entries = input as Readonly<Record<string, string>>;\n let name = 'components';\n while (name in entries) name += '-loader';\n return { ...entries, [name]: COMPONENTS_ID };\n}\n\n/** The API `gyralDevServer` reads from this plugin (the tags it found, fresh). */\ninterface ComponentsPluginApi {\n readonly tags: () => ReadonlyMap<string, string>;\n}\n\n/** Automatic component loading (`gyralVitePreset({ components })`). */\nexport function createComponents(options: ComponentsOptions): Plugin {\n const include = options.include ?? ['src'];\n let root = process.cwd();\n let tags = new Map<string, string>();\n /** The app's own build inputs, recorded before the loader is added. */\n let appInputs: string[] = [];\n /** Tags the app's entry defines itself (statically imported): not loaded by the loader. */\n let entryTags: string[] = [];\n let loaderFile = '';\n const scan = (): Map<string, string> => (tags = scanComponents(root, include, options.modules));\n const api: ComponentsPluginApi = { tags: scan };\n return {\n name: 'gyral:components',\n api,\n config(config, env) {\n root = resolve(config.root ?? process.cwd());\n if (env.command !== 'build' || env.isSsrBuild === true) return;\n type Build = {\n manifest?: boolean | string;\n rollupOptions?: { input?: Input };\n rolldownOptions?: { input?: Input };\n };\n const build = ((config as { build?: Build }).build ??= {});\n const key = build.rolldownOptions !== undefined ? 'rolldownOptions' : 'rollupOptions';\n const bundler = (build[key] ??= {});\n const input = bundler.input ?? config.input;\n appInputs = inputsOf(input);\n bundler.input = withLoader(input);\n build.manifest ??= true;\n },\n configResolved(config) {\n root = config.root;\n if (config.command !== 'build' || config.build.ssr !== false) return;\n const preload = config.build.modulePreload;\n if (preload === false) return;\n const previous = preload.resolveDependencies;\n preload.resolveDependencies = (file, deps, context) =>\n context.hostType === 'js' && context.hostId === loaderFile\n ? []\n : (previous?.(file, deps, context) ?? deps);\n },\n buildStart() {\n scan();\n },\n resolveId(source) {\n return source === COMPONENTS_ID ? COMPONENTS_RESOLVED : null;\n },\n async load(id) {\n if (id !== COMPONENTS_RESOLVED) return null;\n if (this.environment.config.command === 'serve') {\n scan();\n return loaderSource(tags);\n }\n // A production build: components the app's entry imports are already defined at startup,\n // and every other one becomes a dynamic import. The dependency filter removes repeated\n // JS preload lists (renderPage already preloads them); Vite still loads their CSS.\n const inEntry = await staticallyReachable(this, appInputs);\n entryTags = [];\n const lazy = new Map<string, string>();\n for (const [tag, url] of tags) {\n const file = resolve(root, url.slice(1));\n if (inEntry.has(file)) {\n entryTags.push(tag);\n continue;\n }\n lazy.set(tag, url);\n }\n return loaderSource(lazy, { known: entryTags });\n },\n configureServer(server) {\n // A new or edited component module changes the loader's table.\n const refresh = (file: string): void => {\n if (!SOURCE_FILE.test(file)) return;\n const module = server.environments.client.moduleGraph.getModuleById(COMPONENTS_RESOLVED);\n if (module !== undefined) server.environments.client.moduleGraph.invalidateModule(module);\n };\n server.watcher.on('add', refresh);\n server.watcher.on('unlink', refresh);\n server.watcher.on('change', refresh);\n },\n generateBundle(_output, bundle) {\n if (this.environment.config.consumer !== 'client') return;\n const loader = Object.values(bundle).find(\n (chunk) => chunk.type === 'chunk' && chunk.facadeModuleId === COMPONENTS_RESOLVED,\n );\n if (loader?.type !== 'chunk') return;\n loaderFile = loader.fileName;\n const modules = Object.fromEntries([...tags].map(([tag, url]) => [tag, url.slice(1)]));\n // Each tag's chunk by file. A module another chunk imports statically is bundled into\n // that chunk (often the entry), so Vite's manifest has no key for it; the server finds\n // the chunk by file instead (`componentAssets`).\n const chunkOf = new Map<string, string>();\n const chunkModules: Record<string, string[]> = {};\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk') continue;\n for (const id of chunk.moduleIds) chunkOf.set(id, chunk.fileName);\n // Actual emitted ownership includes merged workspace modules which have no\n // separate src/isDynamicEntry record in Vite's manifest. Keep paths portable.\n chunkModules[chunk.fileName] = chunk.moduleIds\n .filter((id) => !id.startsWith('\\0') && isAbsolute(id))\n .map((id) => relative(root, id).split(sep).join('/'))\n .sort();\n }\n const chunks: Record<string, string> = {};\n for (const [tag, url] of tags) {\n const file = chunkOf.get(resolve(root, url.slice(1)));\n if (file !== undefined) chunks[tag] = file;\n }\n const written = {\n loader: loader.fileName,\n loaderImports: loader.imports,\n modules,\n chunks,\n chunkModules,\n entryTags,\n };\n this.emitFile({\n type: 'asset',\n fileName: COMPONENTS_MANIFEST,\n source: `${JSON.stringify(written, null, 2)}\\n`,\n });\n },\n };\n}\n"]}
@@ -3,10 +3,11 @@
3
3
  // traces point into Vite's transformed modules, whose lines aren't the author's. So the preset's
4
4
  // `gyral:template-locations` plugin (serve only, enforce: 'pre', loaded lazily like the
5
5
  // compiler) rewrites each html`…` call site of the configured sources into
6
- // (html.at?.("src/app.ts:12:5") ?? html)`…`
7
- // (svg`…` call sites the same way, with svg.at) and the development runtime records that
6
+ // __gyralLocate(html, "src/app.ts:12:5")`…`
7
+ // The optional .at fallback lives in a virtual helper, so consumer coverage counts no
8
+ // generated branches. svg call sites work the same way, and the development runtime records that
8
9
  // position for the template (view/loc.ts). Nothing else changes and no line moves. Uses the
9
- // compiler can't follow (an aliased html) stay as they are: the runtime falls back to the stack
10
+ // compiler can't follow (a runtime-derived tag) stay as they are: the runtime falls back to the stack
10
11
  // trace there.
11
12
  import { relative } from 'node:path';
12
13
  import { fileURLToPath } from 'node:url';
@@ -61,6 +62,9 @@ export function createLocator(sources) {
61
62
  const lines = new Lines(code);
62
63
  const file = relative(root, id);
63
64
  const edits = [];
65
+ let helper = '__gyralLocate';
66
+ while (code.includes(helper))
67
+ helper += '_';
64
68
  for (const { node } of sites) {
65
69
  const tag = nodeAt(node, 'tag');
66
70
  if (tag === undefined)
@@ -68,9 +72,17 @@ export function createLocator(sources) {
68
72
  const { line, column } = lines.position(node.start);
69
73
  const loc = JSON.stringify(`${file}:${String(line)}:${String(column + 1)}`);
70
74
  const name = code.slice(tag.start, tag.end);
71
- edits.push({ start: tag.start, end: tag.start, text: '(' });
72
- edits.push({ start: tag.end, end: tag.end, text: `.at?.(${loc}) ?? ${name})` });
75
+ edits.push({
76
+ start: tag.start,
77
+ end: tag.end,
78
+ text: `${helper}(${name}, ${loc})`,
79
+ });
73
80
  }
81
+ edits.push({
82
+ start: code.length,
83
+ end: code.length,
84
+ text: `\nimport { locate as ${helper} } from 'virtual:gyral/template-locations';\n`,
85
+ });
74
86
  return splice(lines, edits, id);
75
87
  },
76
88
  };
@@ -1 +1 @@
1
- {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/compiler/locate.ts"],"names":[],"mappings":"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","sourcesContent":["// Development source locations for runtime templates (view/01-templates.md \"Source locations\",\n// gyral-g1r.24). `vite serve` (the dev server and Vitest) doesn't compile templates, and stack\n// traces point into Vite's transformed modules, whose lines aren't the author's. So the preset's\n// `gyral:template-locations` plugin (serve only, enforce: 'pre', loaded lazily like the\n// compiler) rewrites each html`…` call site of the configured sources into\n// (html.at?.(\"src/app.ts:12:5\") ?? html)`…`\n// (svg`…` call sites the same way, with svg.at) and the development runtime records that\n// position for the template (view/loc.ts). Nothing else changes and no line moves. Uses the\n// compiler can't follow (an aliased html) stay as they are: the runtime falls back to the stack\n// trace there.\nimport { relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Plugin } from 'vite';\nimport { langOf, nodeAt, type Node } from './ast.js';\nimport { findUses, htmlImports } from './scan.js';\nimport { Lines, splice, type Edit } from './source.js';\n\ntype Fn<H> = H extends { handler: infer F } ? F : H;\ntype HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;\n\nexport interface LocatorHooks {\n readonly configResolved: HookFn<'configResolved'>;\n readonly transform: HookFn<'transform'>;\n}\n\n/** Core's own view modules: always sources (core-internal templates, tests). */\nexport const VIEW = ['index', 'template'].flatMap((name) =>\n ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))),\n);\n\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\n/** `sources`: as the compiler's (module specifiers whose `html` and `svg` are the view layer's tags). */\nexport function createLocator(sources: readonly string[]): LocatorHooks {\n let root = '';\n /** Resolved ids of the sources, per environment (resolution may differ between them). */\n const resolved = new WeakMap<object, Promise<Set<string>>>();\n\n return {\n configResolved(config) {\n root = config.root;\n },\n\n async transform(code, id) {\n if (id.startsWith('\\0') || !SCRIPT.test(id) || id.includes('/node_modules/')) return null;\n let program: Node;\n try {\n program = this.parse(code, { lang: langOf(id) }) as unknown as Node;\n } catch {\n return null; // not ours to report\n }\n const { imports } = htmlImports(program);\n if (imports.length === 0) return null;\n let ids = resolved.get(this.environment);\n if (ids === undefined) {\n ids = Promise.all(sources.map((s) => this.resolve(s, id))).then(\n (rs) => new Set([...VIEW, ...rs.flatMap((r) => (r === null ? [] : [r.id]))]),\n );\n resolved.set(this.environment, ids);\n }\n const sourceIds = await ids;\n const matched = [];\n for (const binding of imports) {\n const r = sources.includes(binding.specifier)\n ? null\n : await this.resolve(binding.specifier, id);\n if (sources.includes(binding.specifier) || (r !== null && sourceIds.has(r.id))) {\n matched.push(binding);\n }\n }\n if (matched.length === 0) return null;\n const { sites } = findUses(program, matched);\n if (sites.length === 0) return null;\n const lines = new Lines(code);\n const file = relative(root, id);\n const edits: Edit[] = [];\n for (const { node } of sites) {\n const tag = nodeAt(node, 'tag');\n if (tag === undefined) continue;\n const { line, column } = lines.position(node.start);\n const loc = JSON.stringify(`${file}:${String(line)}:${String(column + 1)}`);\n const name = code.slice(tag.start, tag.end);\n edits.push({ start: tag.start, end: tag.start, text: '(' });\n edits.push({ start: tag.end, end: tag.end, text: `.at?.(${loc}) ?? ${name})` });\n }\n return splice(lines, edits, id);\n },\n };\n}\n"]}
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/compiler/locate.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,+FAA+F;AAC/F,iGAAiG;AACjG,wFAAwF;AACxF,2EAA2E;AAC3E,8CAA8C;AAC9C,sFAAsF;AACtF,iGAAiG;AACjG,4FAA4F;AAC5F,sGAAsG;AACtG,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAa,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAa,MAAM,aAAa,CAAC;AAUvD,gFAAgF;AAChF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACzD,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAC9F,CAAC;AAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC;AAEjC,yGAAyG;AACzG,MAAM,UAAU,aAAa,CAAC,OAA0B;IACtD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,yFAAyF;IACzF,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAgC,CAAC;IAE7D,OAAO;QACL,cAAc,CAAC,MAAM;YACnB,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE;YACtB,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1F,IAAI,OAAa,CAAC;YAClB,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAoB,CAAC;YACtE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,CAAC,qBAAqB;YACpC,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACtC,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAC7D,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAC7E,CAAC;gBACF,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YACtC,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC;YAC5B,MAAM,OAAO,GAAG,EAAE,CAAC;YACnB,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;oBAC3C,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC/E,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACtC,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM,KAAK,GAAW,EAAE,CAAC;YACzB,IAAI,MAAM,GAAG,eAAe,CAAC;YAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,GAAG,CAAC;YAC5C,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;gBAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAChC,IAAI,GAAG,KAAK,SAAS;oBAAE,SAAS;gBAChC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC5C,KAAK,CAAC,IAAI,CAAC;oBACT,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,IAAI,EAAE,GAAG,MAAM,IAAI,IAAI,KAAK,GAAG,GAAG;iBACnC,CAAC,CAAC;YACL,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACT,KAAK,EAAE,IAAI,CAAC,MAAM;gBAClB,GAAG,EAAE,IAAI,CAAC,MAAM;gBAChB,IAAI,EAAE,wBAAwB,MAAM,+CAA+C;aACpF,CAAC,CAAC;YACH,OAAO,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Development source locations for runtime templates (view/01-templates.md \"Source locations\",\n// gyral-g1r.24). `vite serve` (the dev server and Vitest) doesn't compile templates, and stack\n// traces point into Vite's transformed modules, whose lines aren't the author's. So the preset's\n// `gyral:template-locations` plugin (serve only, enforce: 'pre', loaded lazily like the\n// compiler) rewrites each html`…` call site of the configured sources into\n// __gyralLocate(html, \"src/app.ts:12:5\")`…`\n// The optional .at fallback lives in a virtual helper, so consumer coverage counts no\n// generated branches. svg call sites work the same way, and the development runtime records that\n// position for the template (view/loc.ts). Nothing else changes and no line moves. Uses the\n// compiler can't follow (a runtime-derived tag) stay as they are: the runtime falls back to the stack\n// trace there.\nimport { relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Plugin } from 'vite';\nimport { langOf, nodeAt, type Node } from './ast.js';\nimport { findUses, htmlImports } from './scan.js';\nimport { Lines, splice, type Edit } from './source.js';\n\ntype Fn<H> = H extends { handler: infer F } ? F : H;\ntype HookFn<K extends keyof Plugin> = Fn<NonNullable<Plugin[K]>>;\n\nexport interface LocatorHooks {\n readonly configResolved: HookFn<'configResolved'>;\n readonly transform: HookFn<'transform'>;\n}\n\n/** Core's own view modules: always sources (core-internal templates, tests). */\nexport const VIEW = ['index', 'template'].flatMap((name) =>\n ['.ts', '.js'].map((ext) => fileURLToPath(new URL(`../view/${name}${ext}`, import.meta.url))),\n);\n\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\n/** `sources`: as the compiler's (module specifiers whose `html` and `svg` are the view layer's tags). */\nexport function createLocator(sources: readonly string[]): LocatorHooks {\n let root = '';\n /** Resolved ids of the sources, per environment (resolution may differ between them). */\n const resolved = new WeakMap<object, Promise<Set<string>>>();\n\n return {\n configResolved(config) {\n root = config.root;\n },\n\n async transform(code, id) {\n if (id.startsWith('\\0') || !SCRIPT.test(id) || id.includes('/node_modules/')) return null;\n let program: Node;\n try {\n program = this.parse(code, { lang: langOf(id) }) as unknown as Node;\n } catch {\n return null; // not ours to report\n }\n const { imports } = htmlImports(program);\n if (imports.length === 0) return null;\n let ids = resolved.get(this.environment);\n if (ids === undefined) {\n ids = Promise.all(sources.map((s) => this.resolve(s, id))).then(\n (rs) => new Set([...VIEW, ...rs.flatMap((r) => (r === null ? [] : [r.id]))]),\n );\n resolved.set(this.environment, ids);\n }\n const sourceIds = await ids;\n const matched = [];\n for (const binding of imports) {\n const r = sources.includes(binding.specifier)\n ? null\n : await this.resolve(binding.specifier, id);\n if (sources.includes(binding.specifier) || (r !== null && sourceIds.has(r.id))) {\n matched.push(binding);\n }\n }\n if (matched.length === 0) return null;\n const { sites } = findUses(program, matched);\n if (sites.length === 0) return null;\n const lines = new Lines(code);\n const file = relative(root, id);\n const edits: Edit[] = [];\n let helper = '__gyralLocate';\n while (code.includes(helper)) helper += '_';\n for (const { node } of sites) {\n const tag = nodeAt(node, 'tag');\n if (tag === undefined) continue;\n const { line, column } = lines.position(node.start);\n const loc = JSON.stringify(`${file}:${String(line)}:${String(column + 1)}`);\n const name = code.slice(tag.start, tag.end);\n edits.push({\n start: tag.start,\n end: tag.end,\n text: `${helper}(${name}, ${loc})`,\n });\n }\n edits.push({\n start: code.length,\n end: code.length,\n text: `\\nimport { locate as ${helper} } from 'virtual:gyral/template-locations';\\n`,\n });\n return splice(lines, edits, id);\n },\n };\n}\n"]}
package/dist/define.d.ts CHANGED
@@ -11,8 +11,7 @@ export type Definer<S, M extends Tagged, P extends object, O extends Tagged> = <
11
11
  * define<State, Msg>()('x-counter', { init, intent: { Increment: … }, update, view })
12
12
  *
13
13
  * With no type arguments, `define()('x-badge', spec)` infers the state, messages, props and
14
- * intent names from the spec. See docs/design-docs/0001-mvi-parsed-intent.md,
15
- * 0006-effects-and-drivers.md, 0007-props.md, 0008-forms.md and view/05-element.md.
14
+ * intent names from the spec. Guide: https://gyral.dev/docs/components/
16
15
  */
17
16
  export declare function define(): <S, M extends Tagged = never, P extends object = object, N extends string = never>(tag: string, spec: ComponentSpec<S, M, P, N>) => GyralElementClass<S, M, P, never, N>;
18
17
  export declare function define<S, M extends Tagged = never, P extends object = object, O extends Tagged = never>(): Definer<S, M, P, O>;
@@ -1 +1 @@
1
- {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAU/C,SAAS,SAAS,CAAC,GAAW,EAAE,IAAe;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5C,kFAAkF;IAClF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAChE,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,mEAAmE,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAqCD,sEAAsE;AACtE,MAAM,UAAU,MAAM;IACpB,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,IAA4C;IAC9E,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,IAAI,OAAO,WAAW,KAAK,WAAW,EAAE,CAAC;QACvC,0FAA0F;QAC1F,mFAAmF;QACnF,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACtB,OAAO,MAAM,CAAC,MAAM,CAClB,SAAS,WAAW;YAClB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnC,CAAC,EACD,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CACvB,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,GAAG,IAAK,QAAwC,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,gEAAgE,CAAC,CAAC;QACxF,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,aAAa,CAAC,OAAO,CAAC,CAAC;IACvB,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// `define<S, M>()(tag, spec)` (docs/design-docs/view/05-element.md, ADR 0023): compiles a\n// Model-View-Intent spec into a custom element and registers it. Outside the browser it\n// records the spec in the server registry instead (view/06-server.md) and returns a\n// placeholder class.\nimport type { GyralElementClass } from './element-types.js';\nimport { elementClass } from './element.js';\nimport { markGyralHost } from './intent.js';\nimport { isLight } from './light-dom.js';\nimport { features } from './features.js';\nimport { recordSpec } from './server-specs.js';\nimport type { ComponentSpec, ShadowOption, Tagged } from './types.js';\nimport { DEV, message } from './view/index.js';\n\nexport type { GyralElement, GyralElementClass } from './element-types.js';\n\ninterface SpecShape {\n readonly props?: object;\n readonly shadow?: ShadowOption;\n readonly styles?: unknown;\n}\n\nfunction checkSpec(tag: string, spec: SpecShape): void {\n const names = Object.keys(spec.props ?? {});\n // The prop builders registered the prop feature (features.ts) if there are props.\n if (names.length > 0) features.props?.checkShadowed(tag, names);\n if (DEV && isLight(spec) && spec.styles !== undefined) {\n console.warn(`<${tag}> has shadow: false, so its styles are ignored; use document CSS.`);\n }\n}\n\n/** The second call of `define()`: takes the tag and the spec, infers the intent names. */\nexport type Definer<S, M extends Tagged, P extends object, O extends Tagged> = <\n N extends string = never,\n>(\n tag: string,\n spec: ComponentSpec<S, M, P, N>,\n) => GyralElementClass<S, M, P, O, N>;\n\n/**\n * Compiles a Model-View-Intent spec into a custom element and registers it under `tag`. Two\n * calls (ADR 0023): the first takes the types, the second the tag and spec, from whose\n * `intent` keys it infers the component's intent names.\n *\n * define<State, Msg>()('x-counter', { init, intent: { Increment: … }, update, view })\n *\n * With no type arguments, `define()('x-badge', spec)` infers the state, messages, props and\n * intent names from the spec. See docs/design-docs/0001-mvi-parsed-intent.md,\n * 0006-effects-and-drivers.md, 0007-props.md, 0008-forms.md and view/05-element.md.\n */\nexport function define(): <\n S,\n M extends Tagged = never,\n P extends object = object,\n N extends string = never,\n>(\n tag: string,\n spec: ComponentSpec<S, M, P, N>,\n) => GyralElementClass<S, M, P, never, N>;\nexport function define<\n S,\n // No messages: `define<State>()` for a pure view (intent and update optional).\n M extends Tagged = never,\n P extends object = object,\n O extends Tagged = never,\n>(): Definer<S, M, P, O>;\n// One function serves both call forms; the overloads type the result.\nexport function define(): (tag: string, spec: never) => unknown {\n return defineElement;\n}\n\nfunction defineElement(tag: string, spec: ComponentSpec<unknown, Tagged, object>): unknown {\n checkSpec(tag, spec);\n if (typeof HTMLElement === 'undefined') {\n // No DOM (Node, Workers): the server renderer renders the spec itself (Phase 4). Only the\n // spec is recorded here, so the server registry code never reaches client bundles.\n recordSpec(tag, spec);\n return Object.assign(\n function Placeholder(): never {\n throw new Error(message(1, tag));\n },\n { spec, tagName: tag },\n );\n }\n const existing = customElements.get(tag);\n if (existing !== undefined) {\n if (DEV && (existing as { readonly spec?: unknown }).spec !== spec) {\n console.warn(`<${tag}> is already defined with a different spec; keeping the first.`);\n }\n return existing;\n }\n const Element = elementClass(tag, spec);\n markGyralHost(Element);\n customElements.define(tag, Element);\n return Element;\n}\n"]}
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAU/C,SAAS,SAAS,CAAC,GAAW,EAAE,IAAe;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5C,kFAAkF;IAClF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAChE,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,mEAAmE,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAoCD,sEAAsE;AACtE,MAAM,UAAU,MAAM;IACpB,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,IAA4C;IAC9E,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,IAAI,OAAO,WAAW,KAAK,WAAW,EAAE,CAAC;QACvC,0FAA0F;QAC1F,mFAAmF;QACnF,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACtB,OAAO,MAAM,CAAC,MAAM,CAClB,SAAS,WAAW;YAClB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnC,CAAC,EACD,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CACvB,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,GAAG,IAAK,QAAwC,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,gEAAgE,CAAC,CAAC;QACxF,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,aAAa,CAAC,OAAO,CAAC,CAAC;IACvB,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// `define<S, M>()(tag, spec)` (docs/design-docs/view/05-element.md, ADR 0023): compiles a\n// Model-View-Intent spec into a custom element and registers it. Outside the browser it\n// records the spec in the server registry instead (view/06-server.md) and returns a\n// placeholder class.\nimport type { GyralElementClass } from './element-types.js';\nimport { elementClass } from './element.js';\nimport { markGyralHost } from './intent.js';\nimport { isLight } from './light-dom.js';\nimport { features } from './features.js';\nimport { recordSpec } from './server-specs.js';\nimport type { ComponentSpec, ShadowOption, Tagged } from './types.js';\nimport { DEV, message } from './view/index.js';\n\nexport type { GyralElement, GyralElementClass } from './element-types.js';\n\ninterface SpecShape {\n readonly props?: object;\n readonly shadow?: ShadowOption;\n readonly styles?: unknown;\n}\n\nfunction checkSpec(tag: string, spec: SpecShape): void {\n const names = Object.keys(spec.props ?? {});\n // The prop builders registered the prop feature (features.ts) if there are props.\n if (names.length > 0) features.props?.checkShadowed(tag, names);\n if (DEV && isLight(spec) && spec.styles !== undefined) {\n console.warn(`<${tag}> has shadow: false, so its styles are ignored; use document CSS.`);\n }\n}\n\n/** The second call of `define()`: takes the tag and the spec, infers the intent names. */\nexport type Definer<S, M extends Tagged, P extends object, O extends Tagged> = <\n N extends string = never,\n>(\n tag: string,\n spec: ComponentSpec<S, M, P, N>,\n) => GyralElementClass<S, M, P, O, N>;\n\n/**\n * Compiles a Model-View-Intent spec into a custom element and registers it under `tag`. Two\n * calls (ADR 0023): the first takes the types, the second the tag and spec, from whose\n * `intent` keys it infers the component's intent names.\n *\n * define<State, Msg>()('x-counter', { init, intent: { Increment: … }, update, view })\n *\n * With no type arguments, `define()('x-badge', spec)` infers the state, messages, props and\n * intent names from the spec. Guide: https://gyral.dev/docs/components/\n */\nexport function define(): <\n S,\n M extends Tagged = never,\n P extends object = object,\n N extends string = never,\n>(\n tag: string,\n spec: ComponentSpec<S, M, P, N>,\n) => GyralElementClass<S, M, P, never, N>;\nexport function define<\n S,\n // No messages: `define<State>()` for a pure view (intent and update optional).\n M extends Tagged = never,\n P extends object = object,\n O extends Tagged = never,\n>(): Definer<S, M, P, O>;\n// One function serves both call forms; the overloads type the result.\nexport function define(): (tag: string, spec: never) => unknown {\n return defineElement;\n}\n\nfunction defineElement(tag: string, spec: ComponentSpec<unknown, Tagged, object>): unknown {\n checkSpec(tag, spec);\n if (typeof HTMLElement === 'undefined') {\n // No DOM (Node, Workers): the server renderer renders the spec itself (Phase 4). Only the\n // spec is recorded here, so the server registry code never reaches client bundles.\n recordSpec(tag, spec);\n return Object.assign(\n function Placeholder(): never {\n throw new Error(message(1, tag));\n },\n { spec, tagName: tag },\n );\n }\n const existing = customElements.get(tag);\n if (existing !== undefined) {\n if (DEV && (existing as { readonly spec?: unknown }).spec !== spec) {\n console.warn(`<${tag}> is already defined with a different spec; keeping the first.`);\n }\n return existing;\n }\n const Element = elementClass(tag, spec);\n markGyralHost(Element);\n customElements.define(tag, Element);\n return Element;\n}\n"]}
@@ -2,8 +2,7 @@ import type { DriverOverrides } from './command.js';
2
2
  import type { StoreOverrides } from './store.js';
3
3
  import type { ComponentSpec, Tagged } from './types.js';
4
4
  /**
5
- * The custom element `define()` produces: a plain `HTMLElement` (view/05-element.md "Public
6
- * instance API"). Tests wait for rendering with `settled()`.
5
+ * The custom element `define()` produces: a plain `HTMLElement`. Tests wait for rendering with `settled()`.
7
6
  */
8
7
  export interface GyralElement<S, M extends Tagged> extends HTMLElement {
9
8
  /** Current model state. */
@@ -1 +1 @@
1
- {"version":3,"file":"element-types.js","sourceRoot":"","sources":["../src/element-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DriverOverrides } from './command.js';\nimport type { StoreOverrides } from './store.js';\nimport type { ComponentSpec, Tagged } from './types.js';\n\n/**\n * The custom element `define()` produces: a plain `HTMLElement` (view/05-element.md \"Public\n * instance API\"). Tests wait for rendering with `settled()`.\n */\nexport interface GyralElement<S, M extends Tagged> extends HTMLElement {\n /** Current model state. */\n readonly state: S;\n /** Feeds a message through `update`, as if an intent had produced it. */\n send(msg: M): void;\n /** Per-instance driver substitutions by name (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /**\n * Per-instance store instances by store name (tests, islands). Checked before the nearest\n * `<gyral-stores>` provider and the document default (ADR 0013).\n */\n stores: StoreOverrides;\n /**\n * Messages applied through `update` right after `init`, before the first render. The server\n * uses it to render a rejected form with the same reducer as the JS path (ADR 0008). Ignored\n * when the element resumes from a hydration seed (the seed already contains their effect).\n */\n initialMessages: readonly Tagged[];\n}\n\nexport interface GyralElementClass<\n S,\n M extends Tagged,\n P,\n O extends Tagged = never,\n N extends string = string,\n> {\n /** Instances expose their declared props as settable properties. */\n new (): GyralElement<S, M> & { -readonly [K in keyof P]: P[K] };\n readonly spec: ComponentSpec<S, M, P, N>;\n readonly tagName: string;\n /** Native custom-element registration flag, true when the spec opts into forms. */\n readonly formAssociated?: boolean;\n /** Type-only: the outputs this component emits (read by `child()`). */\n readonly outputs?: O;\n /** Type-only: the component's intent names, the keys of `intent` (read by `intentsOf()`). */\n readonly intentNames?: N;\n}\n"]}
1
+ {"version":3,"file":"element-types.js","sourceRoot":"","sources":["../src/element-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DriverOverrides } from './command.js';\nimport type { StoreOverrides } from './store.js';\nimport type { ComponentSpec, Tagged } from './types.js';\n\n/**\n * The custom element `define()` produces: a plain `HTMLElement`. Tests wait for rendering with `settled()`.\n */\nexport interface GyralElement<S, M extends Tagged> extends HTMLElement {\n /** Current model state. */\n readonly state: S;\n /** Feeds a message through `update`, as if an intent had produced it. */\n send(msg: M): void;\n /** Per-instance driver substitutions by name (test fakes). Checked before the spec's. */\n drivers: DriverOverrides;\n /**\n * Per-instance store instances by store name (tests, islands). Checked before the nearest\n * `<gyral-stores>` provider and the document default (ADR 0013).\n */\n stores: StoreOverrides;\n /**\n * Messages applied through `update` right after `init`, before the first render. The server\n * uses it to render a rejected form with the same reducer as the JS path (ADR 0008). Ignored\n * when the element resumes from a hydration seed (the seed already contains their effect).\n */\n initialMessages: readonly Tagged[];\n}\n\nexport interface GyralElementClass<\n S,\n M extends Tagged,\n P,\n O extends Tagged = never,\n N extends string = string,\n> {\n /** Instances expose their declared props as settable properties. */\n new (): GyralElement<S, M> & { -readonly [K in keyof P]: P[K] };\n readonly spec: ComponentSpec<S, M, P, N>;\n readonly tagName: string;\n /** Native custom-element registration flag, true when the spec opts into forms. */\n readonly formAssociated?: boolean;\n /** Type-only: the outputs this component emits (read by `child()`). */\n readonly outputs?: O;\n /** Type-only: the component's intent names, the keys of `intent` (read by `intentsOf()`). */\n readonly intentNames?: N;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -25,7 +25,8 @@ export type { FormAssociation, FormControlValue, FormControlValidity, FormDisabl
25
25
  export { prop } from './prop.js';
26
26
  export type { Prop, PropGuard, PropKind, PropsOf } from './prop.js';
27
27
  export type { Definer, GyralElement, GyralElementClass } from './define.js';
28
- export type { ComponentSpec, Ctx, FieldIssue, FormFields, IntentInput, IntentNames, IntentParser, IntentRejected, Intents, ParserCtx, ParserFor, PropDeclarations, Errored, Hydrated, PropsChanged, ShadowOption, Stateless, StoreReader, Tagged, Update, } from './types.js';
28
+ export type { ComponentSpec, Ctx, FieldIssue, FormFields, IntentInput, IntentNames, IntentParser, IntentRejected, Intents, ParseResult, ParserCtx, ParserFor, PropDeclarations, Errored, Hydrated, PropsChanged, ShadowOption, Stateless, StoreReader, Reducer, Tagged, Update, Variant, } from './types.js';
29
+ export type { StandardSchemaV1 } from '@standard-schema/spec';
29
30
  export { css, defineHook, each, html, keyed, nothing, raw, svg } from './view/index.js';
30
31
  export type { ChildValue, CssValue, HookAttributes, HookResult, HookSpec, ListResult, RawResult, StyleSource, Styles, TemplateResult, } from './view/index.js';
31
32
  export type { Head, HeadLink, HeadMeta, JsonValue } from './head.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;AAarD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAkB,MAAM,WAAW,CAAC;AAKjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAcxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASlE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAS7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA0BjC,yFAAyF;AACzF,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,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 DriverToken,\n Next,\n RetryPolicy,\n} from './command.js';\nexport { retry } from './retry.js';\nexport { subscription } from './subscription.js';\nexport type { SubscriptionContext, SubscriptionOptions, Unsubscribe } from './subscription.js';\nexport { child, outputs } from './children.js';\nexport { focus } from './focus.js';\nexport { call, type CallInput } from './call.js';\nexport type { FocusOptions } from './focus.js';\nexport type { CommandInfo } from './invokers.js';\nexport type { HydrateStrategy } from './islands.js';\nexport type { ChildSource, OutputEvent, OutputSource, OutputsOf } from './children.js';\nexport { define } from './define.js';\nexport { intentsOf, OUTPUT_EVENT } from './intent.js';\nexport { settled } from './settled.js';\nexport { componentsReady } from './components-ready.js';\nexport { GyralError, type ErrorPhase } from './errors.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 SeedCheck,\n StoreResolver,\n StoreSpec,\n StoreUpdate,\n} from './store.js';\nexport { provideDrivers } from './drivers-scope.js';\nexport { defineForm, field, fieldErrors, form } from './forms.js';\nexport type {\n FieldName,\n FormDefinition,\n FormOptions,\n FormResult,\n FormValue,\n FormValues,\n} from './forms.js';\nexport { invalid } from './hooks/invalid.js';\nexport type {\n FormAssociation,\n FormControlValue,\n FormControlValidity,\n FormDisabled,\n FormReset,\n FormStateRestore,\n} from './form-associated.js';\nexport { prop } from './prop.js';\nexport type { Prop, PropGuard, PropKind, PropsOf } from './prop.js';\nexport type { Definer, 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 ParserCtx,\n ParserFor,\n PropDeclarations,\n Errored,\n Hydrated,\n PropsChanged,\n ShadowOption,\n Stateless,\n StoreReader,\n Tagged,\n Update,\n} from './types.js';\n\n// The view layer (ADR 0018, docs/design-docs/view/): templates, lists, hooks and styles.\nexport { css, defineHook, each, html, keyed, nothing, raw, svg } from './view/index.js';\nexport type {\n ChildValue,\n CssValue,\n HookAttributes,\n HookResult,\n HookSpec,\n ListResult,\n RawResult,\n StyleSource,\n Styles,\n TemplateResult,\n} from './view/index.js';\n\n// The head model (ADR 0019): shared by @gyral/ssr's renderPage() and @gyral/router's setHead().\nexport type { Head, HeadLink, HeadMeta, JsonValue } from './head.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;AAarD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAkB,MAAM,WAAW,CAAC;AAKjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAcxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASlE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAS7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgCjC,yFAAyF;AACzF,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,iBAAiB,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 DriverToken,\n Next,\n RetryPolicy,\n} from './command.js';\nexport { retry } from './retry.js';\nexport { subscription } from './subscription.js';\nexport type { SubscriptionContext, SubscriptionOptions, Unsubscribe } from './subscription.js';\nexport { child, outputs } from './children.js';\nexport { focus } from './focus.js';\nexport { call, type CallInput } from './call.js';\nexport type { FocusOptions } from './focus.js';\nexport type { CommandInfo } from './invokers.js';\nexport type { HydrateStrategy } from './islands.js';\nexport type { ChildSource, OutputEvent, OutputSource, OutputsOf } from './children.js';\nexport { define } from './define.js';\nexport { intentsOf, OUTPUT_EVENT } from './intent.js';\nexport { settled } from './settled.js';\nexport { componentsReady } from './components-ready.js';\nexport { GyralError, type ErrorPhase } from './errors.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 SeedCheck,\n StoreResolver,\n StoreSpec,\n StoreUpdate,\n} from './store.js';\nexport { provideDrivers } from './drivers-scope.js';\nexport { defineForm, field, fieldErrors, form } from './forms.js';\nexport type {\n FieldName,\n FormDefinition,\n FormOptions,\n FormResult,\n FormValue,\n FormValues,\n} from './forms.js';\nexport { invalid } from './hooks/invalid.js';\nexport type {\n FormAssociation,\n FormControlValue,\n FormControlValidity,\n FormDisabled,\n FormReset,\n FormStateRestore,\n} from './form-associated.js';\nexport { prop } from './prop.js';\nexport type { Prop, PropGuard, PropKind, PropsOf } from './prop.js';\nexport type { Definer, 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 ParseResult,\n ParserCtx,\n ParserFor,\n PropDeclarations,\n Errored,\n Hydrated,\n PropsChanged,\n ShadowOption,\n Stateless,\n StoreReader,\n Reducer,\n Tagged,\n Update,\n Variant,\n} from './types.js';\n// Standard Schema (https://standardschema.dev), the schema interface `form()`, `field()`,\n// `defineForm()`, `prop.*` and store seeds accept.\nexport type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// The view layer (ADR 0018, docs/design-docs/view/): templates, lists, hooks and styles.\nexport { css, defineHook, each, html, keyed, nothing, raw, svg } from './view/index.js';\nexport type {\n ChildValue,\n CssValue,\n HookAttributes,\n HookResult,\n HookSpec,\n ListResult,\n RawResult,\n StyleSource,\n Styles,\n TemplateResult,\n} from './view/index.js';\n\n// The head model (ADR 0019): shared by @gyral/ssr's renderPage() and @gyral/router's setHead().\nexport type { Head, HeadLink, HeadMeta, JsonValue } from './head.js';\n"]}
package/dist/intent.d.ts CHANGED
@@ -41,7 +41,7 @@ export declare function readIntent(event: Event, root: Node, after?: Element): I
41
41
  export declare const intentNames: unknown;
42
42
  /**
43
43
  * A component's intent names as a module-level constant, so pure list rows can name intents
44
- * without passing them through `pick` (view/03-lists.md "Rows must be pure"). The same object
44
+ * without passing them through `pick` (rows must be pure). The same object
45
45
  * the view gets as `i`, typed by the component's class (ADR 0023). A row that names intents
46
46
  * this way needs an explicit return type, or the row, the view and the class infer each other
47
47
  * in a circle:
package/dist/intent.js CHANGED
@@ -179,7 +179,7 @@ export function readIntent(event, root, after) {
179
179
  export const intentNames = new Proxy({}, { get: (_target, key) => (typeof key === 'string' ? key : undefined) });
180
180
  /**
181
181
  * A component's intent names as a module-level constant, so pure list rows can name intents
182
- * without passing them through `pick` (view/03-lists.md "Rows must be pure"). The same object
182
+ * without passing them through `pick` (rows must be pure). The same object
183
183
  * the view gets as `i`, typed by the component's class (ADR 0023). A row that names intents
184
184
  * this way needs an explicit return type, or the row, the view and the class infer each other
185
185
  * in a circle:
@@ -1 +1 @@
1
- {"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,OAAO,EAAe,MAAM,iBAAiB,CAAC;AAEvD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,OAAO;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,YAAY;CACb,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,GAAG,cAAc;IACjB,SAAS;IACT,OAAO;IACP,SAAS;IACT,UAAU;IACV,QAAQ;IACR,SAAS;CACV,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG,mDAAmD,CAAC;AACxE,wEAAwE;AACxE,MAAM,SAAS,GAAG,CAAC,KAAa,EAAY,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AACzE,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAA6B,CAAC;AAElE;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,IAAI,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,2FAA2F;QAC3F,sFAAsF;QACtF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAW,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/E,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CACtE,CAAC,CAAC,EAAE,KAAK,EAAE,AAAD,EAAG,KAAK,CAAC,EAAE,EAAE,CACrB,KAAK,KAAK,IAAI;YACZ,CAAC,CAAE,KAAgB;YACnB,CAAC,CAAC,KAAK,KAAK,GAAG;gBACb,CAAC,CAAC,aAAa;gBACf,CAAC,CAAC,SAAS,CAAC,KAAe,CAAC,CACnC,CAAC;QACF,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,EAAW,EAA0B,EAAE,CACvD,EAAE,YAAY,gBAAgB,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AAEpF,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAE1D;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAW;IACxC,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC;IAC1B,OAAO,IAAI,KAAK,MAAM;QACpB,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,QAAQ,CAAC,EAAE,CAAC;YACjC,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,IAAI,KAAK,UAAU;gBACjB,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAE,EAAuB,CAAC,IAAI,CAAC,CAAC;gBAC9E,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;oBAClB,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,OAAO,CAAC;AACpB,CAAC;AAED,6FAA6F;AAC7F,SAAS,UAAU,CAAC,EAAW;IAC7B,OAAO,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,0FAA0F;AAC1F,MAAM,WAAW,GAAG,IAAI,OAAO,EAAU,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAU,EAAW,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAW;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,IAAI,GAAgB,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,GAAG,CAAC;QAC3D,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;QAClC,IAAI,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,OAAO,CAAC,IAAa,EAAE,IAAU;IACxC,MAAM,IAAI,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACpE,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9C,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC;QAC3D,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5B,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CAAC,EAAW;IAC1B,IACE,EAAE,YAAY,gBAAgB;QAC9B,EAAE,YAAY,iBAAiB;QAC/B,EAAE,YAAY,mBAAmB;QACjC,EAAE,YAAY,iBAAiB,EAC/B,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,CAAC;IAClB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAY,EAAE,IAAU,EAAE,KAAe;IAClE,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;IAClC,6DAA6D;IAC7D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACtC,IAAI,CAAC,CAAC,MAAM,YAAY,OAAO,CAAC;YAAE,SAAS;QAC3C,uFAAuF;QACvF,yFAAyF;QACzF,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QAChE,MAAM,IAAI,GACR,MAAM,CAAC,YAAY,CAAC,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC;YAChD,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxF,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;YAAE,SAAS;QACtD,IAAI,QAA8B,CAAC;QACnC,IAAI,MAAM,YAAY,eAAe,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACtE,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QACD,OAAO;YACL,IAAI;YACJ,KAAK;YACL,MAAM;YACN,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;YACtB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;YACtD,QAAQ;YACR,MAAM,EAAE,KAAK,YAAY,WAAW,CAAC,CAAC,CAAE,KAAK,CAAC,MAAkB,CAAC,CAAC,CAAC,SAAS;YAC5E,uEAAuE;YACvE,GAAG,EAAG,KAAmC,CAAC,GAAG;YAC7C,2FAA2F;YAC3F,QAAQ,EAAG,KAAmD,CAAC,QAAQ;YACvE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC;SAC1B,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,kFAAkF;AAClF,yFAAyF;AACzF,MAAM,CAAC,MAAM,WAAW,GAAY,IAAI,KAAK,CAC3C,EAAE,EACF,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CACvE,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS;IAGvB,OAAO,WAAyD,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAU,EACV,SAAsB,EACtB,KAAwB,EACxB,OAA+B;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,iBAAiB,EAAE;YAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAY,EACZ,IAAU,EACV,OAAqE,EACrE,GAAW,EACX,KAGC,EACD,OAA0C;IAE1C,KAAK,IAAI,KAA8B,EAAE,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC;QAC3F,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QACD,4EAA4E;QAC5E,MAAM,MAAM,GAAG,CAAC,KAAc,EAAQ,EAAE;YACtC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC,CAAC;QACF,IAAI,MAAwD,CAAC;QAC7D,IAAI,CAAC;YACH,sFAAsF;YACtF,MAAM;gBACJ,MAAM,KAAK,IAAI;oBACb,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE;oBAChB,CAAC,CAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAsD,CAAC;QACzF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QACD,0FAA0F;QAC1F,0EAA0E;QAC1E,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,KAAK,SAAS;YAAE,KAAK,CAAC,eAAe,EAAE,CAAC;QACjF,IAAI,MAAM,YAAY,OAAO;YAAE,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { loadInvokerShim } from '#invoker-fallback';\nimport { commandOf, invokersSupported } from './invokers.js';\nimport { isLightComponent } from './light-dom.js';\nimport type { IntentInput, IntentNames, IntentParser, ParserCtx, Tagged } from './types.js';\nimport { message, type Markup } from './view/index.js';\n\n/** Event a child component dispatches on its host to send an output up (ADR 0010); public. */\nexport const OUTPUT_EVENT = 'gyral-output';\n\n/** Default triggers (`defaultTrigger`): every component root listens for these. */\nexport const DEFAULT_EVENTS: readonly string[] = [\n 'click',\n 'submit',\n 'input',\n 'change',\n OUTPUT_EVENT,\n];\n\n/**\n * The events `data-intent-on` usually names. A component root listens (capture phase, so\n * non-bubbling events such as `toggle` are seen too) for the defaults and for the events its\n * templates name in `data-intent-<event>` attributes and static `data-intent-on` values; a\n * template that binds `data-intent-on` dynamically adds all of these. Any other event with a\n * bound intent takes a per-event attribute, `data-intent-pointerdown=${i.Press}`, whose name the\n * template carries (view/05-element.md \"Intent events\").\n */\nexport const INTENT_EVENTS: readonly string[] = [\n ...DEFAULT_EVENTS,\n 'keydown',\n 'keyup',\n 'focusin',\n 'focusout',\n 'toggle',\n 'command',\n];\n\n/**\n * A static `data-intent-<event>` attribute: a per-event intent (the event is in the name), or\n * `data-intent-on` (its value is the event, or a quoted list of them separated by spaces).\n */\nconst INTENT_ATTR = /\\sdata-intent-([\\w-]+)=([\"']?)(.*?)\\2(?=[\\s/>])/gi;\n// Names separated by any whitespace; none empty, so no listener for \"\".\nconst eventList = (value: string): string[] => value.match(/\\S+/g) ?? [];\nconst eventsByTemplate = new WeakMap<Markup, readonly string[]>();\n\n/**\n * The intent events a template's (or `raw()` markup's) `data-intent-<event>` and\n * `data-intent-on` attributes name. A bound attribute's name is in its part; a bound\n * `data-intent-on` adds every intent event.\n */\nexport function eventsOf(markup: Markup): readonly string[] {\n let events = eventsByTemplate.get(markup);\n if (events === undefined) {\n // Bound attributes are in the parts, by name: listed as ` name=* ` and matched like markup\n // (other parts' names are never intent attributes; `*` marks a bound data-intent-on).\n const bound = markup.parts?.map((p) => ` ${p[2] as string}=* `).join('') ?? '';\n events = Array.from((markup.html + bound).matchAll(INTENT_ATTR)).flatMap(\n ([, event, , value]) =>\n event !== 'on'\n ? (event as string)\n : value === '*'\n ? INTENT_EVENTS\n : eventList(value as string),\n );\n eventsByTemplate.set(markup, events);\n }\n return events;\n}\n\nconst isToggle = (el: Element): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\nconst CLICK_INPUT_TYPES = /^(button|submit|reset|image)$/;\n\n/**\n * The event that fires an intent unless `data-intent-on` overrides it. Custom elements\n * (autonomous: the name has a dash) talk to their parent via outputs.\n */\nexport function defaultTrigger(el: Element): string {\n const name = el.localName;\n return name === 'form'\n ? 'submit'\n : name === 'select' || isToggle(el)\n ? 'change'\n : name === 'textarea' ||\n (name === 'input' && !CLICK_INPUT_TYPES.test((el as HTMLInputElement).type))\n ? 'input'\n : name.includes('-')\n ? OUTPUT_EVENT\n : 'click';\n}\n\n/** The events that fire `el`'s intent: its `data-intent-on` list, or its default trigger. */\nfunction triggersOf(el: Element): string[] {\n return eventList(el.getAttribute('data-intent-on') ?? defaultTrigger(el));\n}\n\n/** Every class `define()` creates, so intent lookup can tell where a component begins. */\nconst hostClasses = new WeakSet<object>();\n\nexport function markGyralHost(ctor: object): void {\n hostClasses.add(ctor);\n}\n\nconst isGyralHost = (node: Node): boolean => hostClasses.has(node.constructor);\n\n/**\n * The number of Gyral host ancestors of `el` in the composed tree (across shadow roots): the\n * scheduler renders smaller depths first (view/04-scheduler.md \"Marking\").\n */\nexport function hostDepth(el: Element): number {\n let depth = 0;\n for (let node: Node | null = el.parentNode; node !== null;) {\n if (isGyralHost(node)) depth += 1;\n node = node instanceof ShadowRoot ? node.host : node.parentNode;\n }\n return depth;\n}\n\n/**\n * Does `node` belong to the component whose intent root is `root`? `root` is the component's\n * shadow root, or the host itself for light-DOM components (ADR 0014). The node must be in the\n * same tree (not inside a nested shadow root) with no light-DOM Gyral host between them: a\n * light-DOM component owns its children (it rendered them). Children of a shadow-DOM host are\n * the outer template's content, slotted into it, so they stay the outer component's (a form or\n * a child component slotted into a card). The nested host element itself (with the parent's\n * `data-intent` on it) still belongs to the parent.\n */\nfunction ownedBy(node: Element, root: Node): boolean {\n const tree = root instanceof ShadowRoot ? root : root.getRootNode();\n if (node.getRootNode() !== tree) return false;\n for (let p = node.parentNode; p !== null; p = p.parentNode) {\n if (p === root) return true;\n if (isGyralHost(p) && isLightComponent(p.constructor)) return false;\n }\n return false;\n}\n\nfunction valueOf(el: Element): string | undefined {\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLSelectElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLButtonElement\n ) {\n return el.value;\n }\n return undefined;\n}\n\n/**\n * Reads an event into an IntentInput for the nearest element on its path that names an intent\n * for it and belongs to `root`: its `data-intent-<type>`, else its `data-intent` when the\n * event is one of its triggers. With `after`, the search starts past that element (the intent\n * whose parser declined). Elements inside nested components are ignored: that is component\n * isolation. Form submissions are prevented and turned into FormData.\n */\nexport function readIntent(event: Event, root: Node, after?: Element): IntentInput | undefined {\n const path = event.composedPath();\n // indexOf(undefined) is -1: without `after`, the whole path.\n for (const target of path.slice(path.indexOf(after as Element) + 1)) {\n if (target === root) return undefined;\n if (!(target instanceof Element)) continue;\n // An output is the dispatching child's own: only the element it was dispatched on (the\n // child host, where the direct parent's `data-intent` sits) takes it, never an ancestor.\n if (event.type === OUTPUT_EVENT && target !== path[0]) continue;\n const name =\n target.getAttribute('data-intent-' + event.type) ??\n (triggersOf(target).includes(event.type) ? target.getAttribute('data-intent') : null);\n if (name === null || !ownedBy(target, root)) continue;\n let formData: FormData | undefined;\n if (target instanceof HTMLFormElement && event instanceof SubmitEvent) {\n event.preventDefault();\n formData = new FormData(target, event.submitter);\n }\n return {\n name,\n event,\n target,\n value: valueOf(target),\n checked: isToggle(target) ? target.checked : undefined,\n formData,\n detail: event instanceof CustomEvent ? (event.detail as unknown) : undefined,\n // Read structurally too: only keyboard events carry `key` to elements.\n key: (event as { readonly key?: string }).key,\n // ToggleEvent (popover, <details>) is not Baseline widely available: read it structurally.\n newState: (event as { readonly newState?: 'open' | 'closed' }).newState,\n command: commandOf(event),\n };\n }\n return undefined;\n}\n\n// Any property read returns its own name, so `intents.Increment === 'Increment'`.\n// Types restrict reads to real message tags; a tag without a parser warns at event time.\nexport const intentNames: unknown = new Proxy(\n {},\n { get: (_target, key) => (typeof key === 'string' ? key : undefined) },\n);\n\n/**\n * A component's intent names as a module-level constant, so pure list rows can name intents\n * without passing them through `pick` (view/03-lists.md \"Rows must be pure\"). The same object\n * the view gets as `i`, typed by the component's class (ADR 0023). A row that names intents\n * this way needs an explicit return type, or the row, the view and the class infer each other\n * in a circle:\n *\n * const i = intentsOf<typeof TodoList>();\n * const Row = (t: Todo): TemplateResult => html`<input value=${t.id} data-intent=${i.Toggle} />`;\n */\nexport function intentsOf<C extends { readonly intentNames?: string }>(): IntentNames<\n NonNullable<C['intentNames']>\n> {\n return intentNames as IntentNames<NonNullable<C['intentNames']>>;\n}\n\n/**\n * Adds capture listeners for `types` to a component root, skipping those in `listening`. A\n * root that listens for `command` in a browser without invoker commands loads the fallback\n * (invokers-shim.ts, ADR 0003 tier 3); settled() waits for it.\n */\nexport function listenForIntents(\n root: Node,\n listening: Set<string>,\n types: readonly string[],\n handler: (event: Event) => void,\n): void {\n for (const type of types) {\n if (listening.has(type)) continue;\n listening.add(type);\n root.addEventListener(type, handler, { capture: true });\n if (type === 'command' && !invokersSupported()) loadInvokerShim(root);\n }\n}\n\n/**\n * Parses one event with its matching parser and delivers the message (sync or async). A\n * parser that returns `undefined` synchronously declines: the next intent outward for the same\n * event gets it, up to the component root. A promise can't decline. The model's `ctx()` is\n * built only when a parser runs, so its props are those at event time.\n */\nexport function handleIntent<M>(\n event: Event,\n root: Node,\n parsers: Readonly<Record<string, IntentParser<M> | true | undefined>>,\n tag: string,\n model: {\n ctx(): ParserCtx<unknown>;\n fail(cause: unknown, phase: 'parse', text: string, msg?: string): unknown;\n },\n deliver: (msg: Tagged | undefined) => void,\n): void {\n for (let input: IntentInput | undefined; (input = readIntent(event, root, input?.target));) {\n const { name } = input;\n const parser = parsers[name];\n if (parser === undefined) {\n console.warn(message(11, tag, name));\n return;\n }\n // A parser that throws or rejects is reported (ADR 0024) and sends nothing.\n const failed = (cause: unknown): void => {\n model.fail(cause, 'parse', message(12, tag, name), name);\n };\n let result: Tagged | undefined | Promise<Tagged | undefined>;\n try {\n // `true`: the intent sends its own tag (types allow it only for field-less variants).\n result =\n parser === true\n ? { _tag: name }\n : (parser(input, model.ctx()) as Tagged | undefined | Promise<Tagged | undefined>);\n } catch (cause) {\n failed(cause);\n return;\n }\n // The direct parent took a child's output: it goes no further (no outer component or page\n // listener sees it, ADR 0010 addendum). A declining parser lets it go on.\n if (event.type === OUTPUT_EVENT && result !== undefined) event.stopPropagation();\n if (result instanceof Promise) return void result.then(deliver, failed);\n if (result !== undefined) {\n deliver(result);\n return;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"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","sourcesContent":["import { loadInvokerShim } from '#invoker-fallback';\nimport { commandOf, invokersSupported } from './invokers.js';\nimport { isLightComponent } from './light-dom.js';\nimport type { IntentInput, IntentNames, IntentParser, ParserCtx, Tagged } from './types.js';\nimport { message, type Markup } from './view/index.js';\n\n/** Event a child component dispatches on its host to send an output up (ADR 0010); public. */\nexport const OUTPUT_EVENT = 'gyral-output';\n\n/** Default triggers (`defaultTrigger`): every component root listens for these. */\nexport const DEFAULT_EVENTS: readonly string[] = [\n 'click',\n 'submit',\n 'input',\n 'change',\n OUTPUT_EVENT,\n];\n\n/**\n * The events `data-intent-on` usually names. A component root listens (capture phase, so\n * non-bubbling events such as `toggle` are seen too) for the defaults and for the events its\n * templates name in `data-intent-<event>` attributes and static `data-intent-on` values; a\n * template that binds `data-intent-on` dynamically adds all of these. Any other event with a\n * bound intent takes a per-event attribute, `data-intent-pointerdown=${i.Press}`, whose name the\n * template carries (view/05-element.md \"Intent events\").\n */\nexport const INTENT_EVENTS: readonly string[] = [\n ...DEFAULT_EVENTS,\n 'keydown',\n 'keyup',\n 'focusin',\n 'focusout',\n 'toggle',\n 'command',\n];\n\n/**\n * A static `data-intent-<event>` attribute: a per-event intent (the event is in the name), or\n * `data-intent-on` (its value is the event, or a quoted list of them separated by spaces).\n */\nconst INTENT_ATTR = /\\sdata-intent-([\\w-]+)=([\"']?)(.*?)\\2(?=[\\s/>])/gi;\n// Names separated by any whitespace; none empty, so no listener for \"\".\nconst eventList = (value: string): string[] => value.match(/\\S+/g) ?? [];\nconst eventsByTemplate = new WeakMap<Markup, readonly string[]>();\n\n/**\n * The intent events a template's (or `raw()` markup's) `data-intent-<event>` and\n * `data-intent-on` attributes name. A bound attribute's name is in its part; a bound\n * `data-intent-on` adds every intent event.\n */\nexport function eventsOf(markup: Markup): readonly string[] {\n let events = eventsByTemplate.get(markup);\n if (events === undefined) {\n // Bound attributes are in the parts, by name: listed as ` name=* ` and matched like markup\n // (other parts' names are never intent attributes; `*` marks a bound data-intent-on).\n const bound = markup.parts?.map((p) => ` ${p[2] as string}=* `).join('') ?? '';\n events = Array.from((markup.html + bound).matchAll(INTENT_ATTR)).flatMap(\n ([, event, , value]) =>\n event !== 'on'\n ? (event as string)\n : value === '*'\n ? INTENT_EVENTS\n : eventList(value as string),\n );\n eventsByTemplate.set(markup, events);\n }\n return events;\n}\n\nconst isToggle = (el: Element): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\nconst CLICK_INPUT_TYPES = /^(button|submit|reset|image)$/;\n\n/**\n * The event that fires an intent unless `data-intent-on` overrides it. Custom elements\n * (autonomous: the name has a dash) talk to their parent via outputs.\n */\nexport function defaultTrigger(el: Element): string {\n const name = el.localName;\n return name === 'form'\n ? 'submit'\n : name === 'select' || isToggle(el)\n ? 'change'\n : name === 'textarea' ||\n (name === 'input' && !CLICK_INPUT_TYPES.test((el as HTMLInputElement).type))\n ? 'input'\n : name.includes('-')\n ? OUTPUT_EVENT\n : 'click';\n}\n\n/** The events that fire `el`'s intent: its `data-intent-on` list, or its default trigger. */\nfunction triggersOf(el: Element): string[] {\n return eventList(el.getAttribute('data-intent-on') ?? defaultTrigger(el));\n}\n\n/** Every class `define()` creates, so intent lookup can tell where a component begins. */\nconst hostClasses = new WeakSet<object>();\n\nexport function markGyralHost(ctor: object): void {\n hostClasses.add(ctor);\n}\n\nconst isGyralHost = (node: Node): boolean => hostClasses.has(node.constructor);\n\n/**\n * The number of Gyral host ancestors of `el` in the composed tree (across shadow roots): the\n * scheduler renders smaller depths first (view/04-scheduler.md \"Marking\").\n */\nexport function hostDepth(el: Element): number {\n let depth = 0;\n for (let node: Node | null = el.parentNode; node !== null;) {\n if (isGyralHost(node)) depth += 1;\n node = node instanceof ShadowRoot ? node.host : node.parentNode;\n }\n return depth;\n}\n\n/**\n * Does `node` belong to the component whose intent root is `root`? `root` is the component's\n * shadow root, or the host itself for light-DOM components (ADR 0014). The node must be in the\n * same tree (not inside a nested shadow root) with no light-DOM Gyral host between them: a\n * light-DOM component owns its children (it rendered them). Children of a shadow-DOM host are\n * the outer template's content, slotted into it, so they stay the outer component's (a form or\n * a child component slotted into a card). The nested host element itself (with the parent's\n * `data-intent` on it) still belongs to the parent.\n */\nfunction ownedBy(node: Element, root: Node): boolean {\n const tree = root instanceof ShadowRoot ? root : root.getRootNode();\n if (node.getRootNode() !== tree) return false;\n for (let p = node.parentNode; p !== null; p = p.parentNode) {\n if (p === root) return true;\n if (isGyralHost(p) && isLightComponent(p.constructor)) return false;\n }\n return false;\n}\n\nfunction valueOf(el: Element): string | undefined {\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLSelectElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLButtonElement\n ) {\n return el.value;\n }\n return undefined;\n}\n\n/**\n * Reads an event into an IntentInput for the nearest element on its path that names an intent\n * for it and belongs to `root`: its `data-intent-<type>`, else its `data-intent` when the\n * event is one of its triggers. With `after`, the search starts past that element (the intent\n * whose parser declined). Elements inside nested components are ignored: that is component\n * isolation. Form submissions are prevented and turned into FormData.\n */\nexport function readIntent(event: Event, root: Node, after?: Element): IntentInput | undefined {\n const path = event.composedPath();\n // indexOf(undefined) is -1: without `after`, the whole path.\n for (const target of path.slice(path.indexOf(after as Element) + 1)) {\n if (target === root) return undefined;\n if (!(target instanceof Element)) continue;\n // An output is the dispatching child's own: only the element it was dispatched on (the\n // child host, where the direct parent's `data-intent` sits) takes it, never an ancestor.\n if (event.type === OUTPUT_EVENT && target !== path[0]) continue;\n const name =\n target.getAttribute('data-intent-' + event.type) ??\n (triggersOf(target).includes(event.type) ? target.getAttribute('data-intent') : null);\n if (name === null || !ownedBy(target, root)) continue;\n let formData: FormData | undefined;\n if (target instanceof HTMLFormElement && event instanceof SubmitEvent) {\n event.preventDefault();\n formData = new FormData(target, event.submitter);\n }\n return {\n name,\n event,\n target,\n value: valueOf(target),\n checked: isToggle(target) ? target.checked : undefined,\n formData,\n detail: event instanceof CustomEvent ? (event.detail as unknown) : undefined,\n // Read structurally too: only keyboard events carry `key` to elements.\n key: (event as { readonly key?: string }).key,\n // ToggleEvent (popover, <details>) is not Baseline widely available: read it structurally.\n newState: (event as { readonly newState?: 'open' | 'closed' }).newState,\n command: commandOf(event),\n };\n }\n return undefined;\n}\n\n// Any property read returns its own name, so `intents.Increment === 'Increment'`.\n// Types restrict reads to real message tags; a tag without a parser warns at event time.\nexport const intentNames: unknown = new Proxy(\n {},\n { get: (_target, key) => (typeof key === 'string' ? key : undefined) },\n);\n\n/**\n * A component's intent names as a module-level constant, so pure list rows can name intents\n * without passing them through `pick` (rows must be pure). The same object\n * the view gets as `i`, typed by the component's class (ADR 0023). A row that names intents\n * this way needs an explicit return type, or the row, the view and the class infer each other\n * in a circle:\n *\n * const i = intentsOf<typeof TodoList>();\n * const Row = (t: Todo): TemplateResult => html`<input value=${t.id} data-intent=${i.Toggle} />`;\n */\nexport function intentsOf<C extends { readonly intentNames?: string }>(): IntentNames<\n NonNullable<C['intentNames']>\n> {\n return intentNames as IntentNames<NonNullable<C['intentNames']>>;\n}\n\n/**\n * Adds capture listeners for `types` to a component root, skipping those in `listening`. A\n * root that listens for `command` in a browser without invoker commands loads the fallback\n * (invokers-shim.ts, ADR 0003 tier 3); settled() waits for it.\n */\nexport function listenForIntents(\n root: Node,\n listening: Set<string>,\n types: readonly string[],\n handler: (event: Event) => void,\n): void {\n for (const type of types) {\n if (listening.has(type)) continue;\n listening.add(type);\n root.addEventListener(type, handler, { capture: true });\n if (type === 'command' && !invokersSupported()) loadInvokerShim(root);\n }\n}\n\n/**\n * Parses one event with its matching parser and delivers the message (sync or async). A\n * parser that returns `undefined` synchronously declines: the next intent outward for the same\n * event gets it, up to the component root. A promise can't decline. The model's `ctx()` is\n * built only when a parser runs, so its props are those at event time.\n */\nexport function handleIntent<M>(\n event: Event,\n root: Node,\n parsers: Readonly<Record<string, IntentParser<M> | true | undefined>>,\n tag: string,\n model: {\n ctx(): ParserCtx<unknown>;\n fail(cause: unknown, phase: 'parse', text: string, msg?: string): unknown;\n },\n deliver: (msg: Tagged | undefined) => void,\n): void {\n for (let input: IntentInput | undefined; (input = readIntent(event, root, input?.target));) {\n const { name } = input;\n const parser = parsers[name];\n if (parser === undefined) {\n console.warn(message(11, tag, name));\n return;\n }\n // A parser that throws or rejects is reported (ADR 0024) and sends nothing.\n const failed = (cause: unknown): void => {\n model.fail(cause, 'parse', message(12, tag, name), name);\n };\n let result: Tagged | undefined | Promise<Tagged | undefined>;\n try {\n // `true`: the intent sends its own tag (types allow it only for field-less variants).\n result =\n parser === true\n ? { _tag: name }\n : (parser(input, model.ctx()) as Tagged | undefined | Promise<Tagged | undefined>);\n } catch (cause) {\n failed(cause);\n return;\n }\n // The direct parent took a child's output: it goes no further (no outer component or page\n // listener sees it, ADR 0010 addendum). A declining parser lets it go on.\n if (event.type === OUTPUT_EVENT && result !== undefined) event.stopPropagation();\n if (result instanceof Promise) return void result.then(deliver, failed);\n if (result !== undefined) {\n deliver(result);\n return;\n }\n }\n}\n"]}
package/dist/server.d.ts CHANGED
@@ -18,7 +18,7 @@ export declare function render(value: ChildValue, options?: ServerRenderOptions)
18
18
  export declare function renderToString(value: ChildValue, options?: ServerRenderOptions): string;
19
19
  /**
20
20
  * `'sha256-…'` hashes of every registered shadow component's declarative-shadow-root `<style>`,
21
- * for a `Content-Security-Policy` `style-src` (view/08-styles.md "Server").
21
+ * for a `Content-Security-Policy` `style-src`.
22
22
  */
23
23
  export declare function styleHashes(): Promise<readonly string[]>;
24
24
  /**
package/dist/server.js CHANGED
@@ -33,7 +33,7 @@ export function renderToString(value, options) {
33
33
  }
34
34
  /**
35
35
  * `'sha256-…'` hashes of every registered shadow component's declarative-shadow-root `<style>`,
36
- * for a `Content-Security-Policy` `style-src` (view/08-styles.md "Server").
36
+ * for a `Content-Security-Policy` `style-src`.
37
37
  */
38
38
  export function styleHashes() {
39
39
  prepare();
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,yFAAyF;AACzF,kFAAkF;AAClF,6BAA6B;AAC7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,GAAG,EACH,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,eAAe,IAAI,MAAM,EACzB,MAAM,IAAI,YAAY,EACtB,cAAc,IAAI,YAAY,EAC9B,SAAS,EACT,WAAW,IAAI,MAAM,EACrB,aAAa,GAGd,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;AAEpC;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAY,GAAG,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE,SAAS,OAAO;IACd,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACvC,qBAAqB,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAiB,EAAE,OAA6B;IACrE,OAAO,EAAE,CAAC;IACV,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,cAAc,CAAC,KAAiB,EAAE,OAA6B;IAC7E,OAAO,EAAE,CAAC;IACV,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,EAAE,CAAC;IACV,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,CAAC;IACV,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,CAAC;IACV,OAAO,qBAAqB,EAAE,CAAC;AACjC,CAAC","sourcesContent":["// `@gyral/core/internal/server` (docs/design-docs/view/06-server.md): renders template results and Gyral\n// components to HTML without a DOM. Server-only: never import it from client code. Every\n// render first registers the specs define() recorded outside the browser, and the\n// `<gyral-stores>` provider.\nimport { registerRecordedSpecs } from './server-component.js';\nimport { storesProvider } from './stores-provider.js';\nimport {\n DEV,\n registerServerProvider,\n serverRegistryVersion,\n type ChildValue,\n} from './view/index.js';\nimport {\n componentStyles as styles,\n render as renderChunks,\n renderToString as renderString,\n styleHash,\n styleHashes as hashes,\n styleHashSync,\n type ServerRenderOptions,\n type StyleValues,\n} from './view/server/index.js';\n\nexport type { ServerRenderOptions, StyleValues };\nexport { styleHash, styleHashSync };\n\n/**\n * Whether `@gyral/core` resolved with the `development` condition (Vite's dev server and its\n * SSR, Vitest): the default of `render`'s `dev` option (view/06-server.md \"Development markers\").\n */\nexport const development: boolean = DEV;\nexport { StoreRegistry, withStoreScope } from './store-scope.js';\n\nfunction prepare(): void {\n registerServerProvider(storesProvider);\n registerRecordedSpecs();\n}\n\n/**\n * Renders `value` to HTML chunks, synchronously and lazily: each step of the iterator writes up\n * to the next component boundary, running that component's `init` and view. Wrap each step in\n * `withStoreScope(registry, …)` to give components the request's stores (ADR 0013).\n */\nexport function render(value: ChildValue, options?: ServerRenderOptions): Iterable<string> {\n prepare();\n return renderChunks(value, options);\n}\n\n/** Renders `value` to one HTML string. */\nexport function renderToString(value: ChildValue, options?: ServerRenderOptions): string {\n prepare();\n return renderString(value, options);\n}\n\n/**\n * `'sha256-…'` hashes of every registered shadow component's declarative-shadow-root `<style>`,\n * for a `Content-Security-Policy` `style-src` (view/08-styles.md \"Server\").\n */\nexport function styleHashes(): Promise<readonly string[]> {\n prepare();\n return hashes();\n}\n\n/**\n * Tag → declarative-shadow-root `<style>` text of every registered shadow component with CSS\n * (recorded specs registered first), for checking or building a CSP at render time.\n */\nexport function componentStyles(): ReadonlyMap<string, string> {\n prepare();\n return styles();\n}\n\n/**\n * A number that changes whenever a component registers with the server renderer (recorded\n * specs registered first), so its CSS, and the `<style>` hashes a CSP lists, may have changed.\n * Caches built from `componentStyles()` key on it.\n */\nexport function registryVersion(): number {\n prepare();\n return serverRegistryVersion();\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,yFAAyF;AACzF,kFAAkF;AAClF,6BAA6B;AAC7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,GAAG,EACH,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,eAAe,IAAI,MAAM,EACzB,MAAM,IAAI,YAAY,EACtB,cAAc,IAAI,YAAY,EAC9B,SAAS,EACT,WAAW,IAAI,MAAM,EACrB,aAAa,GAGd,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;AAEpC;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAY,GAAG,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE,SAAS,OAAO;IACd,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACvC,qBAAqB,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAiB,EAAE,OAA6B;IACrE,OAAO,EAAE,CAAC;IACV,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,cAAc,CAAC,KAAiB,EAAE,OAA6B;IAC7E,OAAO,EAAE,CAAC;IACV,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,EAAE,CAAC;IACV,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,CAAC;IACV,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,CAAC;IACV,OAAO,qBAAqB,EAAE,CAAC;AACjC,CAAC","sourcesContent":["// `@gyral/core/internal/server` (docs/design-docs/view/06-server.md): renders template results and Gyral\n// components to HTML without a DOM. Server-only: never import it from client code. Every\n// render first registers the specs define() recorded outside the browser, and the\n// `<gyral-stores>` provider.\nimport { registerRecordedSpecs } from './server-component.js';\nimport { storesProvider } from './stores-provider.js';\nimport {\n DEV,\n registerServerProvider,\n serverRegistryVersion,\n type ChildValue,\n} from './view/index.js';\nimport {\n componentStyles as styles,\n render as renderChunks,\n renderToString as renderString,\n styleHash,\n styleHashes as hashes,\n styleHashSync,\n type ServerRenderOptions,\n type StyleValues,\n} from './view/server/index.js';\n\nexport type { ServerRenderOptions, StyleValues };\nexport { styleHash, styleHashSync };\n\n/**\n * Whether `@gyral/core` resolved with the `development` condition (Vite's dev server and its\n * SSR, Vitest): the default of `render`'s `dev` option (view/06-server.md \"Development markers\").\n */\nexport const development: boolean = DEV;\nexport { StoreRegistry, withStoreScope } from './store-scope.js';\n\nfunction prepare(): void {\n registerServerProvider(storesProvider);\n registerRecordedSpecs();\n}\n\n/**\n * Renders `value` to HTML chunks, synchronously and lazily: each step of the iterator writes up\n * to the next component boundary, running that component's `init` and view. Wrap each step in\n * `withStoreScope(registry, …)` to give components the request's stores (ADR 0013).\n */\nexport function render(value: ChildValue, options?: ServerRenderOptions): Iterable<string> {\n prepare();\n return renderChunks(value, options);\n}\n\n/** Renders `value` to one HTML string. */\nexport function renderToString(value: ChildValue, options?: ServerRenderOptions): string {\n prepare();\n return renderString(value, options);\n}\n\n/**\n * `'sha256-…'` hashes of every registered shadow component's declarative-shadow-root `<style>`,\n * for a `Content-Security-Policy` `style-src`.\n */\nexport function styleHashes(): Promise<readonly string[]> {\n prepare();\n return hashes();\n}\n\n/**\n * Tag → declarative-shadow-root `<style>` text of every registered shadow component with CSS\n * (recorded specs registered first), for checking or building a CSP at render time.\n */\nexport function componentStyles(): ReadonlyMap<string, string> {\n prepare();\n return styles();\n}\n\n/**\n * A number that changes whenever a component registers with the server renderer (recorded\n * specs registered first), so its CSS, and the `<style>` hashes a CSP lists, may have changed.\n * Caches built from `componentStyles()` key on it.\n */\nexport function registryVersion(): number {\n prepare();\n return serverRegistryVersion();\n}\n"]}
package/dist/store.d.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
2
  import { type Command, type DriverOverrides, type Next } from './command.js';
3
3
  import { type LocalHost } from './features.js';
4
- import type { Tagged } from './types.js';
5
- type Variant<M extends Tagged, K extends M['_tag']> = Extract<M, {
6
- readonly _tag: K;
7
- }>;
4
+ import type { Tagged, Variant } from './types.js';
8
5
  /** One pure reducer per message tag, like a component's `update` but without context. */
9
6
  export type StoreUpdate<S, M extends Tagged> = {
10
7
  readonly [K in M['_tag']]: (state: S, msg: Variant<M, K>) => Next<S, M>;
@@ -77,7 +74,7 @@ export interface AnyStoreInstance {
77
74
  dispose(): void;
78
75
  /**
79
76
  * Called by the registry that holds this instance, so the store's `send(other, msg)`
80
- * commands reach `other`'s instance in the same scope (gyral-czi.20).
77
+ * commands reach `other`'s instance in the same scope.
81
78
  */
82
79
  bindScope?(resolve: StoreResolver): void;
83
80
  }
@@ -114,4 +111,3 @@ export declare function changed<S>(store: StoreRef<S>, msg: StoreChanged): {
114
111
  readonly state: S;
115
112
  readonly prev: S;
116
113
  } | undefined;
117
- export {};