redweb 0.13.5 → 0.15.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 (72) hide show
  1. package/CHANGELOG.md +20 -3
  2. package/README.md +289 -288
  3. package/contract.d.ts +11 -3
  4. package/docs/APPLICATION.md +98 -0
  5. package/docs/CLI.md +1 -1
  6. package/docs/CLIENT_DEVELOPMENT.md +9 -5
  7. package/docs/COVERAGE_SCOPE_AUDIT.md +7 -0
  8. package/docs/DEFINE_APP_VERIFICATION.md +101 -0
  9. package/docs/DEVELOPMENT.md +1 -1
  10. package/docs/GETTING_STARTED.md +1 -1
  11. package/docs/LIVE_HTML.md +2 -2
  12. package/docs/MIGRATION.md +1 -1
  13. package/docs/MULTIPLAYER_OPERATIONS.md +6 -0
  14. package/docs/RELEASE_TRUST.md +29 -6
  15. package/docs/RUNTIME_DIAGNOSTICS.md +1 -1
  16. package/docs/SOCKET_CONTRACTS.md +6 -3
  17. package/docs/SOCKET_PAGES.md +99 -0
  18. package/docs/SOCKET_PAGE_RELEASE_PREPARATION.md +120 -0
  19. package/docs/SOCKET_PAGE_VERIFICATION.md +85 -0
  20. package/docs/STARTER_LIFECYCLE_VERIFICATION.md +9 -0
  21. package/docs/generated.json +2217 -2154
  22. package/docs/guides/http-websocket.md +4 -4
  23. package/docs/guides/jsx-without-react.md +1 -1
  24. package/docs/reference.json +40 -1
  25. package/docs/releases/0.14.0.json +2208 -0
  26. package/docs/releases/0.15.0.json +2217 -0
  27. package/docs/topics.json +3 -1
  28. package/examples/live-html/cards.js +87 -86
  29. package/examples/live-html/cards.ts +3 -2
  30. package/examples/live-html/chatroom.js +210 -207
  31. package/examples/live-html/chatroom.tsx +6 -2
  32. package/examples/live-html/components.js +103 -102
  33. package/examples/live-html/components.ts +3 -2
  34. package/examples/live-html/counter.js +74 -73
  35. package/examples/live-html/counter.ts +3 -2
  36. package/examples/live-html/jsx-page.js +2 -1
  37. package/examples/live-html/jsx-page.tsx +3 -2
  38. package/index.d.ts +59 -7
  39. package/index.js +3 -0
  40. package/package.json +8 -5
  41. package/recipes/chat/app.test.cjs +3 -1
  42. package/recipes/chat/app.tsx +4 -7
  43. package/recipes/dashboard/app.test.cjs +10 -10
  44. package/recipes/dashboard/app.tsx +29 -29
  45. package/recipes/http-ws/README.md +1 -1
  46. package/recipes/http-ws/app.test.cjs +8 -10
  47. package/recipes/http-ws/app.tsx +9 -20
  48. package/recipes/realtime/app.tsx +3 -6
  49. package/recipes/shared/README.md +10 -1
  50. package/recipes/shared/lifecycle.test.cjs +81 -0
  51. package/recipes/shared/network.cjs +10 -5
  52. package/recipes/site/app.tsx +3 -6
  53. package/recipes/socket/app.tsx +3 -10
  54. package/src/Application.js +239 -0
  55. package/src/StartupCleanup.js +24 -0
  56. package/src/cli/SourceInspector.js +5 -0
  57. package/src/cli/templates.js +6 -6
  58. package/src/htmx/Jsx.js +2 -2
  59. package/src/htmx/LiveHtmlServer.js +14 -5
  60. package/src/htmx/PageManager.js +13 -8
  61. package/src/htmx/PageSocketRoute.js +132 -0
  62. package/src/htmx/ReactiveRenderer.js +8 -2
  63. package/src/htmx/SocketAction.js +19 -0
  64. package/src/htmx/metadata.js +6 -2
  65. package/src/ws/BaseHandler.js +6 -4
  66. package/src/ws/BaseSocketServer.js +13 -13
  67. package/src/ws/HandlerGuard.js +4 -0
  68. package/src/ws/SocketAction.js +16 -0
  69. package/src/ws/SocketContract.js +3 -2
  70. package/src/ws/SocketRoute.js +9 -4
  71. package/recipes/shared/run-app.test.cjs +0 -158
  72. package/recipes/shared/run-app.ts +0 -50
