@loadbare/app 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/build/skills-cli.d.ts +12 -0
  2. package/dist/build/skills-cli.d.ts.map +1 -0
  3. package/dist/build/skills-cli.js +81 -0
  4. package/dist/build/skills-cli.js.map +1 -0
  5. package/dist/build/skills.d.ts +47 -0
  6. package/dist/build/skills.d.ts.map +1 -0
  7. package/dist/build/skills.js +124 -0
  8. package/dist/build/skills.js.map +1 -0
  9. package/dist/core/lb-constants.d.ts +2 -0
  10. package/dist/core/lb-constants.d.ts.map +1 -1
  11. package/dist/core/lb-constants.js +18 -4
  12. package/dist/core/lb-constants.js.map +1 -1
  13. package/dist/hub/lb-apply.d.ts +10 -0
  14. package/dist/hub/lb-apply.d.ts.map +1 -1
  15. package/dist/hub/lb-apply.js +15 -1
  16. package/dist/hub/lb-apply.js.map +1 -1
  17. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  18. package/dist/hub/lb-hub.browser.js +116 -35
  19. package/dist/hub/lb-hub.browser.js.map +1 -1
  20. package/dist/server/lb-express.d.ts +13 -5
  21. package/dist/server/lb-express.d.ts.map +1 -1
  22. package/dist/server/lb-express.js +26 -7
  23. package/dist/server/lb-express.js.map +1 -1
  24. package/dist/server/lb-server.d.ts +3 -0
  25. package/dist/server/lb-server.d.ts.map +1 -1
  26. package/dist/server/lb-server.js.map +1 -1
  27. package/docs/TECHREF-1.0.md +93 -22
  28. package/docs/comparison.md +8 -7
  29. package/docs/reference/chrome.md +5 -3
  30. package/docs/reference/data-binding.md +28 -0
  31. package/docs/reference/server.md +11 -0
  32. package/docs/reference/widgets.md +8 -4
  33. package/docs/roadmap.md +16 -0
  34. package/docs/theory.md +8 -1
  35. package/docs/tutorials/010-pages-and-navigation.md +2 -1
  36. package/package.json +8 -4
  37. package/skills/loadbare-app/SKILL.md +275 -0
  38. package/skills/loadbare-app/references/TECHREF-1.0.md +1260 -0
  39. package/skills/loadbare-app/references/builder.md +134 -0
  40. package/skills/loadbare-app/references/chrome.md +160 -0
  41. package/skills/loadbare-app/references/css.md +44 -0
  42. package/skills/loadbare-app/references/custom-elements.md +397 -0
  43. package/skills/loadbare-app/references/data-binding.md +485 -0
  44. package/skills/loadbare-app/references/overview.md +38 -0
  45. package/skills/loadbare-app/references/page-files.md +194 -0
  46. package/skills/loadbare-app/references/server.md +153 -0
  47. package/skills/loadbare-app/references/widgets.md +178 -0
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * loadbare-app — commands that are not the build.
4
+ *
5
+ * loadbare-app skills install [--copy] [--dir <path>]
6
+ *
7
+ * Installs the agent skills that ship in this package into `.agents/skills`
8
+ * and `.claude/skills`, or into `--dir` alone — see build/skills.ts. Nothing
9
+ * goes to stdout; every line is progress, on stderr.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=skills-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-cli.d.ts","sourceRoot":"","sources":["../../build/skills-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * loadbare-app — commands that are not the build.
4
+ *
5
+ * loadbare-app skills install [--copy] [--dir <path>]
6
+ *
7
+ * Installs the agent skills that ship in this package into `.agents/skills`
8
+ * and `.claude/skills`, or into `--dir` alone — see build/skills.ts. Nothing
9
+ * goes to stdout; every line is progress, on stderr.
10
+ */
11
+ import { parseArgs } from "node:util";
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { findPackageRoot } from "./package-root.js";
15
+ import { installSkills, SKILL_DESTINATIONS } from "./skills.js";
16
+ const USAGE = "usage: loadbare-app skills install [--copy] [--dir <path>]";
17
+ async function main() {
18
+ let parsed;
19
+ try {
20
+ parsed = parseArgs({
21
+ allowPositionals: true,
22
+ options: {
23
+ copy: { type: "boolean", default: false },
24
+ dir: { type: "string" },
25
+ },
26
+ });
27
+ }
28
+ catch (err) {
29
+ console.error(`loadbare-app: ${err.message}`);
30
+ console.error(USAGE);
31
+ return 1;
32
+ }
33
+ const { values, positionals } = parsed;
34
+ if (positionals.join(" ") !== "skills install") {
35
+ console.error(USAGE);
36
+ return 1;
37
+ }
38
+ // Source and compiled output sit at different depths, so the package is
39
+ // found by walking up rather than by counting.
40
+ const packageRoot = await findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
41
+ const source = path.join(packageRoot, "skills");
42
+ const destinations = values.dir ? [values.dir] : SKILL_DESTINATIONS;
43
+ const result = installSkills({
44
+ source,
45
+ destinations,
46
+ copy: values.copy,
47
+ cwd: process.cwd(),
48
+ log: (line) => console.error(line),
49
+ });
50
+ // An install with no skills in it is a packaging fault, not a user error,
51
+ // and saying where it looked is what makes that diagnosable.
52
+ if (result.names.length === 0) {
53
+ console.error(`loadbare-app: this install of @loadbare/app carries no skills (looked in ${source})`);
54
+ return 1;
55
+ }
56
+ console.error("");
57
+ if (result.copiedInstead) {
58
+ console.error("Some links could not be created and those skills were copied instead.");
59
+ console.error("A copy does not follow the package when it is upgraded — rerun this");
60
+ console.error("command after an upgrade, or use --copy everywhere and commit the result.");
61
+ console.error("");
62
+ }
63
+ if (values.copy) {
64
+ console.error("These are copies. Commit them, and rerun this command after upgrading");
65
+ console.error("@loadbare/app so the skill matches the framework.");
66
+ }
67
+ else {
68
+ console.error("These are links into node_modules. To keep them out of git:");
69
+ console.error("");
70
+ for (const destination of destinations) {
71
+ for (const name of result.names) {
72
+ console.error(` ${path.join(destination, name)}`);
73
+ }
74
+ }
75
+ }
76
+ console.error("");
77
+ console.error("Start your agent from this directory and it will find them.");
78
+ return result.refused > 0 ? 1 : 0;
79
+ }
80
+ process.exitCode = await main();
81
+ //# sourceMappingURL=skills-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-cli.js","sourceRoot":"","sources":["../../build/skills-cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * loadbare-app — commands that are not the build.\n *\n * loadbare-app skills install [--copy] [--dir <path>]\n *\n * Installs the agent skills that ship in this package into `.agents/skills`\n * and `.claude/skills`, or into `--dir` alone — see build/skills.ts. Nothing\n * goes to stdout; every line is progress, on stderr.\n */\n\nimport { parseArgs } from \"node:util\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { findPackageRoot } from \"./package-root.js\";\nimport { installSkills, SKILL_DESTINATIONS } from \"./skills.js\";\n\nconst USAGE = \"usage: loadbare-app skills install [--copy] [--dir <path>]\";\n\nasync function main(): Promise<number> {\n let parsed;\n try {\n parsed = parseArgs({\n allowPositionals: true,\n options: {\n copy: { type: \"boolean\", default: false },\n dir: { type: \"string\" },\n },\n });\n } catch (err) {\n console.error(`loadbare-app: ${(err as Error).message}`);\n console.error(USAGE);\n return 1;\n }\n\n const { values, positionals } = parsed;\n if (positionals.join(\" \") !== \"skills install\") {\n console.error(USAGE);\n return 1;\n }\n\n // Source and compiled output sit at different depths, so the package is\n // found by walking up rather than by counting.\n const packageRoot = await findPackageRoot(\n path.dirname(fileURLToPath(import.meta.url)),\n );\n const source = path.join(packageRoot, \"skills\");\n const destinations = values.dir ? [values.dir] : SKILL_DESTINATIONS;\n\n const result = installSkills({\n source,\n destinations,\n copy: values.copy!,\n cwd: process.cwd(),\n log: (line) => console.error(line),\n });\n\n // An install with no skills in it is a packaging fault, not a user error,\n // and saying where it looked is what makes that diagnosable.\n if (result.names.length === 0) {\n console.error(\n `loadbare-app: this install of @loadbare/app carries no skills (looked in ${source})`,\n );\n return 1;\n }\n\n console.error(\"\");\n if (result.copiedInstead) {\n console.error(\n \"Some links could not be created and those skills were copied instead.\",\n );\n console.error(\n \"A copy does not follow the package when it is upgraded — rerun this\",\n );\n console.error(\n \"command after an upgrade, or use --copy everywhere and commit the result.\",\n );\n console.error(\"\");\n }\n\n if (values.copy) {\n console.error(\n \"These are copies. Commit them, and rerun this command after upgrading\",\n );\n console.error(\"@loadbare/app so the skill matches the framework.\");\n } else {\n console.error(\n \"These are links into node_modules. To keep them out of git:\",\n );\n console.error(\"\");\n for (const destination of destinations) {\n for (const name of result.names) {\n console.error(` ${path.join(destination, name)}`);\n }\n }\n }\n\n console.error(\"\");\n console.error(\"Start your agent from this directory and it will find them.\");\n\n return result.refused > 0 ? 1 : 0;\n}\n\nprocess.exitCode = await main();\n"]}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Put the agent skills that ship in this package where a coding agent will
3
+ * find them.
4
+ *
5
+ * The same command as `loadbare-db skills install`, and deliberately a copy
6
+ * of it rather than a shared module: `@loadbare/db` shares no code with this
7
+ * package, and this package imports nothing from the repository.
8
+ *
9
+ * A symlink by default, because the point is that the skill tracks the
10
+ * dependency: `npm install @loadbare/app@next` and the skill moves with it.
11
+ * `copy` is for a team that would rather commit the text than have every
12
+ * clone depend on `node_modules` being populated.
13
+ *
14
+ * Nothing is done to `.gitignore`. Whether these paths are committed is a
15
+ * decision about the project, and the command that installs a file is not the
16
+ * one to make it.
17
+ */
18
+ /**
19
+ * Where a coding agent looks for skills.
20
+ *
21
+ * `.agents/skills` is the vendor-neutral location, read by Codex, Cursor and
22
+ * Copilot; `.claude/skills` is Claude Code's, and Cursor reads it too. Both
23
+ * are written rather than guessing from what happens to be configured.
24
+ */
25
+ export declare const SKILL_DESTINATIONS: string[];
26
+ export interface InstallOptions {
27
+ /** The directory holding one subdirectory per skill. */
28
+ source: string;
29
+ /** The directories to install into. */
30
+ destinations: string[];
31
+ /** Copy rather than link. */
32
+ copy: boolean;
33
+ /** The project root; a link to a package inside it is relative. */
34
+ cwd: string;
35
+ /** Where progress goes, one line at a time. */
36
+ log: (line: string) => void;
37
+ }
38
+ export interface InstallResult {
39
+ names: string[];
40
+ refused: number;
41
+ /** A link was refused by the platform and a copy made in its place. */
42
+ copiedInstead: boolean;
43
+ }
44
+ /** The skills under `source`: each directory holding a `SKILL.md`. */
45
+ export declare function shippedSkills(source: string): string[];
46
+ export declare function installSkills(options: InstallOptions): InstallResult;
47
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../build/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,UAAuC,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,6BAA6B;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,aAAa,EAAE,OAAO,CAAC;CACxB;AAwCD,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAUtD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAsDpE"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Put the agent skills that ship in this package where a coding agent will
3
+ * find them.
4
+ *
5
+ * The same command as `loadbare-db skills install`, and deliberately a copy
6
+ * of it rather than a shared module: `@loadbare/db` shares no code with this
7
+ * package, and this package imports nothing from the repository.
8
+ *
9
+ * A symlink by default, because the point is that the skill tracks the
10
+ * dependency: `npm install @loadbare/app@next` and the skill moves with it.
11
+ * `copy` is for a team that would rather commit the text than have every
12
+ * clone depend on `node_modules` being populated.
13
+ *
14
+ * Nothing is done to `.gitignore`. Whether these paths are committed is a
15
+ * decision about the project, and the command that installs a file is not the
16
+ * one to make it.
17
+ */
18
+ import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, symlinkSync, } from "node:fs";
19
+ import path from "node:path";
20
+ /**
21
+ * Where a coding agent looks for skills.
22
+ *
23
+ * `.agents/skills` is the vendor-neutral location, read by Codex, Cursor and
24
+ * Copilot; `.claude/skills` is Claude Code's, and Cursor reads it too. Both
25
+ * are written rather than guessing from what happens to be configured.
26
+ */
27
+ export const SKILL_DESTINATIONS = [".agents/skills", ".claude/skills"];
28
+ /** `absent` covers a dangling symlink, which `existsSync` reports as missing. */
29
+ function entryKind(p) {
30
+ let stat;
31
+ try {
32
+ stat = lstatSync(p);
33
+ }
34
+ catch {
35
+ return "absent";
36
+ }
37
+ if (stat.isSymbolicLink())
38
+ return "symlink";
39
+ if (stat.isDirectory())
40
+ return "directory";
41
+ return "other";
42
+ }
43
+ /** The `name:` a skill directory declares, or null if there is no skill there. */
44
+ function declaredSkillName(dir) {
45
+ try {
46
+ const match = /^name:\s*(\S+)\s*$/m.exec(readFileSync(path.join(dir, "SKILL.md"), "utf-8"));
47
+ return match?.[1] ?? null;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ /**
54
+ * A link into `node_modules` is relative, so the project stays movable and the
55
+ * link means the same thing to everyone who checks it out. A globally
56
+ * installed package gets an absolute path, since a relative one breaks the
57
+ * moment the project moves.
58
+ */
59
+ function linkTarget(from, to, cwd) {
60
+ const inside = path.resolve(from).startsWith(path.resolve(cwd) + path.sep);
61
+ return inside
62
+ ? path.relative(path.dirname(path.resolve(cwd, to)), path.resolve(from))
63
+ : path.resolve(from);
64
+ }
65
+ /** The skills under `source`: each directory holding a `SKILL.md`. */
66
+ export function shippedSkills(source) {
67
+ if (!existsSync(source))
68
+ return [];
69
+ return readdirSync(source, { withFileTypes: true })
70
+ .filter((entry) => entry.isDirectory() &&
71
+ existsSync(path.join(source, entry.name, "SKILL.md")))
72
+ .map((entry) => entry.name)
73
+ .sort();
74
+ }
75
+ export function installSkills(options) {
76
+ const { source, destinations, copy, cwd, log } = options;
77
+ const names = shippedSkills(source);
78
+ let refused = 0;
79
+ let copiedInstead = false;
80
+ for (const destination of destinations) {
81
+ for (const name of names) {
82
+ const from = path.join(source, name);
83
+ const to = path.join(destination, name);
84
+ const at = path.resolve(cwd, to);
85
+ const kind = entryKind(at);
86
+ // A directory this command wrote says so on its own front matter, and
87
+ // is replaced. Anything else at that path belongs to someone, and a
88
+ // skill install is not permitted to be the thing that lost it.
89
+ if (kind === "directory" && declaredSkillName(at) !== name) {
90
+ log(` refused ${to} — a directory is already there and is not the ${name} skill`);
91
+ refused++;
92
+ continue;
93
+ }
94
+ if (kind === "other") {
95
+ log(` refused ${to} — a file is already there`);
96
+ refused++;
97
+ continue;
98
+ }
99
+ mkdirSync(path.resolve(cwd, destination), { recursive: true });
100
+ rmSync(at, { recursive: true, force: true });
101
+ if (copy) {
102
+ cpSync(from, at, { recursive: true });
103
+ log(` copied ${to}`);
104
+ continue;
105
+ }
106
+ try {
107
+ symlinkSync(linkTarget(from, to, cwd), at, "dir");
108
+ log(` linked ${to}`);
109
+ }
110
+ catch (error) {
111
+ // Windows refuses a symlink to an unprivileged process unless
112
+ // developer mode is on. A copy is the same skill, so the install
113
+ // succeeds and the difference is reported rather than raised.
114
+ if (error.code !== "EPERM")
115
+ throw error;
116
+ cpSync(from, at, { recursive: true });
117
+ copiedInstead = true;
118
+ log(` copied ${to}`);
119
+ }
120
+ }
121
+ }
122
+ return { names, refused, copiedInstead };
123
+ }
124
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../build/skills.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Put the agent skills that ship in this package where a coding agent will\n * find them.\n *\n * The same command as `loadbare-db skills install`, and deliberately a copy\n * of it rather than a shared module: `@loadbare/db` shares no code with this\n * package, and this package imports nothing from the repository.\n *\n * A symlink by default, because the point is that the skill tracks the\n * dependency: `npm install @loadbare/app@next` and the skill moves with it.\n * `copy` is for a team that would rather commit the text than have every\n * clone depend on `node_modules` being populated.\n *\n * Nothing is done to `.gitignore`. Whether these paths are committed is a\n * decision about the project, and the command that installs a file is not the\n * one to make it.\n */\n\nimport {\n cpSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n symlinkSync,\n} from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Where a coding agent looks for skills.\n *\n * `.agents/skills` is the vendor-neutral location, read by Codex, Cursor and\n * Copilot; `.claude/skills` is Claude Code's, and Cursor reads it too. Both\n * are written rather than guessing from what happens to be configured.\n */\nexport const SKILL_DESTINATIONS = [\".agents/skills\", \".claude/skills\"];\n\nexport interface InstallOptions {\n /** The directory holding one subdirectory per skill. */\n source: string;\n /** The directories to install into. */\n destinations: string[];\n /** Copy rather than link. */\n copy: boolean;\n /** The project root; a link to a package inside it is relative. */\n cwd: string;\n /** Where progress goes, one line at a time. */\n log: (line: string) => void;\n}\n\nexport interface InstallResult {\n names: string[];\n refused: number;\n /** A link was refused by the platform and a copy made in its place. */\n copiedInstead: boolean;\n}\n\n/** `absent` covers a dangling symlink, which `existsSync` reports as missing. */\nfunction entryKind(p: string): \"absent\" | \"symlink\" | \"directory\" | \"other\" {\n let stat;\n try {\n stat = lstatSync(p);\n } catch {\n return \"absent\";\n }\n if (stat.isSymbolicLink()) return \"symlink\";\n if (stat.isDirectory()) return \"directory\";\n return \"other\";\n}\n\n/** The `name:` a skill directory declares, or null if there is no skill there. */\nfunction declaredSkillName(dir: string): string | null {\n try {\n const match = /^name:\\s*(\\S+)\\s*$/m.exec(\n readFileSync(path.join(dir, \"SKILL.md\"), \"utf-8\"),\n );\n return match?.[1] ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * A link into `node_modules` is relative, so the project stays movable and the\n * link means the same thing to everyone who checks it out. A globally\n * installed package gets an absolute path, since a relative one breaks the\n * moment the project moves.\n */\nfunction linkTarget(from: string, to: string, cwd: string): string {\n const inside = path.resolve(from).startsWith(path.resolve(cwd) + path.sep);\n return inside\n ? path.relative(path.dirname(path.resolve(cwd, to)), path.resolve(from))\n : path.resolve(from);\n}\n\n/** The skills under `source`: each directory holding a `SKILL.md`. */\nexport function shippedSkills(source: string): string[] {\n if (!existsSync(source)) return [];\n return readdirSync(source, { withFileTypes: true })\n .filter(\n (entry) =>\n entry.isDirectory() &&\n existsSync(path.join(source, entry.name, \"SKILL.md\")),\n )\n .map((entry) => entry.name)\n .sort();\n}\n\nexport function installSkills(options: InstallOptions): InstallResult {\n const { source, destinations, copy, cwd, log } = options;\n const names = shippedSkills(source);\n let refused = 0;\n let copiedInstead = false;\n\n for (const destination of destinations) {\n for (const name of names) {\n const from = path.join(source, name);\n const to = path.join(destination, name);\n const at = path.resolve(cwd, to);\n const kind = entryKind(at);\n\n // A directory this command wrote says so on its own front matter, and\n // is replaced. Anything else at that path belongs to someone, and a\n // skill install is not permitted to be the thing that lost it.\n if (kind === \"directory\" && declaredSkillName(at) !== name) {\n log(\n ` refused ${to} — a directory is already there and is not the ${name} skill`,\n );\n refused++;\n continue;\n }\n if (kind === \"other\") {\n log(` refused ${to} — a file is already there`);\n refused++;\n continue;\n }\n\n mkdirSync(path.resolve(cwd, destination), { recursive: true });\n rmSync(at, { recursive: true, force: true });\n\n if (copy) {\n cpSync(from, at, { recursive: true });\n log(` copied ${to}`);\n continue;\n }\n\n try {\n symlinkSync(linkTarget(from, to, cwd), at, \"dir\");\n log(` linked ${to}`);\n } catch (error) {\n // Windows refuses a symlink to an unprivileged process unless\n // developer mode is on. A copy is the same skill, so the install\n // succeeds and the difference is reported rather than raised.\n if ((error as NodeJS.ErrnoException).code !== \"EPERM\") throw error;\n cpSync(from, at, { recursive: true });\n copiedInstead = true;\n log(` copied ${to}`);\n }\n }\n }\n\n return { names, refused, copiedInstead };\n}\n"]}
@@ -16,6 +16,8 @@ export declare const LB_ACTIONS: readonly ["lb-row-insert", "lb-row-delete", "lb
16
16
  export declare const ATTR_SLOT = "lb-slot";
17
17
  export declare const ATTR_TEMPLATE = "lb-template";
18
18
  export declare const ATTR_NAV_LINK = "lb-nav-link";
19
+ export declare const ATTR_QUERY_PARM = "lb-query-parm";
20
+ export declare const ATTR_QUERY_PARM_PUSH = "lb-query-parm-push";
19
21
  export declare const ATTR_PAGE = "lb-page";
20
22
  export declare const NAV_ROW = "lb-navigation";
21
23
  export declare const NAV_CELL_LABEL = "page-label";
@@ -1 +1 @@
1
- {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAQ7C,eAAO,MAAM,SAAS,YAAY,CAAC;AAanC,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC,eAAO,MAAM,UAAU,aAAa,CAAC;AAOrC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAK7C,eAAO,MAAM,aAAa,eAAe,CAAC;AAS1C,eAAO,MAAM,WAAW,cAAc,CAAC;AAYvC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAMxC,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAKjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAcjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAIjD,eAAO,MAAM,UAAU,8DAIb,CAAC;AAMX,eAAO,MAAM,SAAS,YAAY,CAAC;AAYnC,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAM3C,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAKvC,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,YAAY,aAAa,CAAC;AAUvC,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAQnD,eAAO,MAAM,YAAY,WAAW,CAAC;AASrC,eAAO,MAAM,YAAY,eAAe,CAAC;AAOzC,eAAO,MAAM,UAAU,aAAa,CAAC;AAMrC,eAAO,MAAM,WAAW,QAAQ,CAAC;AAIjC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}
1
+ {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAQ7C,eAAO,MAAM,SAAS,YAAY,CAAC;AAanC,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC,eAAO,MAAM,UAAU,aAAa,CAAC;AAOrC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAK7C,eAAO,MAAM,aAAa,eAAe,CAAC;AAS1C,eAAO,MAAM,WAAW,cAAc,CAAC;AAYvC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAMxC,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAKjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAcjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAIjD,eAAO,MAAM,UAAU,8DAIb,CAAC;AAMX,eAAO,MAAM,SAAS,YAAY,CAAC;AAYnC,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAQ3C,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAK/C,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAMzD,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAKvC,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,YAAY,aAAa,CAAC;AAUvC,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAQnD,eAAO,MAAM,YAAY,WAAW,CAAC;AASrC,eAAO,MAAM,YAAY,eAAe,CAAC;AAOzC,eAAO,MAAM,UAAU,aAAa,CAAC;AASrC,eAAO,MAAM,WAAW,QAAQ,CAAC;AAIjC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}
@@ -122,6 +122,17 @@ export const ATTR_TEMPLATE = "lb-template";
122
122
  // Only anchors carrying this are intercepted, so external links are never
123
123
  // hijacked.
124
124
  export const ATTR_NAV_LINK = "lb-nav-link";
125
+ // Names the query parm a control writes. The query string describes what is
126
+ // on screen and never commands, so a control that narrows the page writes its
127
+ // value into the address bar rather than sending it: the hub sets the one
128
+ // parm, or deletes it when the value is empty, and loads the page at the new
129
+ // URL. Every other parm is left as it is. Landed back onto the control from
130
+ // the URL on every load, so a reload, a mailed link and Back all show it.
131
+ export const ATTR_QUERY_PARM = "lb-query-parm";
132
+ // Changing what the current page shows is not going anywhere, so a query parm
133
+ // write replaces the history entry. A control carrying this pushes one
134
+ // instead, for the rare parm a user would expect Back to undo.
135
+ export const ATTR_QUERY_PARM_PUSH = "lb-query-parm-push";
125
136
  // A page host ships as <template lb-page="<name>">. An attribute rather than
126
137
  // an id: an id lives in the application's own namespace, where a page named
127
138
  // for something the chrome also ids would collide, and getElementById()
@@ -136,7 +147,7 @@ export const ATTR_PAGE = "lb-page";
136
147
  export const NAV_ROW = "lb-navigation";
137
148
  // Its two columns. The label is the nav's: the text of the lb-nav-link
138
149
  // anchor whose href names the current page, and empty when no anchor does.
139
- // The URI is the path as the browser has it.
150
+ // The URI is the path and query string as the browser has them.
140
151
  export const NAV_CELL_LABEL = "page-label";
141
152
  export const NAV_CELL_URI = "page-uri";
142
153
  // Marks the chrome's own <dialog> for a page name that resolves to no
@@ -170,9 +181,12 @@ export const ATTR_PENDING = "lb-pending";
170
181
  // happened.
171
182
  export const ATTR_ERROR = "lb-error";
172
183
  // --- Wire --------------------------------------------------------------
173
- // One path, one method: everything the hub asks of the server is a POST to
174
- // this path. Navigation is therefore the only GET an application serves,
175
- // which is what leaves the page name space unrestricted.
184
+ // One path, one method: everything the hub asks of the server is a POST
185
+ // below this path. The page name follows it, and the query string the
186
+ // browser is showing rides after that verbatim, so `/accounts?acct=23` asks
187
+ // as `POST /lb/accounts?acct=23` and the server always knows what URL the
188
+ // browser is showing. Navigation is therefore the only GET an application
189
+ // serves, which is what leaves the page name space unrestricted.
176
190
  export const LB_ENDPOINT = "/lb";
177
191
  // A client-side deadline for one round trip. fetch() has no deadline of its
178
192
  // own; past this the request is aborted and treated as a failure.
@@ -1 +1 @@
1
- {"version":3,"file":"lb-constants.js","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"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","sourcesContent":["// Every hub attribute name lives here. Nothing writes one as a string\n// literal — not a widget, not a page host, not a test. See\n// docs/reference/data-binding.md.\n\n// --- Binding (docs/reference/data-binding.md) ---------------------------\n// One containment ladder: a list holds rows, a row holds cells. Both scope\n// attributes take a name the server declares, and a nested one of either\n// kind begins a new scope.\n\n// Scopes DOM children to a named set of rows.\nexport const ATTR_LIST = \"lb-list\";\n\n// Scopes DOM children to one named row.\n//\n// A single-row scope is read-only. Every write operation needs a key, and a\n// key exists only on a live row the hub stamped inside a list, so a declared\n// action is the only thing such a scope can send. An application that wants\n// a writable single row declares a list that answers with one row.\nexport const ATTR_ROW = \"lb-row\";\n\n// Named on a row template by the developer: the column that identifies a row\n// of the list. A property of the list rather than of the template — a list\n// without a fixed key column is meaningless — but written where the rows\n// land, because that is the only place the page says anything about them.\nexport const ATTR_KEY = \"lb-key\";\n\n// Stamped on a live row by the row machinery: that row's value of the column\n// ATTR_KEY names. Written by the hub, never by a developer, and read back by\n// whatever needs to know which row it is inside — a delete button, an update\n// form, a widget building its own request, or a stylesheet.\nexport const ATTR_KEY_VALUE = \"lb-key-value\";\n\n// The column an element displays.\n//\n// Its value is a column name and never a cell name. A cell has no name of\n// its own: it is identified by its row and its column, and the row arrives\n// from scope, so the column is all that is left to say. ATTR_KEY holds a\n// column name for the same reason.\nexport const ATTR_CELL = \"lb-cell\";\n\n// The column that decides whether an element is present: `null` and `false`\n// are off, and every other value is on. It binds by the same ancestor rule as\n// ATTR_CELL, and names a column for the same reason.\n//\n// An element that is off is moved into a `<template>` carrying this attribute\n// and the same column, standing where the element stood, and moved back out\n// when the column turns on. The builder ships every such element already\n// inside its template. A developer writes this attribute on an element and\n// never on a template: the template form is Loadbare's, and holds the absent\n// branch in the document so the hub holds nothing. Landing reaches inside it,\n// so a branch that returns is already current; nothing else does.\nexport const ATTR_SHOW = \"lb-show\";\n\nexport const ATTR_VALUE = \"lb-value\";\n\n// Stamped on a list scope by the shared row machinery: how many rows it is\n// showing. Written by the hub and never bound to — it is derived from rows\n// the hub already knows, exactly as the section heading in lb-table is. A\n// list widget watches it, and it exists so that \"no members yet\" is a\n// stylesheet rule rather than a conditional in every list widget.\nexport const ATTR_ROW_COUNT = \"lb-row-count\";\n\n// --- Requests (docs/reference/data-binding.md) --------------------------\n// One event name, used by a widget reaching the server and by a widget\n// reaching an ancestor widget.\nexport const LB_EVENT_NAME = \"lb-request\";\n\n// Names what an interaction asks for.\n//\n// One attribute name, one wire field, one set of values: the request field\n// `action` carries this attribute's value verbatim, and nothing is\n// translated in between. On a native element the hub turns the element's own\n// event into the request — a form submits, anything else clicks; on a\n// widget, the widget builds it from whatever interaction it owns.\nexport const ATTR_ACTION = \"lb-action\";\n\n// --- Reserved names ----------------------------------------------------\n// Loadbare owns every value beginning with this prefix, in every lb-*\n// attribute: a name so prefixed is the hub's own (NAV_ROW), and an\n// lb-action so prefixed is an operation (below). The server refuses a page\n// that declares a query or an action beginning with it, so a new reserved\n// name can never collide with one an application already uses.\n//\n// It is also the whole of the wire discriminant. A request whose `action`\n// begins with it is one of the three operations; anything else is a name the\n// page declared under its requests. Nothing else tells them apart.\nexport const LB_RESERVED_PREFIX = \"lb-\";\n\n// On a <form> in a list scope: builds a new row on submit. The hub gathers\n// every lb-cell inside the form into a values map — no key, because there is\n// no row yet — and the form's own lb-list says which list it is inserting\n// into.\nexport const ACTION_ROW_INSERT = \"lb-row-insert\";\n\n// Deletes the row the element is inside. Needs no declared name: it takes\n// only the row's own lb-list/lb-key-value binding, which is all the server\n// needs to know which row and whether the operation is permitted on it.\nexport const ACTION_ROW_DELETE = \"lb-row-delete\";\n\n// Commits cells of the row the element is inside. From a <form> or a button,\n// on submit or click, the hub gathers the same values map as an insert, plus\n// the row's own lb-key-value: a form that is a list's row template root, or\n// sits inside one, has both from its ancestors exactly as a delete button\n// does.\n//\n// From an element carrying lb-cell — a widget wrapping one control — the\n// values map holds that one cell and nothing else, the way a control has a\n// value and a form has values. The widget decides when a change is made and\n// sends the action with its value; without one, the hub reads the control\n// the widget wraps. SQL has one UPDATE whether it sets one column or many,\n// and so there is one operation here.\nexport const ACTION_ROW_UPDATE = \"lb-row-update\";\n\n// Every reserved lb-action, in one place, so the builder can refuse an\n// unknown one and the hub can refuse to send one it does not implement.\nexport const LB_ACTIONS = [\n ACTION_ROW_INSERT,\n ACTION_ROW_DELETE,\n ACTION_ROW_UPDATE,\n] as const;\n\n// --- Expansion (docs/reference/custom-elements.md) ---------------------\n// Marks the element in a definition whose children receive the authored\n// inner content. An attribute rather than an element because HTML content\n// models discard foreign elements inside <select> and <table>.\nexport const ATTR_SLOT = \"lb-slot\";\n\n// One name in two positions. In a definition it marks a destination; on an\n// authored <template> it names the destination that template is for, and the\n// template is unwrapped into it.\n//\n// It exists because the parser, not the content model, is the obstacle. A\n// <thead> written inside <lb-table> on a page is not in a table, so the\n// tokenizer drops the tag and keeps the text. A <template> survives anywhere\n// and its contents parse as if they were in a table, so wrapping the markup\n// is what gets it to expansion intact — and unwrapping it is what puts real\n// elements in the shipped document.\nexport const ATTR_TEMPLATE = \"lb-template\";\n\n// --- Host channel (docs/reference/chrome.md) ---------------------------\n// Only anchors carrying this are intercepted, so external links are never\n// hijacked.\nexport const ATTR_NAV_LINK = \"lb-nav-link\";\n\n// A page host ships as <template lb-page=\"<name>\">. An attribute rather than\n// an id: an id lives in the application's own namespace, where a page named\n// for something the chrome also ids would collide, and getElementById()\n// answers a collision by silently picking one.\nexport const ATTR_PAGE = \"lb-page\";\n\n// The hub's own row. Landed on every navigation, on any subtree inside the\n// hub that names it, through the same applyData() a server answer goes\n// through — so a chrome displays where the page is the way it displays\n// anything else. One row rather than a list, so a subtree binds it with\n// lb-row. A reserved name (LB_RESERVED_PREFIX): the server refuses to\n// declare a query so named.\nexport const NAV_ROW = \"lb-navigation\";\n\n// Its two columns. The label is the nav's: the text of the lb-nav-link\n// anchor whose href names the current page, and empty when no anchor does.\n// The URI is the path as the browser has it.\nexport const NAV_CELL_LABEL = \"page-label\";\nexport const NAV_CELL_URI = \"page-uri\";\n\n// Marks the chrome's own <dialog> for a page name that resolves to no\n// template — a routing miss discovered client-side, not an HTTP 404 (every\n// route gets the same 200 response; see docs/tutorials/010-pages-and-navigation.md).\n// The hub only opens it; what it says comes from NAV_ROW, which the dialog\n// consumes like any other subtree, by naming it in lb-row. Optional: an\n// application that declares none gets today's console.error and nothing\n// more. The builder requires the element it's on to be a <dialog>\n// (build/assemble.ts), since the hub calls showModal() on it.\nexport const ATTR_UNKNOWN_PAGE = \"lb-unknown-page\";\n\n// The hub's own tag. It is exempt from the rule every other LB-* tag\n// follows: expansion does not require it to have a definition (build/expand.ts),\n// and the client build does not require it to have a script of its own\n// (build/elements.ts) — it is the framework's live element, always present,\n// registered once by the package's main entry point, not a widget an\n// application discovers or declares.\nexport const HUB_TAG_NAME = \"lb-hub\";\n\n// --- Request lifecycle --------------------------------------------------\n// Stamped on the element that dispatched a request — the button, form, or\n// widget itself — for the duration of its round trip. It carries no\n// meaning beyond \"in flight\": a stylesheet dims the element, or a custom\n// button widget watches it to disable itself. The hub sets and clears it,\n// alongside aria-busy, and reads it in one place: a native action (a button\n// or form, not a widget) still pending ignores being performed again.\nexport const ATTR_PENDING = \"lb-pending\";\n\n// Stamped on the same element if that round trip failed — a non-2xx\n// response, a thrown application error, or a timeout, all alike. Cleared at\n// the start of that element's next request. What (if anything) an\n// application shows for it is undecided; this is only the fact that it\n// happened.\nexport const ATTR_ERROR = \"lb-error\";\n\n// --- Wire --------------------------------------------------------------\n// One path, one method: everything the hub asks of the server is a POST to\n// this path. Navigation is therefore the only GET an application serves,\n// which is what leaves the page name space unrestricted.\nexport const LB_ENDPOINT = \"/lb\";\n\n// A client-side deadline for one round trip. fetch() has no deadline of its\n// own; past this the request is aborted and treated as a failure.\nexport const LB_REQUEST_TIMEOUT_MS = 10_000;\n"]}
1
+ {"version":3,"file":"lb-constants.js","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"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","sourcesContent":["// Every hub attribute name lives here. Nothing writes one as a string\n// literal — not a widget, not a page host, not a test. See\n// docs/reference/data-binding.md.\n\n// --- Binding (docs/reference/data-binding.md) ---------------------------\n// One containment ladder: a list holds rows, a row holds cells. Both scope\n// attributes take a name the server declares, and a nested one of either\n// kind begins a new scope.\n\n// Scopes DOM children to a named set of rows.\nexport const ATTR_LIST = \"lb-list\";\n\n// Scopes DOM children to one named row.\n//\n// A single-row scope is read-only. Every write operation needs a key, and a\n// key exists only on a live row the hub stamped inside a list, so a declared\n// action is the only thing such a scope can send. An application that wants\n// a writable single row declares a list that answers with one row.\nexport const ATTR_ROW = \"lb-row\";\n\n// Named on a row template by the developer: the column that identifies a row\n// of the list. A property of the list rather than of the template — a list\n// without a fixed key column is meaningless — but written where the rows\n// land, because that is the only place the page says anything about them.\nexport const ATTR_KEY = \"lb-key\";\n\n// Stamped on a live row by the row machinery: that row's value of the column\n// ATTR_KEY names. Written by the hub, never by a developer, and read back by\n// whatever needs to know which row it is inside — a delete button, an update\n// form, a widget building its own request, or a stylesheet.\nexport const ATTR_KEY_VALUE = \"lb-key-value\";\n\n// The column an element displays.\n//\n// Its value is a column name and never a cell name. A cell has no name of\n// its own: it is identified by its row and its column, and the row arrives\n// from scope, so the column is all that is left to say. ATTR_KEY holds a\n// column name for the same reason.\nexport const ATTR_CELL = \"lb-cell\";\n\n// The column that decides whether an element is present: `null` and `false`\n// are off, and every other value is on. It binds by the same ancestor rule as\n// ATTR_CELL, and names a column for the same reason.\n//\n// An element that is off is moved into a `<template>` carrying this attribute\n// and the same column, standing where the element stood, and moved back out\n// when the column turns on. The builder ships every such element already\n// inside its template. A developer writes this attribute on an element and\n// never on a template: the template form is Loadbare's, and holds the absent\n// branch in the document so the hub holds nothing. Landing reaches inside it,\n// so a branch that returns is already current; nothing else does.\nexport const ATTR_SHOW = \"lb-show\";\n\nexport const ATTR_VALUE = \"lb-value\";\n\n// Stamped on a list scope by the shared row machinery: how many rows it is\n// showing. Written by the hub and never bound to — it is derived from rows\n// the hub already knows, exactly as the section heading in lb-table is. A\n// list widget watches it, and it exists so that \"no members yet\" is a\n// stylesheet rule rather than a conditional in every list widget.\nexport const ATTR_ROW_COUNT = \"lb-row-count\";\n\n// --- Requests (docs/reference/data-binding.md) --------------------------\n// One event name, used by a widget reaching the server and by a widget\n// reaching an ancestor widget.\nexport const LB_EVENT_NAME = \"lb-request\";\n\n// Names what an interaction asks for.\n//\n// One attribute name, one wire field, one set of values: the request field\n// `action` carries this attribute's value verbatim, and nothing is\n// translated in between. On a native element the hub turns the element's own\n// event into the request — a form submits, anything else clicks; on a\n// widget, the widget builds it from whatever interaction it owns.\nexport const ATTR_ACTION = \"lb-action\";\n\n// --- Reserved names ----------------------------------------------------\n// Loadbare owns every value beginning with this prefix, in every lb-*\n// attribute: a name so prefixed is the hub's own (NAV_ROW), and an\n// lb-action so prefixed is an operation (below). The server refuses a page\n// that declares a query or an action beginning with it, so a new reserved\n// name can never collide with one an application already uses.\n//\n// It is also the whole of the wire discriminant. A request whose `action`\n// begins with it is one of the three operations; anything else is a name the\n// page declared under its requests. Nothing else tells them apart.\nexport const LB_RESERVED_PREFIX = \"lb-\";\n\n// On a <form> in a list scope: builds a new row on submit. The hub gathers\n// every lb-cell inside the form into a values map — no key, because there is\n// no row yet — and the form's own lb-list says which list it is inserting\n// into.\nexport const ACTION_ROW_INSERT = \"lb-row-insert\";\n\n// Deletes the row the element is inside. Needs no declared name: it takes\n// only the row's own lb-list/lb-key-value binding, which is all the server\n// needs to know which row and whether the operation is permitted on it.\nexport const ACTION_ROW_DELETE = \"lb-row-delete\";\n\n// Commits cells of the row the element is inside. From a <form> or a button,\n// on submit or click, the hub gathers the same values map as an insert, plus\n// the row's own lb-key-value: a form that is a list's row template root, or\n// sits inside one, has both from its ancestors exactly as a delete button\n// does.\n//\n// From an element carrying lb-cell — a widget wrapping one control — the\n// values map holds that one cell and nothing else, the way a control has a\n// value and a form has values. The widget decides when a change is made and\n// sends the action with its value; without one, the hub reads the control\n// the widget wraps. SQL has one UPDATE whether it sets one column or many,\n// and so there is one operation here.\nexport const ACTION_ROW_UPDATE = \"lb-row-update\";\n\n// Every reserved lb-action, in one place, so the builder can refuse an\n// unknown one and the hub can refuse to send one it does not implement.\nexport const LB_ACTIONS = [\n ACTION_ROW_INSERT,\n ACTION_ROW_DELETE,\n ACTION_ROW_UPDATE,\n] as const;\n\n// --- Expansion (docs/reference/custom-elements.md) ---------------------\n// Marks the element in a definition whose children receive the authored\n// inner content. An attribute rather than an element because HTML content\n// models discard foreign elements inside <select> and <table>.\nexport const ATTR_SLOT = \"lb-slot\";\n\n// One name in two positions. In a definition it marks a destination; on an\n// authored <template> it names the destination that template is for, and the\n// template is unwrapped into it.\n//\n// It exists because the parser, not the content model, is the obstacle. A\n// <thead> written inside <lb-table> on a page is not in a table, so the\n// tokenizer drops the tag and keeps the text. A <template> survives anywhere\n// and its contents parse as if they were in a table, so wrapping the markup\n// is what gets it to expansion intact — and unwrapping it is what puts real\n// elements in the shipped document.\nexport const ATTR_TEMPLATE = \"lb-template\";\n\n// --- Host channel (docs/reference/chrome.md) ---------------------------\n// Only anchors carrying this are intercepted, so external links are never\n// hijacked.\nexport const ATTR_NAV_LINK = \"lb-nav-link\";\n\n// Names the query parm a control writes. The query string describes what is\n// on screen and never commands, so a control that narrows the page writes its\n// value into the address bar rather than sending it: the hub sets the one\n// parm, or deletes it when the value is empty, and loads the page at the new\n// URL. Every other parm is left as it is. Landed back onto the control from\n// the URL on every load, so a reload, a mailed link and Back all show it.\nexport const ATTR_QUERY_PARM = \"lb-query-parm\";\n\n// Changing what the current page shows is not going anywhere, so a query parm\n// write replaces the history entry. A control carrying this pushes one\n// instead, for the rare parm a user would expect Back to undo.\nexport const ATTR_QUERY_PARM_PUSH = \"lb-query-parm-push\";\n\n// A page host ships as <template lb-page=\"<name>\">. An attribute rather than\n// an id: an id lives in the application's own namespace, where a page named\n// for something the chrome also ids would collide, and getElementById()\n// answers a collision by silently picking one.\nexport const ATTR_PAGE = \"lb-page\";\n\n// The hub's own row. Landed on every navigation, on any subtree inside the\n// hub that names it, through the same applyData() a server answer goes\n// through — so a chrome displays where the page is the way it displays\n// anything else. One row rather than a list, so a subtree binds it with\n// lb-row. A reserved name (LB_RESERVED_PREFIX): the server refuses to\n// declare a query so named.\nexport const NAV_ROW = \"lb-navigation\";\n\n// Its two columns. The label is the nav's: the text of the lb-nav-link\n// anchor whose href names the current page, and empty when no anchor does.\n// The URI is the path and query string as the browser has them.\nexport const NAV_CELL_LABEL = \"page-label\";\nexport const NAV_CELL_URI = \"page-uri\";\n\n// Marks the chrome's own <dialog> for a page name that resolves to no\n// template — a routing miss discovered client-side, not an HTTP 404 (every\n// route gets the same 200 response; see docs/tutorials/010-pages-and-navigation.md).\n// The hub only opens it; what it says comes from NAV_ROW, which the dialog\n// consumes like any other subtree, by naming it in lb-row. Optional: an\n// application that declares none gets today's console.error and nothing\n// more. The builder requires the element it's on to be a <dialog>\n// (build/assemble.ts), since the hub calls showModal() on it.\nexport const ATTR_UNKNOWN_PAGE = \"lb-unknown-page\";\n\n// The hub's own tag. It is exempt from the rule every other LB-* tag\n// follows: expansion does not require it to have a definition (build/expand.ts),\n// and the client build does not require it to have a script of its own\n// (build/elements.ts) — it is the framework's live element, always present,\n// registered once by the package's main entry point, not a widget an\n// application discovers or declares.\nexport const HUB_TAG_NAME = \"lb-hub\";\n\n// --- Request lifecycle --------------------------------------------------\n// Stamped on the element that dispatched a request — the button, form, or\n// widget itself — for the duration of its round trip. It carries no\n// meaning beyond \"in flight\": a stylesheet dims the element, or a custom\n// button widget watches it to disable itself. The hub sets and clears it,\n// alongside aria-busy, and reads it in one place: a native action (a button\n// or form, not a widget) still pending ignores being performed again.\nexport const ATTR_PENDING = \"lb-pending\";\n\n// Stamped on the same element if that round trip failed — a non-2xx\n// response, a thrown application error, or a timeout, all alike. Cleared at\n// the start of that element's next request. What (if anything) an\n// application shows for it is undecided; this is only the fact that it\n// happened.\nexport const ATTR_ERROR = \"lb-error\";\n\n// --- Wire --------------------------------------------------------------\n// One path, one method: everything the hub asks of the server is a POST\n// below this path. The page name follows it, and the query string the\n// browser is showing rides after that verbatim, so `/accounts?acct=23` asks\n// as `POST /lb/accounts?acct=23` and the server always knows what URL the\n// browser is showing. Navigation is therefore the only GET an application\n// serves, which is what leaves the page name space unrestricted.\nexport const LB_ENDPOINT = \"/lb\";\n\n// A client-side deadline for one round trip. fetch() has no deadline of its\n// own; past this the request is aborted and treated as a failure.\nexport const LB_REQUEST_TIMEOUT_MS = 10_000;\n"]}
@@ -40,4 +40,14 @@ export declare function applyRow(root: Element, row: Row): void;
40
40
  export declare function applyList(scope: Element, result: ListResult): void;
41
41
  /** Land a whole response. Every result arrives through here. */
42
42
  export declare function applyData(root: ParentNode, data: HubData): void;
43
+ /**
44
+ * Land the query string on every control that writes one of its parms, the
45
+ * way a cell lands a column. A parm the URL does not carry lands empty, so a
46
+ * control shows what the address bar says after Back as well as after a
47
+ * reload: the URL is what is on screen, and nothing else is.
48
+ *
49
+ * Landed after the page's data, so a picker already has the options its
50
+ * value selects.
51
+ */
52
+ export declare function applyQueryParms(root: ParentNode, parms: URLSearchParams): void;
43
53
  //# sourceMappingURL=lb-apply.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"AAkCA,OAAO,EAEL,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,KAAK,GAAG,EACT,MAAM,qBAAqB,CAAC;AAW7B;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,OAAO,GACV,EAAE,IAAI,gBAAgB,GAAG,iBAAiB,GAAG,mBAAmB,CAMlE;AA8DD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAMjE;AAqED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAetD;AA6CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAgElE;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAkC/D"}
1
+ {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"AAmCA,OAAO,EAEL,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,KAAK,GAAG,EACT,MAAM,qBAAqB,CAAC;AAW7B;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,OAAO,GACV,EAAE,IAAI,gBAAgB,GAAG,iBAAiB,GAAG,mBAAmB,CAMlE;AA8DD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAMjE;AAqED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAetD;AA6CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAgElE;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAkC/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,eAAe,GACrB,IAAI,CAIN"}
@@ -20,7 +20,7 @@
20
20
  * landing reaches in there as it reaches everywhere else, so a branch that
21
21
  * returns is already current. Nothing but landing reaches in.
22
22
  */
23
- import { ATTR_CELL, ATTR_KEY, ATTR_KEY_VALUE, ATTR_LIST, ATTR_ROW, ATTR_ROW_COUNT, ATTR_SHOW, ATTR_VALUE, } from "../core/lb-constants.js";
23
+ import { ATTR_CELL, ATTR_KEY, ATTR_KEY_VALUE, ATTR_LIST, ATTR_QUERY_PARM, ATTR_ROW, ATTR_ROW_COUNT, ATTR_SHOW, ATTR_VALUE, } from "../core/lb-constants.js";
24
24
  import { isPatch, } from "../core/lb-types.js";
25
25
  /** The template holding an absent branch — see ATTR_SHOW. */
26
26
  const PARKED = `template[${ATTR_SHOW}]`;
@@ -358,4 +358,18 @@ export function applyData(root, data) {
358
358
  applyRow(scope, result);
359
359
  }
360
360
  }
361
+ /**
362
+ * Land the query string on every control that writes one of its parms, the
363
+ * way a cell lands a column. A parm the URL does not carry lands empty, so a
364
+ * control shows what the address bar says after Back as well as after a
365
+ * reload: the URL is what is on screen, and nothing else is.
366
+ *
367
+ * Landed after the page's data, so a picker already has the options its
368
+ * value selects.
369
+ */
370
+ export function applyQueryParms(root, parms) {
371
+ for (const el of everywhere(root, `[${ATTR_QUERY_PARM}]`)) {
372
+ land(el, parms.get(el.getAttribute(ATTR_QUERY_PARM)) ?? "");
373
+ }
374
+ }
361
375
  //# sourceMappingURL=lb-apply.js.map