@boopkit/cap-web 0.5.1 → 0.5.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/README.md CHANGED
@@ -43,6 +43,8 @@ app.use(pageRoutes); // order 50 — mount the example pages
43
43
 
44
44
  Order numbers are the `preRoutes` phase ordering; other capabilities slot themselves around these (for example, `cap-api`'s error handler mounts after route handlers).
45
45
 
46
+ `configureViewEngine(app, options?)` takes `viewsDir`, `layoutsDir`, `defaultLayout`, and `compilerOptions` (forwarded to Handlebars). It defaults `preventIndent: true` so a standalone partial's call-site indentation is never applied to interpolated values — which would otherwise corrupt user text inside `<textarea>`/`<pre>`. Override any Handlebars compile option via `compilerOptions`, e.g. `configureViewEngine(app, { compilerOptions: { preventIndent: false } })`.
47
+
46
48
  ### Skills installed
47
49
 
48
50
  - `design.web-ui-design.skill.md` — teaches agents the semantic-CSS convention and view-rendering patterns used in boop web apps.
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAyBA;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAyE3D,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAyBA;;GAEG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAiG3D,CAAC"}
package/dist/helpers.js CHANGED
@@ -43,6 +43,28 @@ exports.helpers = {
43
43
  return str;
44
44
  return str.slice(0, len) + '…';
45
45
  },
46
+ /**
47
+ * URL-encode a value for a query parameter: {{urlencode next}}
48
+ *
49
+ * Handlebars escapes an interpolated value for HTML, which is not the same as
50
+ * escaping it for a URL. `href="?next={{next}}"` with a return path of
51
+ * `/a&b=c` renders `next=/a&amp;b&#x3D;c`, and the browser reads that as
52
+ * `next=/a` plus a stray `b=c` — the visitor lands on the wrong page. Any
53
+ * template that interpolates a value into a query string needs this helper.
54
+ *
55
+ * This *encodes*; it does not sanitise. Deciding whether a return path is
56
+ * acceptable belongs to whatever gates it (cap-oauth-client's
57
+ * `validateReturnPath`, say) — stripping characters here would put a second,
58
+ * quietly divergent copy of that judgement in the view layer.
59
+ *
60
+ * The result is deliberately not a SafeString: `encodeURIComponent` leaves
61
+ * only alphanumerics and `-_.!~*'()%`, of which Handlebars escapes just the
62
+ * apostrophe, and `&#x27;` decodes back to `'` before the browser parses the
63
+ * URL. So the value round-trips, and the attribute keeps its HTML escaping.
64
+ */
65
+ urlencode: (value) => typeof value === 'string' || typeof value === 'number'
66
+ ? encodeURIComponent(String(value))
67
+ : '',
46
68
  /** Uppercase: {{upper text}} */
47
69
  upper: (str) => (typeof str === 'string' ? str.toUpperCase() : ''),
48
70
  /** Lowercase: {{lower text}} */
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,12 @@ export interface ViewEngineOptions {
6
6
  layoutsDir?: string;
7
7
  /** Name of the default layout file (without extension). Defaults to `'main'`. */
8
8
  defaultLayout?: string;
9
+ /**
10
+ * Handlebars compiler options, forwarded to express-handlebars (and on to
11
+ * `Handlebars.compile`). Merged over the defaults below, so any key you set
12
+ * here wins — including turning `preventIndent` back off if you need it.
13
+ */
14
+ compilerOptions?: CompileOptions;
9
15
  }
10
16
  /**
11
17
  * Configure Handlebars as the view engine for an Express app.
@@ -1 +1 @@
1
- {"version":3,"file":"view-engine.d.ts","sourceRoot":"","sources":["../src/view-engine.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOvC,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAkBvF"}
1
+ {"version":3,"file":"view-engine.d.ts","sourceRoot":"","sources":["../src/view-engine.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOvC,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,cAAc,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CA4BvF"}
@@ -19,12 +19,21 @@ const helpers_1 = require("./helpers");
19
19
  * string, math, array, etc.) plus custom formatting helpers.
20
20
  */
21
21
  function configureViewEngine(app, options = {}) {
22
- const { viewsDir = path_1.default.join(process.cwd(), 'views'), layoutsDir = path_1.default.join(process.cwd(), 'views', 'layouts'), defaultLayout = 'main', } = options;
22
+ const { viewsDir = path_1.default.join(process.cwd(), 'views'), layoutsDir = path_1.default.join(process.cwd(), 'views', 'layouts'), defaultLayout = 'main', compilerOptions, } = options;
23
23
  app.engine('hbs', (0, express_handlebars_1.engine)({
24
24
  extname: '.hbs',
25
25
  helpers: helpers_1.helpers,
26
26
  layoutsDir,
27
27
  defaultLayout,
28
+ // Default `preventIndent: true`. Handlebars otherwise indents a standalone
29
+ // partial's output by the indentation of its call site, applied to every
30
+ // line of the rendered output — including interpolated values. That is
31
+ // harmless in collapsible markup, but inside a whitespace-significant
32
+ // element (<textarea>, <pre>) it corrupts the user's text, and once the
33
+ // form posts that value back the corruption is stored. Partial indentation
34
+ // only tidied generated HTML that nothing consumes, so opt every consumer
35
+ // out by default; a caller can override via `compilerOptions`. (boop-rv7)
36
+ compilerOptions: { preventIndent: true, ...compilerOptions },
28
37
  }));
29
38
  app.set('view engine', 'hbs');
30
39
  app.set('views', viewsDir);
@@ -1 +1 @@
1
- {"version":3,"file":"view-engine.js","sourceRoot":"","sources":["../src/view-engine.ts"],"names":[],"mappings":";;;;;AA0BA,kDAkBC;AA5CD,gDAAwB;AACxB,4DAAoC;AACpC,2DAA4C;AAE5C,4EAAmD;AACnD,uCAAoC;AAEpC,iFAAiF;AACjF,IAAA,4BAAiB,EAAC,EAAE,UAAU,EAAE,oBAAU,EAAE,CAAC,CAAC;AAW9C;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,GAAY,EAAE,UAA6B,EAAE;IAC/E,MAAM,EACJ,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,EAC5C,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,EACzD,aAAa,GAAG,MAAM,GACvB,GAAG,OAAO,CAAC;IAEZ,GAAG,CAAC,MAAM,CACR,KAAK,EACL,IAAA,2BAAM,EAAC;QACL,OAAO,EAAE,MAAM;QACf,OAAO,EAAP,iBAAO;QACP,UAAU;QACV,aAAa;KACd,CAAC,CACH,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"view-engine.js","sourceRoot":"","sources":["../src/view-engine.ts"],"names":[],"mappings":";;;;;AAgCA,kDA4BC;AA5DD,gDAAwB;AACxB,4DAAoC;AACpC,2DAA4C;AAE5C,4EAAmD;AACnD,uCAAoC;AAEpC,iFAAiF;AACjF,IAAA,4BAAiB,EAAC,EAAE,UAAU,EAAE,oBAAU,EAAE,CAAC,CAAC;AAiB9C;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,GAAY,EAAE,UAA6B,EAAE;IAC/E,MAAM,EACJ,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,EAC5C,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,EACzD,aAAa,GAAG,MAAM,EACtB,eAAe,GAChB,GAAG,OAAO,CAAC;IAEZ,GAAG,CAAC,MAAM,CACR,KAAK,EACL,IAAA,2BAAM,EAAC;QACL,OAAO,EAAE,MAAM;QACf,OAAO,EAAP,iBAAO;QACP,UAAU;QACV,aAAa;QACb,2EAA2E;QAC3E,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,2EAA2E;QAC3E,0EAA0E;QAC1E,0EAA0E;QAC1E,eAAe,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,eAAe,EAAE;KAC7D,CAAC,CACH,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boopkit/cap-web",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Express view engine configuration, Handlebars helpers, and static asset middleware",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,6 +35,7 @@ Handlebars via `express-handlebars`. Templates in `views/`, layouts in `views/la
35
35
  |--------|---------|
36
36
  | `json` | JSON stringify with formatting |
37
37
  | `truncate` | Truncate string with ellipsis |
38
+ | `urlencode` | Percent-encode a value for a query parameter: `href="/signup?next={{urlencode next}}"`. Handlebars escapes for HTML, not for a URL, so a value interpolated straight into a query string is truncated at its first `&`. |
38
39
  | `fmt_dollars` | Currency formatting |
39
40
  | `fmt_percent` | Percentage formatting |
40
41
  | `fmt_date` | Human-readable date (e.g., "March 28, 2026") |
@@ -96,6 +96,7 @@ When considering whether to reuse existing components:
96
96
  - `icon` — inline SVG icon from heroicons: `{{icon "user" "24/outline"}}` or with CSS class: `{{icon "check" "24/solid" class="w-5 h-5 text-green-600"}}`. Styles: `24/outline`, `24/solid`, `20/solid`, `16/solid`. Icons are read from `node_modules/heroicons/` and cached.
97
97
  - `json` — JSON stringify: `{{json obj}}`
98
98
  - `truncate` — truncate string: `{{truncate text 100}}`
99
+ - `urlencode` — percent-encode a value for a query parameter: `<a href="/signup?next={{urlencode next}}">`. Handlebars escapes an interpolated value for HTML, not for a URL, so `?next={{next}}` renders a return path of `/a&b=c` as `next=/a&amp;b&#x3D;c` and the browser reads `next=/a`. Use this helper for **every** value that lands in a query string; it encodes rather than validates, so whatever gates the value keeps doing that job.
99
100
  - `upper` / `lower` — case conversion
100
101
  - `eq` — strict equality check (also available from handlebars-helpers)
101
102