@warlock.js/core 5.15.0 → 5.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/esm/cli/commands/migrate.command.mjs +5 -1
  3. package/esm/cli/commands/migrate.command.mjs.map +1 -1
  4. package/esm/cli/commands/seed.command.mjs +2 -1
  5. package/esm/cli/commands/seed.command.mjs.map +1 -1
  6. package/esm/cli/commands/typings-generator.command.mjs +16 -23
  7. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  8. package/esm/config/locale-configuration.mjs +7 -1
  9. package/esm/config/locale-configuration.mjs.map +1 -1
  10. package/esm/connectors/http-connector.d.mts.map +1 -1
  11. package/esm/connectors/http-connector.mjs +1 -18
  12. package/esm/connectors/http-connector.mjs.map +1 -1
  13. package/esm/connectors/read-bound-port.mjs +32 -0
  14. package/esm/connectors/read-bound-port.mjs.map +1 -0
  15. package/esm/dev-server/development-server.mjs +5 -1
  16. package/esm/dev-server/development-server.mjs.map +1 -1
  17. package/esm/dev-server/extract-translation-keys.mjs +68 -0
  18. package/esm/dev-server/extract-translation-keys.mjs.map +1 -0
  19. package/esm/dev-server/translation-keys-sources.mjs +19 -0
  20. package/esm/dev-server/translation-keys-sources.mjs.map +1 -0
  21. package/esm/dev-server/type-generator.mjs +64 -11
  22. package/esm/dev-server/type-generator.mjs.map +1 -1
  23. package/esm/generations/features/shared/merge-web-sitemap-config.mjs +118 -0
  24. package/esm/generations/features/shared/merge-web-sitemap-config.mjs.map +1 -0
  25. package/esm/generations/features/sitemap.feature.mjs +50 -47
  26. package/esm/generations/features/sitemap.feature.mjs.map +1 -1
  27. package/esm/generations/stubs.mjs +7 -6
  28. package/esm/generations/stubs.mjs.map +1 -1
  29. package/esm/http/csrf-default-guard.d.mts +32 -0
  30. package/esm/http/csrf-default-guard.d.mts.map +1 -0
  31. package/esm/http/csrf-default-guard.mjs +156 -0
  32. package/esm/http/csrf-default-guard.mjs.map +1 -0
  33. package/esm/http/csrf-origin-policy.d.mts +53 -0
  34. package/esm/http/csrf-origin-policy.d.mts.map +1 -0
  35. package/esm/http/csrf-origin-policy.mjs +104 -0
  36. package/esm/http/csrf-origin-policy.mjs.map +1 -0
  37. package/esm/http/error-codes.d.mts +14 -1
  38. package/esm/http/error-codes.d.mts.map +1 -1
  39. package/esm/http/error-codes.mjs +13 -0
  40. package/esm/http/error-codes.mjs.map +1 -1
  41. package/esm/http/index.d.mts +8 -1
  42. package/esm/http/index.mjs +7 -1
  43. package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
  44. package/esm/http/middleware/idempotency.middleware.mjs +1 -1
  45. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  46. package/esm/http/middleware/inject-request-context.mjs +6 -0
  47. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  48. package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
  49. package/esm/http/middleware/maintenance.middleware.mjs +1 -1
  50. package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
  51. package/esm/http/middleware/rate-limit.middleware.mjs +1 -1
  52. package/esm/http/parse-urlencoded-body.mjs +27 -0
  53. package/esm/http/parse-urlencoded-body.mjs.map +1 -0
  54. package/esm/http/plugins.d.mts.map +1 -1
  55. package/esm/http/plugins.mjs +8 -0
  56. package/esm/http/plugins.mjs.map +1 -1
  57. package/esm/http/request.d.mts +2 -2
  58. package/esm/http/request.d.mts.map +1 -1
  59. package/esm/http/request.mjs +7 -3
  60. package/esm/http/request.mjs.map +1 -1
  61. package/esm/http/response.d.mts +9 -2
  62. package/esm/http/response.d.mts.map +1 -1
  63. package/esm/http/response.mjs +18 -4
  64. package/esm/http/response.mjs.map +1 -1
  65. package/esm/http/uploads/detect-image-format.mjs +59 -0
  66. package/esm/http/uploads/detect-image-format.mjs.map +1 -0
  67. package/esm/http/uploads/generate-image-variant.mjs +46 -0
  68. package/esm/http/uploads/generate-image-variant.mjs.map +1 -0
  69. package/esm/http/uploads/generate-image-variants.d.mts +49 -0
  70. package/esm/http/uploads/generate-image-variants.d.mts.map +1 -0
  71. package/esm/http/uploads/generate-image-variants.mjs +141 -0
  72. package/esm/http/uploads/generate-image-variants.mjs.map +1 -0
  73. package/esm/http/uploads/image-variant-types.d.mts +93 -0
  74. package/esm/http/uploads/image-variant-types.d.mts.map +1 -0
  75. package/esm/http/uploads/image-variants-config-error.d.mts +13 -0
  76. package/esm/http/uploads/image-variants-config-error.d.mts.map +1 -0
  77. package/esm/http/uploads/image-variants-config-error.mjs +17 -0
  78. package/esm/http/uploads/image-variants-config-error.mjs.map +1 -0
  79. package/esm/http/uploads/index.d.mts +4 -0
  80. package/esm/http/uploads/index.mjs +5 -0
  81. package/esm/http/uploads/is-path-inside.mjs +26 -0
  82. package/esm/http/uploads/is-path-inside.mjs.map +1 -0
  83. package/esm/http/uploads/load-variant-source.mjs +42 -0
  84. package/esm/http/uploads/load-variant-source.mjs.map +1 -0
  85. package/esm/http/uploads/matches-if-none-match.mjs +13 -0
  86. package/esm/http/uploads/matches-if-none-match.mjs.map +1 -0
  87. package/esm/http/uploads/parse-uploaded-file-query.mjs +51 -0
  88. package/esm/http/uploads/parse-uploaded-file-query.mjs.map +1 -0
  89. package/esm/http/uploads/read-file-head.mjs +20 -0
  90. package/esm/http/uploads/read-file-head.mjs.map +1 -0
  91. package/esm/http/uploads/resolve-image-variants-config.mjs +91 -0
  92. package/esm/http/uploads/resolve-image-variants-config.mjs.map +1 -0
  93. package/esm/http/uploads/resolve-inline-image-content-type.mjs +53 -0
  94. package/esm/http/uploads/resolve-inline-image-content-type.mjs.map +1 -0
  95. package/esm/http/uploads/resolve-original-content-type.mjs +31 -0
  96. package/esm/http/uploads/resolve-original-content-type.mjs.map +1 -0
  97. package/esm/http/uploads/resolve-upload-path.mjs +56 -0
  98. package/esm/http/uploads/resolve-upload-path.mjs.map +1 -0
  99. package/esm/http/uploads/resolve-variant-candidate.mjs +39 -0
  100. package/esm/http/uploads/resolve-variant-candidate.mjs.map +1 -0
  101. package/esm/http/uploads/single-flight.mjs +40 -0
  102. package/esm/http/uploads/single-flight.mjs.map +1 -0
  103. package/esm/http/uploads/uploaded-file.controller.d.mts +24 -0
  104. package/esm/http/uploads/uploaded-file.controller.d.mts.map +1 -0
  105. package/esm/http/uploads/uploaded-file.controller.mjs +174 -0
  106. package/esm/http/uploads/uploaded-file.controller.mjs.map +1 -0
  107. package/esm/http/uploads/variant-cache-key.mjs +45 -0
  108. package/esm/http/uploads/variant-cache-key.mjs.map +1 -0
  109. package/esm/http/uploads/variant-generations.mjs +16 -0
  110. package/esm/http/uploads/variant-generations.mjs.map +1 -0
  111. package/esm/http/uploads-types.d.mts +102 -1
  112. package/esm/http/uploads-types.d.mts.map +1 -1
  113. package/esm/http/xmlable.d.mts +14 -0
  114. package/esm/http/xmlable.d.mts.map +1 -0
  115. package/esm/index.d.mts +10 -2
  116. package/esm/index.mjs +8 -2
  117. package/esm/router/types.d.mts +23 -0
  118. package/esm/router/types.d.mts.map +1 -1
  119. package/esm/storage/index.d.mts +1 -0
  120. package/esm/storage/index.mjs +1 -0
  121. package/esm/storage/scoped-storage.d.mts +1 -0
  122. package/esm/storage/scoped-storage.d.mts.map +1 -1
  123. package/esm/storage/scoped-storage.mjs +3 -0
  124. package/esm/storage/scoped-storage.mjs.map +1 -1
  125. package/esm/storage/storage.d.mts +2 -0
  126. package/esm/storage/storage.d.mts.map +1 -1
  127. package/esm/storage/storage.mjs +4 -0
  128. package/esm/storage/storage.mjs.map +1 -1
  129. package/esm/storage/utils/storage-not-initialized-error.d.mts +22 -0
  130. package/esm/storage/utils/storage-not-initialized-error.d.mts.map +1 -0
  131. package/esm/storage/utils/storage-not-initialized-error.mjs +24 -0
  132. package/esm/storage/utils/storage-not-initialized-error.mjs.map +1 -0
  133. package/llms-full.txt +47 -7
  134. package/llms.txt +1 -1
  135. package/package.json +22 -21
  136. package/skills/configure-app/SKILL.md +6 -6
  137. package/skills/create-controller/SKILL.md +14 -0
  138. package/skills/health-checks/SKILL.md +8 -0
  139. package/skills/send-response/SKILL.md +2 -0
  140. package/skills/use-localization/SKILL.md +16 -0
  141. package/skills/use-middleware/SKILL.md +1 -1
  142. package/esm/dev-server/translation-type-generator.mjs +0 -28
  143. package/esm/dev-server/translation-type-generator.mjs.map +0 -1