package/docs/topics.json CHANGED
@@ -1,5 +1,6 @@
1
1
  [
2
2
  { "id": "getting-started", "title": "Choose a starter and build a working app", "summary": "Requirements, fit, development, tests, and production boundaries.", "source": "docs/GETTING_STARTED.md" },
3
+ { "id": "application", "title": "One application, one listener", "summary": "Define pages, socket routes and application services together; run and shut down one owned HTTP/WebSocket listener.", "source": "docs/APPLICATION.md" },
3
4
  { "id": "guides/realtime-dashboard", "title": "Build a private realtime dashboard", "summary": "Persistent SQLite cards, account-private updates and sign-out across tabs, with explicit single-process limits.", "source": "docs/guides/realtime-dashboard.md", "recipe": { "template": "dashboard", "file": "src/cards.tsx" } },
4
5
  { "id": "guides/jsx-without-react", "title": "Render JSX without React", "summary": "TypeScript pages, a shared layout and external CSS, rendered on the server without browser framework code.", "source": "docs/guides/jsx-without-react.md", "recipe": { "template": "site", "file": "src/app.tsx" } },
5
6
  { "id": "guides/chatroom", "title": "Build a chatroom with live presence", "summary": "Reusable server-side components, validated forms and disconnect-aware presence, without custom browser socket glue.", "source": "docs/guides/chatroom.md", "recipe": { "template": "chat", "file": "src/chatroom.tsx" } },
@@ -11,7 +12,8 @@
11
12
  { "id": "runtime-diagnostics", "title": "Understand runtime failures before retrying", "summary": "Safe authentication, authorization, validation and application errors, with actual retry and cancellation guarantees.", "source": "docs/RUNTIME_DIAGNOSTICS.md" },
12
13
  { "id": "agent-access", "title": "Optional read-only agent documentation access", "summary": "Configure local MCP search and exact recipe retrieval without adding dependencies to Redweb servers.", "source": "docs/AGENT_ACCESS.md" },
13
14
  { "id": "live-html", "title": "Pages, components, state, actions, and CSS", "summary": "Server-side TSX, automatic updates, keyed lists, lifecycle, and static export.", "source": "docs/LIVE_HTML.md" },
14
- { "id": "socket-contracts", "title": "Typed socket routes and handlers", "summary": "Shared validation, client/server types, protocol errors, and join/move/resume.", "source": "docs/SOCKET_CONTRACTS.md" },
15
+ { "id": "socket-contracts", "title": "Typed socket routes and handlers", "summary": "Shared validation, client/server types, protocol errors, and join/move/resume.", "source": "docs/SOCKET_CONTRACTS.md" },
16
+ { "id": "socket-pages", "title": "Server-side TSX for custom socket routes", "summary": "Typed handler bindings, private page state and one generated client connection without handwritten browser glue.", "source": "docs/SOCKET_PAGES.md" },
15
17
  { "id": "room-authorization", "title": "Private rooms and shared request identity", "summary": "Explicit entry policies, bounded authorization, trusted context, publication and revocation.", "source": "docs/ROOM_AUTHORIZATION.md" },
16
18
  { "id": "operations", "title": "Deploy and operate socket services", "summary": "Readiness, shutdown, capacity, reconnection, and distributed boundaries.", "source": "docs/MULTIPLAYER_OPERATIONS.md" },
17
19
  { "id": "production-contract", "title": "Production guarantees and limits", "summary": "Resource ownership, delivery semantics, compatibility, and release gates.", "source": "docs/PRODUCTION_READINESS.md" },
@@ -1,91 +1,92 @@
1
- "use strict";
2
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
- var useValue = arguments.length > 2;
4
- for (var i = 0; i < initializers.length; i++) {
5
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
- }
7
- return useValue ? value : void 0;
8
- };
9
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
10
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
11
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
12
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
13
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
14
- var _, done = false;
15
- for (var i = decorators.length - 1; i >= 0; i--) {
16
- var context = {};
17
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
18
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
19
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
20
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
21
- if (kind === "accessor") {
22
- if (result === void 0) continue;
23
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
24
- if (_ = accept(result.get)) descriptor.get = _;
25
- if (_ = accept(result.set)) descriptor.set = _;
26
- if (_ = accept(result.init)) initializers.unshift(_);
27
- }
28
- else if (_ = accept(result)) {
29
- if (kind === "field") initializers.unshift(_);
30
- else descriptor[key] = _;
31
- }
32
- }
33
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
- done = true;
35
- };
36
- Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.CardsPage = void 0;
38
- const redweb_1 = require('../..');
39
- let CardsPage = (() => {
40
- let _classDecorators = [(0, redweb_1.page)('/', { template: 'cards.html', css: 'cards.css', shared: true })];
41
- let _classDescriptor;
42
- let _classExtraInitializers = [];
43
- let _classThis;
44
- let _instanceExtraInitializers = [];
45
- let _cards_decorators;
46
- let _cards_initializers = [];
47
- let _cards_extraInitializers = [];
48
- let _card_decorators;
49
- let _add_decorators;
50
- var CardsPage = class {
51
- static { _classThis = this; }
52
- static {
53
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
54
- _cards_decorators = [(0, redweb_1.state)()];
55
- _card_decorators = [(0, redweb_1.view)('cards')];
56
- _add_decorators = [(0, redweb_1.action)()];
57
- __esDecorate(this, null, _card_decorators, { kind: "method", name: "card", static: false, private: false, access: { has: obj => "card" in obj, get: obj => obj.card }, metadata: _metadata }, null, _instanceExtraInitializers);
58
- __esDecorate(this, null, _add_decorators, { kind: "method", name: "add", static: false, private: false, access: { has: obj => "add" in obj, get: obj => obj.add }, metadata: _metadata }, null, _instanceExtraInitializers);
59
- __esDecorate(null, null, _cards_decorators, { kind: "field", name: "cards", static: false, private: false, access: { has: obj => "cards" in obj, get: obj => obj.cards, set: (obj, value) => { obj.cards = value; } }, metadata: _metadata }, _cards_initializers, _cards_extraInitializers);
60
- __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
61
- CardsPage = _classThis = _classDescriptor.value;
62
- if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
63
- __runInitializers(_classThis, _classExtraInitializers);
64
- }
65
- cards = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _cards_initializers, [
66
- { title: 'Realtime', description: 'State changes arrive over the existing socket.' },
67
- { title: 'Safe HTML', description: 'Card values are escaped by default.' },
68
- ]));
69
- card(card) {
1
+ "use strict";
2
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
+ var useValue = arguments.length > 2;
4
+ for (var i = 0; i < initializers.length; i++) {
5
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
+ }
7
+ return useValue ? value : void 0;
8
+ };
9
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
10
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
11
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
12
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
13
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
14
+ var _, done = false;
15
+ for (var i = decorators.length - 1; i >= 0; i--) {
16
+ var context = {};
17
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
18
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
19
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
20
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
21
+ if (kind === "accessor") {
22
+ if (result === void 0) continue;
23
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
24
+ if (_ = accept(result.get)) descriptor.get = _;
25
+ if (_ = accept(result.set)) descriptor.set = _;
26
+ if (_ = accept(result.init)) initializers.unshift(_);
27
+ }
28
+ else if (_ = accept(result)) {
29
+ if (kind === "field") initializers.unshift(_);
30
+ else descriptor[key] = _;
31
+ }
32
+ }
33
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
+ done = true;
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.CardsPage = void 0;
38
+ const redweb_1 = require('../..');
39
+ let CardsPage = (() => {
40
+ let _classDecorators = [(0, redweb_1.page)('/', { template: 'cards.html', css: 'cards.css', shared: true })];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ let _instanceExtraInitializers = [];
45
+ let _cards_decorators;
46
+ let _cards_initializers = [];
47
+ let _cards_extraInitializers = [];
48
+ let _card_decorators;
49
+ let _add_decorators;
50
+ var CardsPage = class {
51
+ static { _classThis = this; }
52
+ static {
53
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
54
+ _cards_decorators = [(0, redweb_1.state)()];
55
+ _card_decorators = [(0, redweb_1.view)('cards')];
56
+ _add_decorators = [(0, redweb_1.action)()];
57
+ __esDecorate(this, null, _card_decorators, { kind: "method", name: "card", static: false, private: false, access: { has: obj => "card" in obj, get: obj => obj.card }, metadata: _metadata }, null, _instanceExtraInitializers);
58
+ __esDecorate(this, null, _add_decorators, { kind: "method", name: "add", static: false, private: false, access: { has: obj => "add" in obj, get: obj => obj.add }, metadata: _metadata }, null, _instanceExtraInitializers);
59
+ __esDecorate(null, null, _cards_decorators, { kind: "field", name: "cards", static: false, private: false, access: { has: obj => "cards" in obj, get: obj => obj.cards, set: (obj, value) => { obj.cards = value; } }, metadata: _metadata }, _cards_initializers, _cards_extraInitializers);
60
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
61
+ CardsPage = _classThis = _classDescriptor.value;
62
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
63
+ __runInitializers(_classThis, _classExtraInitializers);
64
+ }
65
+ cards = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _cards_initializers, [
66
+ { title: 'Realtime', description: 'State changes arrive over the existing socket.' },
67
+ { title: 'Safe HTML', description: 'Card values are escaped by default.' },
68
+ ]));
69
+ card(card) {
70
70
  return (0, redweb_1.html) `
71
71
  <article class="card">
72
72
  <h2>${card.title}</h2>
73
73
  <p>${card.description}</p>
74
74
  </article>
75
- `;
76
- }
77
- add() {
78
- this.cards = [...this.cards, {
79
- title: `Card ${this.cards.length + 1}`,
80
- description: 'Rendered on the server and synchronized without client code.',
81
- }];
82
- }
83
- constructor() {
84
- __runInitializers(this, _cards_extraInitializers);
85
- }
86
- };
87
- return CardsPage = _classThis;
88
- })();
89
- exports.CardsPage = CardsPage;
90
- if (require.main === module)
91
- (0, redweb_1.start)(CardsPage, { port: 8080 });
75
+ `;
76
+ }
77
+ add() {
78
+ this.cards = [...this.cards, {
79
+ title: `Card ${this.cards.length + 1}`,
80
+ description: 'Rendered on the server and synchronized without client code.',
81
+ }];
82
+ }
83
+ constructor() {
84
+ __runInitializers(this, _cards_extraInitializers);
85
+ }
86
+ };
87
+ return CardsPage = _classThis;
88
+ })();
89
+ exports.CardsPage = CardsPage;
90
+ if (require.main === module)
91
+ void (0, redweb_1.defineApp)({ pages: [CardsPage], port: 8080 }).run()
92
+ .catch(error => { console.error(error); process.exitCode = 1; });
@@ -1,4 +1,4 @@
1
- import { action, html, page, start, state, view } from 'redweb';
1
+ import { action, defineApp, html, page, state, view } from 'redweb';
2
2
 
3
3
  interface Card {
4
4
  title: string;
@@ -32,4 +32,5 @@ export class CardsPage {
32
32
  }
33
33
  }
34
34
 
35
- if (require.main === module) start(CardsPage, { port: 8080 });
35
+ if (require.main === module) void defineApp({ pages: [CardsPage], port: 8080 }).run()
36
+ .catch(error => { console.error(error); process.exitCode = 1; });