tina4-nodejs 3.13.135 → 3.13.136

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/CLAUDE.md CHANGED
@@ -13,13 +13,13 @@ Even if the skill text is not currently loaded, these are non-negotiable:
13
13
 
14
14
  The full discipline lives in `.claude/skills/tina4-maintainer/SKILL.md`; this block is the always-on floor.
15
15
 
16
- # CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.135)
16
+ # CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.136)
17
17
 
18
18
  > This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
19
19
 
20
20
  ## What This Project Is
21
21
 
22
- Tina4 for Node.js/TypeScript v3.13.135 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
22
+ Tina4 for Node.js/TypeScript v3.13.136 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
23
23
 
24
24
  The philosophy: zero ceremony, batteries included, file system as source of truth.
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4-nodejs",
3
- "version": "3.13.135",
3
+ "version": "3.13.136",
4
4
  "type": "module",
5
5
  "description": "Tina4 for Node.js/TypeScript - native TypeScript conventions and shared Tina4 contracts",
6
6
  "keywords": [
@@ -37955,13 +37955,37 @@ function swaggerEnabled() {
37955
37955
  return ["true", "1", "yes", "on"].includes(raw);
37956
37956
  }
37957
37957
  function createSwaggerRoutes(getSpec) {
37958
+ const serveUi = async (_req, res) => {
37959
+ res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
37960
+ };
37958
37961
  return [
37959
37962
  {
37960
37963
  method: "GET",
37961
37964
  pattern: "/swagger",
37962
- handler: async (_req, res) => {
37963
- res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
37964
- }
37965
+ handler: serveUi
37966
+ },
37967
+ {
37968
+ // The trailing-slash form, registered rather than left to fall through.
37969
+ //
37970
+ // Matching "/foo/" against a "/foo" route is opt-in via
37971
+ // TINA4_TRAILING_SLASH_REDIRECT and OFF by default, so /swagger/ missed
37972
+ // this route and was answered by the framework-bundled
37973
+ // public/swagger/index.html instead. That mattered twice over. It used to
37974
+ // be a 200 carrying a permanently empty UI, because the bundled file asked
37975
+ // for an unsubstituted {SWAGGER_ROUTE}/swagger.json -- fixed in that file.
37976
+ // And it is a SECOND Swagger UI implementation: the bundled one hardcodes
37977
+ // cdnjs, while the page this handler renders loads from
37978
+ // TINA4_SWAGGER_UI_CDN, so an air-gapped deployment pointing that at a
37979
+ // local mirror silently kept reaching cdnjs on this one path.
37980
+ //
37981
+ // Registering it keeps the fix inside swagger rather than changing how
37982
+ // every route treats trailing slashes, satisfies the shared contract that
37983
+ // already requires a 200 here, and matches python and ruby, which both
37984
+ // serve /swagger and /swagger/ with no env var set. Excluded from the
37985
+ // generated document by INTERNAL_PREFIXES like /swagger itself.
37986
+ method: "GET",
37987
+ pattern: "/swagger/",
37988
+ handler: serveUi
37965
37989
  },
37966
37990
  {
37967
37991
  method: "GET",
@@ -37934,13 +37934,37 @@ function swaggerEnabled() {
37934
37934
  return ["true", "1", "yes", "on"].includes(raw);
37935
37935
  }
37936
37936
  function createSwaggerRoutes(getSpec) {
37937
+ const serveUi = async (_req, res) => {
37938
+ res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
37939
+ };
37937
37940
  return [
37938
37941
  {
37939
37942
  method: "GET",
37940
37943
  pattern: "/swagger",
37941
- handler: async (_req, res) => {
37942
- res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
37943
- }
37944
+ handler: serveUi
37945
+ },
37946
+ {
37947
+ // The trailing-slash form, registered rather than left to fall through.
37948
+ //
37949
+ // Matching "/foo/" against a "/foo" route is opt-in via
37950
+ // TINA4_TRAILING_SLASH_REDIRECT and OFF by default, so /swagger/ missed
37951
+ // this route and was answered by the framework-bundled
37952
+ // public/swagger/index.html instead. That mattered twice over. It used to
37953
+ // be a 200 carrying a permanently empty UI, because the bundled file asked
37954
+ // for an unsubstituted {SWAGGER_ROUTE}/swagger.json -- fixed in that file.
37955
+ // And it is a SECOND Swagger UI implementation: the bundled one hardcodes
37956
+ // cdnjs, while the page this handler renders loads from
37957
+ // TINA4_SWAGGER_UI_CDN, so an air-gapped deployment pointing that at a
37958
+ // local mirror silently kept reaching cdnjs on this one path.
37959
+ //
37960
+ // Registering it keeps the fix inside swagger rather than changing how
37961
+ // every route treats trailing slashes, satisfies the shared contract that
37962
+ // already requires a 200 here, and matches python and ruby, which both
37963
+ // serve /swagger and /swagger/ with no env var set. Excluded from the
37964
+ // generated document by INTERNAL_PREFIXES like /swagger itself.
37965
+ method: "GET",
37966
+ pattern: "/swagger/",
37967
+ handler: serveUi
37944
37968
  },
37945
37969
  {
37946
37970
  method: "GET",
@@ -71,7 +71,7 @@
71
71
 
72
72
  // Build a system
73
73
  const ui = SwaggerUIBundle({
74
- url: "{SWAGGER_ROUTE}/swagger.json",
74
+ url: "/swagger/openapi.json",
75
75
  dom_id: '#swagger-ui',
76
76
  deepLinking: true,
77
77
  presets: [
@@ -26223,13 +26223,37 @@ function swaggerEnabled() {
26223
26223
  return ["true", "1", "yes", "on"].includes(raw);
26224
26224
  }
26225
26225
  function createSwaggerRoutes(getSpec) {
26226
+ const serveUi = async (_req, res) => {
26227
+ res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
26228
+ };
26226
26229
  return [
26227
26230
  {
26228
26231
  method: "GET",
26229
26232
  pattern: "/swagger",
26230
- handler: async (_req, res) => {
26231
- res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
26232
- }
26233
+ handler: serveUi
26234
+ },
26235
+ {
26236
+ // The trailing-slash form, registered rather than left to fall through.
26237
+ //
26238
+ // Matching "/foo/" against a "/foo" route is opt-in via
26239
+ // TINA4_TRAILING_SLASH_REDIRECT and OFF by default, so /swagger/ missed
26240
+ // this route and was answered by the framework-bundled
26241
+ // public/swagger/index.html instead. That mattered twice over. It used to
26242
+ // be a 200 carrying a permanently empty UI, because the bundled file asked
26243
+ // for an unsubstituted {SWAGGER_ROUTE}/swagger.json -- fixed in that file.
26244
+ // And it is a SECOND Swagger UI implementation: the bundled one hardcodes
26245
+ // cdnjs, while the page this handler renders loads from
26246
+ // TINA4_SWAGGER_UI_CDN, so an air-gapped deployment pointing that at a
26247
+ // local mirror silently kept reaching cdnjs on this one path.
26248
+ //
26249
+ // Registering it keeps the fix inside swagger rather than changing how
26250
+ // every route treats trailing slashes, satisfies the shared contract that
26251
+ // already requires a 200 here, and matches python and ruby, which both
26252
+ // serve /swagger and /swagger/ with no env var set. Excluded from the
26253
+ // generated document by INTERNAL_PREFIXES like /swagger itself.
26254
+ method: "GET",
26255
+ pattern: "/swagger/",
26256
+ handler: serveUi
26233
26257
  },
26234
26258
  {
26235
26259
  method: "GET",
@@ -521,13 +521,37 @@ function swaggerEnabled() {
521
521
  return ["true", "1", "yes", "on"].includes(raw);
522
522
  }
523
523
  function createSwaggerRoutes(getSpec) {
524
+ const serveUi = async (_req, res) => {
525
+ res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
526
+ };
524
527
  return [
525
528
  {
526
529
  method: "GET",
527
530
  pattern: "/swagger",
528
- handler: async (_req, res) => {
529
- res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
530
- }
531
+ handler: serveUi
532
+ },
533
+ {
534
+ // The trailing-slash form, registered rather than left to fall through.
535
+ //
536
+ // Matching "/foo/" against a "/foo" route is opt-in via
537
+ // TINA4_TRAILING_SLASH_REDIRECT and OFF by default, so /swagger/ missed
538
+ // this route and was answered by the framework-bundled
539
+ // public/swagger/index.html instead. That mattered twice over. It used to
540
+ // be a 200 carrying a permanently empty UI, because the bundled file asked
541
+ // for an unsubstituted {SWAGGER_ROUTE}/swagger.json -- fixed in that file.
542
+ // And it is a SECOND Swagger UI implementation: the bundled one hardcodes
543
+ // cdnjs, while the page this handler renders loads from
544
+ // TINA4_SWAGGER_UI_CDN, so an air-gapped deployment pointing that at a
545
+ // local mirror silently kept reaching cdnjs on this one path.
546
+ //
547
+ // Registering it keeps the fix inside swagger rather than changing how
548
+ // every route treats trailing slashes, satisfies the shared contract that
549
+ // already requires a 200 here, and matches python and ruby, which both
550
+ // serve /swagger and /swagger/ with no env var set. Excluded from the
551
+ // generated document by INTERNAL_PREFIXES like /swagger itself.
552
+ method: "GET",
553
+ pattern: "/swagger/",
554
+ handler: serveUi
531
555
  },
532
556
  {
533
557
  method: "GET",
@@ -56,13 +56,38 @@ export function swaggerEnabled(): boolean {
56
56
  export function createSwaggerRoutes(
57
57
  getSpec: () => unknown
58
58
  ): RouteDefinition[] {
59
+ const serveUi = async (_req: Tina4Request, res: Tina4Response): Promise<void> => {
60
+ res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
61
+ };
62
+
59
63
  return [
60
64
  {
61
65
  method: "GET",
62
66
  pattern: "/swagger",
63
- handler: async (_req: Tina4Request, res: Tina4Response) => {
64
- res.html(SWAGGER_UI_HTML("/swagger/openapi.json"));
65
- },
67
+ handler: serveUi,
68
+ },
69
+ {
70
+ // The trailing-slash form, registered rather than left to fall through.
71
+ //
72
+ // Matching "/foo/" against a "/foo" route is opt-in via
73
+ // TINA4_TRAILING_SLASH_REDIRECT and OFF by default, so /swagger/ missed
74
+ // this route and was answered by the framework-bundled
75
+ // public/swagger/index.html instead. That mattered twice over. It used to
76
+ // be a 200 carrying a permanently empty UI, because the bundled file asked
77
+ // for an unsubstituted {SWAGGER_ROUTE}/swagger.json -- fixed in that file.
78
+ // And it is a SECOND Swagger UI implementation: the bundled one hardcodes
79
+ // cdnjs, while the page this handler renders loads from
80
+ // TINA4_SWAGGER_UI_CDN, so an air-gapped deployment pointing that at a
81
+ // local mirror silently kept reaching cdnjs on this one path.
82
+ //
83
+ // Registering it keeps the fix inside swagger rather than changing how
84
+ // every route treats trailing slashes, satisfies the shared contract that
85
+ // already requires a 200 here, and matches python and ruby, which both
86
+ // serve /swagger and /swagger/ with no env var set. Excluded from the
87
+ // generated document by INTERNAL_PREFIXES like /swagger itself.
88
+ method: "GET",
89
+ pattern: "/swagger/",
90
+ handler: serveUi,
66
91
  },
67
92
  {
68
93
  method: "GET",