@@ -27,6 +27,8 @@ GET /ready → 200 {"status":"ok","checks":{"db":true}}
27
27
  ### Config
28
28
 
29
29
  ```ts title="src/config/http.ts"
30
+ import type { HttpConfigurations } from "@warlock.js/core";
31
+
30
32
  const httpConfigurations: HttpConfigurations = {
31
33
  health: {
32
34
  enabled: true, // default; set false to remove both endpoints
@@ -34,6 +36,8 @@ const httpConfigurations: HttpConfigurations = {
34
36
  readinessPath: "/ready", // readiness path
35
37
  },
36
38
  };
39
+
40
+ export default httpConfigurations;
37
41
  ```
38
42
 
39
43
  ## Readiness checks
@@ -74,12 +78,16 @@ On SIGINT/SIGTERM the framework tears down in order: **app `onShutdown` hooks
74
78
  3. Draining is bounded by a timeout so one stuck request can't hang the deploy — after it, the server force-closes and a warning is logged.
75
79
 
76
80
  ```ts title="src/config/http.ts"
81
+ import type { HttpConfigurations } from "@warlock.js/core";
82
+
77
83
  const httpConfigurations: HttpConfigurations = {
78
84
  gracefulShutdown: {
79
85
  timeout: 10_000, // ms to wait for in-flight drain (default 10s)
80
86
  forceCloseConnections: "idle", // close idle keep-alives, let active finish (default)
81
87
  },
82
88
  };
89
+
90
+ export default httpConfigurations;
83
91
  ```
84
92
 
85
93
  `forceCloseConnections`: `"idle"` (default) closes idle keep-alive connections and lets active requests finish; `true` force-closes everything immediately; `false` waits for every connection.
@@ -84,6 +84,8 @@ return response.sendBuffer(buffer, { contentType: "image/png" });
84
84
 
85
85
  `SendFileOptions` lets you set `cacheTime`, `immutable`, `inline`, `filename` (download attachment name).
86
86
 
87
+ `response.xml(string | XMLable, statusCode?)` sends `application/xml` — pass a raw XML string, or anything with a `toXML(): string` method (structural, so core never depends on `@warlock.js/sitemap`). This is for BOUNDED values only: a raw string, or a single `@warlock.js/sitemap` `Sitemap` under the sitemaps.org 50,000-URL / 50MB ceiling. `SitemapIndex` has no `toXML()` — it is the streaming path for larger URL sets and does not fit in one response body. It writes shards plus a master index as FILES via `saveTo`/`publish`, and those files are served directly rather than passed through `xml()`. In Warlock web this is the `web.sitemap` config: `enabled`, `outputDir` (default `storagePath("sitemap")`), and `path` (default `/sitemap.xml`) — the app serves the generated files from `outputDir` at that route, it never builds a `SitemapIndex` response inline.
88
+
87
89
  ## Streams
88
90
 
89
91
  ```ts
@@ -307,3 +307,19 @@ GET /products/42?locale=ar
307
307
  - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — using `getLocalized` inside resource output for clean per-locale responses.
308
308
  - [`create-module/SKILL.md`](../create-module/SKILL.md) — the `utils/locales.ts` file is part of the generated module scaffold.
309
309
  - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — `utils/locales.ts` is auto-loaded; the suffix is mandatory.
310
+
311
+ ## Route-owned web translations (5.17)
312
+
313
+ `locales.json` applies only to descendant pages on its physical `src/web` ancestor chain. Its optional `$group` overrides the namespace; otherwise the physical directory supplies it, omitting `(group)` and `[param]` segments. Root `src/web/locales.json` has no implicit prefix. Nested objects flatten to dotted keys, and all duplicate flattened key ownership is rejected, including matching ancestor/child declarations.
314
+
315
+ ```json
316
+ {
317
+ "$group": "account",
318
+ "title": { "en": "Account", "ar": "Arabic account" },
319
+ "actions": { "save": { "en": "Save", "ar": "Arabic save" } }
320
+ }
321
+ ```
322
+
323
+ This produces `account.title` and `account.actions.save`. Keep every leaf complete for the configured locale list. The page pipeline selects an immutable snapshot for its route source and locale. `useTrans()`, `request.t()`, and `request.trans()` use that snapshot; a missing scoped key returns the key itself. With no route-locales manifest, the existing global `@mongez/localization` registry remains the legacy fallback path.
324
+
325
+ When a route-locales snapshot applies, `request.t()`, `request.trans()`, and `request.transFrom(locale, key, placeholders?)` resolve through it; without one, they retain the existing global-registry behavior. The underlying `transFromKeywords(locale, keywords, key, placeholders?, converter?)` is supplied by `@mongez/localization` 3.5.0. Error pages rebind to their own snapshot, while framework fallback uses the app/root snapshot. Development HMR add/edit/delete publishes a new snapshot, and the snapshot revision keeps page-cache entries from reusing old JSON. Generated `.warlock` typings contain the flattened route keys; do not maintain a second handwritten key union.
@@ -176,7 +176,7 @@ router.get("/analytics/summary", summaryController, {
176
176
 
177
177
  ### Tag-based invalidation
178
178
 
179
- Give it `tags` — a static list, or a function of the request — to evict the entry early with `cache.tags([...]).invalidate()` (`@warlock.js/cache`), instead of waiting out `ttl`. Mirrors `route.cache.tags` on `@warlock.js/web`'s page cache, so an API response and a page can share the same tag and be invalidated together:
179
+ Give it `tags` — a static list, or a function of the request — to evict the entry early with `cache.tags([...]).invalidate()` (`@warlock.js/cache`), instead of waiting out `ttl`. Mirrors `config.cache.tags` on `@warlock.js/web`'s page cache, so an API response and a page can share the same tag and be invalidated together:
180
180
 
181
181
  ```ts
182
182
  import { middleware } from "@warlock.js/core";
@@ -1,28 +0,0 @@
1
- import ts from "typescript";
2
-
3
- //#region ../core/src/dev-server/translation-type-generator.ts
4
- /** Extract literal lookup keys registered through groupedTranslations calls. */
5
- function extractTranslationKeys(sourceFile) {
6
- const keys = /* @__PURE__ */ new Set();
7
- const visit = (node) => {
8
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "groupedTranslations") {
9
- const [groupArgument, dictionaryArgument] = node.arguments;
10
- if (ts.isStringLiteral(groupArgument) && dictionaryArgument && ts.isObjectLiteralExpression(dictionaryArgument)) for (const property of dictionaryArgument.properties) {
11
- if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property)) continue;
12
- const key = getPropertyName(property.name);
13
- if (key !== void 0) keys.add(`${groupArgument.text}.${key}`);
14
- }
15
- }
16
- ts.forEachChild(node, visit);
17
- };
18
- ts.forEachChild(sourceFile, visit);
19
- return Array.from(keys).sort();
20
- }
21
- function getPropertyName(name) {
22
- if (name === void 0) return;
23
- if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
24
- }
25
-
26
- //#endregion
27
- export { extractTranslationKeys };
28
- //# sourceMappingURL=translation-type-generator.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"translation-type-generator.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/translation-type-generator.ts"],"sourcesContent":["import ts from \"typescript\";\n\n/** Extract literal lookup keys registered through groupedTranslations calls. */\nexport function extractTranslationKeys(sourceFile: ts.SourceFile): string[] {\n const keys = new Set<string>();\n\n const visit = (node: ts.Node): void => {\n if (\n ts.isCallExpression(node) &&\n ts.isIdentifier(node.expression) &&\n node.expression.text === \"groupedTranslations\"\n ) {\n const [groupArgument, dictionaryArgument] = node.arguments;\n\n if (\n ts.isStringLiteral(groupArgument) &&\n dictionaryArgument &&\n ts.isObjectLiteralExpression(dictionaryArgument)\n ) {\n for (const property of dictionaryArgument.properties) {\n if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property)) {\n continue;\n }\n\n const key = getPropertyName(property.name);\n if (key !== undefined) {\n keys.add(`${groupArgument.text}.${key}`);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n\n return Array.from(keys).sort();\n}\n\nfunction getPropertyName(name: ts.PropertyName | undefined): string | undefined {\n if (name === undefined) {\n return undefined;\n }\n\n if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {\n return name.text;\n }\n\n return undefined;\n}\n"],"mappings":";;;;AAGA,SAAgB,uBAAuB,YAAqC;CAC1E,MAAM,uBAAO,IAAI,IAAY;CAE7B,MAAM,SAAS,SAAwB;EACrC,IACE,GAAG,iBAAiB,IAAI,KACxB,GAAG,aAAa,KAAK,UAAU,KAC/B,KAAK,WAAW,SAAS,uBACzB;GACA,MAAM,CAAC,eAAe,sBAAsB,KAAK;GAEjD,IACE,GAAG,gBAAgB,aAAa,KAChC,sBACA,GAAG,0BAA0B,kBAAkB,GAE/C,KAAK,MAAM,YAAY,mBAAmB,YAAY;IACpD,IAAI,CAAC,GAAG,qBAAqB,QAAQ,KAAK,CAAC,GAAG,8BAA8B,QAAQ,GAClF;IAGF,MAAM,MAAM,gBAAgB,SAAS,IAAI;IACzC,IAAI,QAAQ,QACV,KAAK,IAAI,GAAG,cAAc,KAAK,GAAG,KAAK;GAE3C;EAEJ;EAEA,GAAG,aAAa,MAAM,KAAK;CAC7B;CAEA,GAAG,aAAa,YAAY,KAAK;CAEjC,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,KAAK;AAC/B;AAEA,SAAS,gBAAgB,MAAuD;CAC9E,IAAI,SAAS,QACX;CAGF,IAAI,GAAG,aAAa,IAAI,KAAK,GAAG,gBAAgB,IAAI,KAAK,GAAG,iBAAiB,IAAI,GAC/E,OAAO,KAAK;AAIhB"}