@ossido-labs/ossido 0.1.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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -0
  3. package/bin/build-config.js +5 -0
  4. package/bin/build-prod.js +5 -0
  5. package/bin/dev-ssr.js +5 -0
  6. package/bin/run-build-hook.js +6 -0
  7. package/bin/watch.js +5 -0
  8. package/dist/esm/build/config/create-json-config.d.ts +10 -0
  9. package/dist/esm/build/config/create-json-config.js +52 -0
  10. package/dist/esm/build/config/create-json-config.js.map +1 -0
  11. package/dist/esm/build/config/index.d.ts +2 -0
  12. package/dist/esm/build/config/load-config.d.ts +2 -0
  13. package/dist/esm/build/config/load-config.js +20 -0
  14. package/dist/esm/build/config/load-config.js.map +1 -0
  15. package/dist/esm/build/config/normalize-config.d.ts +10 -0
  16. package/dist/esm/build/config/normalize-config.js +75 -0
  17. package/dist/esm/build/config/normalize-config.js.map +1 -0
  18. package/dist/esm/build/constants.d.ts +5 -0
  19. package/dist/esm/build/constants.js +10 -0
  20. package/dist/esm/build/constants.js.map +1 -0
  21. package/dist/esm/build/index.d.ts +15 -0
  22. package/dist/esm/build/index.js +245 -0
  23. package/dist/esm/build/index.js.map +1 -0
  24. package/dist/esm/build/logger.d.ts +11 -0
  25. package/dist/esm/build/logger.js +76 -0
  26. package/dist/esm/build/logger.js.map +1 -0
  27. package/dist/esm/build/types.d.ts +29 -0
  28. package/dist/esm/build/utils.d.ts +6 -0
  29. package/dist/esm/build/utils.js +40 -0
  30. package/dist/esm/build/utils.js.map +1 -0
  31. package/dist/esm/build-client/index.d.ts +1 -0
  32. package/dist/esm/build-client/index.js +1 -0
  33. package/dist/esm/client/index.d.ts +92 -0
  34. package/dist/esm/client/index.js +38 -0
  35. package/dist/esm/client/index.js.map +1 -0
  36. package/dist/esm/config/index.d.ts +1 -0
  37. package/dist/esm/config/index.js +0 -0
  38. package/dist/esm/config/types.d.ts +130 -0
  39. package/dist/esm/constants.d.ts +1 -0
  40. package/dist/esm/constants.js +6 -0
  41. package/dist/esm/constants.js.map +1 -0
  42. package/dist/esm/hydration/browserLogForwarding.d.ts +7 -0
  43. package/dist/esm/hydration/browserLogForwarding.js +152 -0
  44. package/dist/esm/hydration/browserLogForwarding.js.map +1 -0
  45. package/dist/esm/hydration/index.d.ts +4 -0
  46. package/dist/esm/hydration/index.js +24 -0
  47. package/dist/esm/hydration/index.js.map +1 -0
  48. package/dist/esm/index.d.ts +6 -0
  49. package/dist/esm/index.js +7 -0
  50. package/dist/esm/shared/DevResources.d.ts +7 -0
  51. package/dist/esm/shared/DevResources.js +45 -0
  52. package/dist/esm/shared/DevResources.js.map +1 -0
  53. package/dist/esm/shared/OssidoContext.d.ts +14 -0
  54. package/dist/esm/shared/OssidoContext.js +28 -0
  55. package/dist/esm/shared/OssidoContext.js.map +1 -0
  56. package/dist/esm/shared/OssidoEntryPoint.d.ts +11 -0
  57. package/dist/esm/shared/OssidoEntryPoint.js +17 -0
  58. package/dist/esm/shared/OssidoEntryPoint.js.map +1 -0
  59. package/dist/esm/shared/OssidoScripts.d.ts +2 -0
  60. package/dist/esm/shared/OssidoScripts.js +34 -0
  61. package/dist/esm/shared/OssidoScripts.js.map +1 -0
  62. package/dist/esm/shared/ProdResources.d.ts +7 -0
  63. package/dist/esm/shared/ProdResources.js +18 -0
  64. package/dist/esm/shared/ProdResources.js.map +1 -0
  65. package/dist/esm/shared/RouterContextProviderWrapper.d.ts +14 -0
  66. package/dist/esm/shared/RouterContextProviderWrapper.js +27 -0
  67. package/dist/esm/shared/RouterContextProviderWrapper.js.map +1 -0
  68. package/dist/esm/shared/dynamic/RouteLazyLoading.d.ts +6 -0
  69. package/dist/esm/shared/dynamic/RouteLazyLoading.js +20 -0
  70. package/dist/esm/shared/dynamic/RouteLazyLoading.js.map +1 -0
  71. package/dist/esm/shared/dynamic/dynamic.d.ts +15 -0
  72. package/dist/esm/shared/dynamic/dynamic.js +57 -0
  73. package/dist/esm/shared/dynamic/dynamic.js.map +1 -0
  74. package/dist/esm/shared/dynamic/index.d.ts +2 -0
  75. package/dist/esm/shared/ossido-context.d.ts +22 -0
  76. package/dist/esm/shared/ossido-context.js +22 -0
  77. package/dist/esm/shared/ossido-context.js.map +1 -0
  78. package/dist/esm/shared/routeHot.d.ts +10 -0
  79. package/dist/esm/shared/routeHot.js +20 -0
  80. package/dist/esm/shared/routeHot.js.map +1 -0
  81. package/dist/esm/ssr/index.d.ts +4 -0
  82. package/dist/esm/ssr/index.js +6 -0
  83. package/dist/esm/ssr/polyfills/Event.d.ts +18 -0
  84. package/dist/esm/ssr/polyfills/Event.js +24 -0
  85. package/dist/esm/ssr/polyfills/Event.js.map +1 -0
  86. package/dist/esm/ssr/polyfills/MessageChannel.d.ts +19 -0
  87. package/dist/esm/ssr/polyfills/MessageChannel.js +50 -0
  88. package/dist/esm/ssr/polyfills/MessageChannel.js.map +1 -0
  89. package/dist/esm/ssr/polyfills/MessageEvent.d.ts +12 -0
  90. package/dist/esm/ssr/polyfills/MessageEvent.js +20 -0
  91. package/dist/esm/ssr/polyfills/MessageEvent.js.map +1 -0
  92. package/dist/esm/ssr/polyfills/globalScope.d.ts +30 -0
  93. package/dist/esm/ssr/polyfills/globalScope.js +5 -0
  94. package/dist/esm/ssr/polyfills/globalScope.js.map +1 -0
  95. package/dist/esm/ssr/server.d.ts +40 -0
  96. package/dist/esm/ssr/server.js +61 -0
  97. package/dist/esm/ssr/server.js.map +1 -0
  98. package/dist/esm/ssr/utils.d.ts +19 -0
  99. package/dist/esm/ssr/utils.js +78 -0
  100. package/dist/esm/ssr/utils.js.map +1 -0
  101. package/dist/esm/types.d.ts +34 -0
  102. package/package.json +110 -0
@@ -0,0 +1,24 @@
1
+ //#region src/ssr/polyfills/Event.ts
2
+ /**
3
+ * https://dom.spec.whatwg.org/#interface-event
4
+ */
5
+ var EventPolyfill = class {
6
+ constructor(type, eventInitDict) {
7
+ this.currentTarget = null;
8
+ this.defaultPrevented = false;
9
+ this.eventPhase = 0;
10
+ this.isTrusted = false;
11
+ this.target = null;
12
+ this.timeStamp = Date.now();
13
+ this.returnValue = true;
14
+ this.srcElement = null;
15
+ this.type = type;
16
+ this.bubbles = eventInitDict?.bubbles ?? false;
17
+ this.cancelable = eventInitDict?.cancelable ?? false;
18
+ this.composed = eventInitDict?.composed ?? false;
19
+ }
20
+ };
21
+
22
+ //#endregion
23
+ export { EventPolyfill };
24
+ //# sourceMappingURL=Event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Event.js","names":[],"sources":["../../../../src/ssr/polyfills/Event.ts"],"sourcesContent":["/**\n * https://dom.spec.whatwg.org/#interface-event\n */\n// @ts-expect-error Not all the properties are implemented\nexport class EventPolyfill implements Event {\n type: string;\n bubbles: boolean;\n cancelable: boolean;\n composed: boolean;\n currentTarget: EventTarget | null = null;\n defaultPrevented = false;\n eventPhase = 0;\n isTrusted = false;\n target: EventTarget | null = null;\n timeStamp: number = Date.now();\n returnValue = true;\n srcElement: EventTarget | null = null;\n\n constructor(type: string, eventInitDict?: EventInit) {\n this.type = type;\n this.bubbles = eventInitDict?.bubbles ?? false;\n this.cancelable = eventInitDict?.cancelable ?? false;\n this.composed = eventInitDict?.composed ?? false;\n }\n}\n"],"mappings":";;;;AAIA,IAAa,gBAAb,MAA4C;CAc1C,YAAY,MAAc,eAA2B;uBATjB;0BACjB;oBACN;mBACD;gBACiB;mBACT,KAAK,IAAI;qBACf;oBACmB;EAG/B,KAAK,OAAO;EACZ,KAAK,UAAU,eAAe,WAAW;EACzC,KAAK,aAAa,eAAe,cAAc;EAC/C,KAAK,WAAW,eAAe,YAAY;CAC7C;AACF"}
@@ -0,0 +1,19 @@
1
+ export declare class MessagePortPolyfill implements MessagePort {
2
+ onmessage: ((this: MessagePort, ev: MessageEvent) => unknown) | null;
3
+ /** @warning this is declared to satisfy {@link MessagePort} interface requirements but is never called */
4
+ onmessageerror: ((this: MessagePort, ev: MessageEvent) => unknown) | null;
5
+ otherPort: MessagePortPolyfill | null;
6
+ private onmessageListeners;
7
+ private isClosed;
8
+ dispatchEvent(event: MessageEvent): boolean;
9
+ postMessage(message: unknown): void;
10
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject): void;
11
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject): void;
12
+ start(): void;
13
+ close(): void;
14
+ }
15
+ export declare class MessageChannelPolyfill implements MessageChannel {
16
+ readonly port1: MessagePortPolyfill;
17
+ readonly port2: MessagePortPolyfill;
18
+ constructor();
19
+ }
@@ -0,0 +1,50 @@
1
+ //#region src/ssr/polyfills/MessageChannel.ts
2
+ var MessagePortPolyfill = class {
3
+ constructor() {
4
+ this.onmessage = null;
5
+ this.onmessageerror = null;
6
+ this.otherPort = null;
7
+ this.onmessageListeners = [];
8
+ this.isClosed = false;
9
+ }
10
+ dispatchEvent(event) {
11
+ if (this.isClosed) return false;
12
+ if (this.onmessage) this.onmessage(event);
13
+ this.onmessageListeners.forEach((listener) => {
14
+ listener(event);
15
+ });
16
+ return true;
17
+ }
18
+ postMessage(message) {
19
+ if (this.isClosed || !this.otherPort) return;
20
+ const event = new MessageEvent("message", { data: message });
21
+ this.otherPort.dispatchEvent(event);
22
+ }
23
+ addEventListener(type, listener) {
24
+ if (this.isClosed || type !== "message") return;
25
+ if (typeof listener === "function" && !this.onmessageListeners.includes(listener)) this.onmessageListeners.push(listener);
26
+ }
27
+ removeEventListener(type, listener) {
28
+ if (this.isClosed || type !== "message") return;
29
+ if (typeof listener === "function") {
30
+ const index = this.onmessageListeners.indexOf(listener);
31
+ if (index !== -1) this.onmessageListeners.splice(index, 1);
32
+ }
33
+ }
34
+ start() {}
35
+ close() {
36
+ this.isClosed = true;
37
+ }
38
+ };
39
+ var MessageChannelPolyfill = class {
40
+ constructor() {
41
+ this.port1 = new MessagePortPolyfill();
42
+ this.port2 = new MessagePortPolyfill();
43
+ this.port1.otherPort = this.port2;
44
+ this.port2.otherPort = this.port1;
45
+ }
46
+ };
47
+
48
+ //#endregion
49
+ export { MessageChannelPolyfill, MessagePortPolyfill };
50
+ //# sourceMappingURL=MessageChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageChannel.js","names":[],"sources":["../../../../src/ssr/polyfills/MessageChannel.ts"],"sourcesContent":["/* Modified from https://github.com/rocwind/message-port-polyfill/blob/master/src/index.ts\n * MIT License\n *\n * Copyright (c) 2019 Roc\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nexport class MessagePortPolyfill implements MessagePort {\n onmessage: ((this: MessagePort, ev: MessageEvent) => unknown) | null = null;\n /** @warning this is declared to satisfy {@link MessagePort} interface requirements but is never called */\n onmessageerror: ((this: MessagePort, ev: MessageEvent) => unknown) | null =\n null;\n\n otherPort: MessagePortPolyfill | null = null;\n\n private onmessageListeners: Array<(ev: MessageEvent) => void> = [];\n private isClosed = false;\n\n dispatchEvent(event: MessageEvent): boolean {\n if (this.isClosed) return false;\n if (this.onmessage) {\n this.onmessage(event);\n }\n this.onmessageListeners.forEach((listener) => {\n listener(event);\n });\n return true;\n }\n\n postMessage(message: unknown): void {\n if (this.isClosed || !this.otherPort) return;\n\n const event = new MessageEvent('message', { data: message });\n this.otherPort.dispatchEvent(event);\n }\n\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n ): void {\n if (this.isClosed || type !== 'message') return;\n\n if (\n typeof listener === 'function' &&\n !this.onmessageListeners.includes(listener)\n ) {\n this.onmessageListeners.push(listener);\n }\n }\n\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n ): void {\n if (this.isClosed || type !== 'message') return;\n\n if (typeof listener === 'function') {\n const index = this.onmessageListeners.indexOf(listener);\n if (index !== -1) {\n this.onmessageListeners.splice(index, 1);\n }\n }\n }\n\n start(): void {\n // do nothing at this moment\n }\n\n close(): void {\n this.isClosed = true;\n }\n}\n\nexport class MessageChannelPolyfill implements MessageChannel {\n readonly port1: MessagePortPolyfill;\n readonly port2: MessagePortPolyfill;\n\n constructor() {\n this.port1 = new MessagePortPolyfill();\n this.port2 = new MessagePortPolyfill();\n\n this.port1.otherPort = this.port2;\n this.port2.otherPort = this.port1;\n }\n}\n"],"mappings":";AAwBA,IAAa,sBAAb,MAAwD;;mBACiB;wBAGrE;mBAEsC;4BAEwB,CAAC;kBAC9C;;CAEnB,cAAc,OAA8B;EAC1C,IAAI,KAAK,UAAU,OAAO;EAC1B,IAAI,KAAK,WACP,KAAK,UAAU,KAAK;EAEtB,KAAK,mBAAmB,SAAS,aAAa;GAC5C,SAAS,KAAK;EAChB,CAAC;EACD,OAAO;CACT;CAEA,YAAY,SAAwB;EAClC,IAAI,KAAK,YAAY,CAAC,KAAK,WAAW;EAEtC,MAAM,QAAQ,IAAI,aAAa,WAAW,EAAE,MAAM,QAAQ,CAAC;EAC3D,KAAK,UAAU,cAAc,KAAK;CACpC;CAEA,iBACE,MACA,UACM;EACN,IAAI,KAAK,YAAY,SAAS,WAAW;EAEzC,IACE,OAAO,aAAa,cACpB,CAAC,KAAK,mBAAmB,SAAS,QAAQ,GAE1C,KAAK,mBAAmB,KAAK,QAAQ;CAEzC;CAEA,oBACE,MACA,UACM;EACN,IAAI,KAAK,YAAY,SAAS,WAAW;EAEzC,IAAI,OAAO,aAAa,YAAY;GAClC,MAAM,QAAQ,KAAK,mBAAmB,QAAQ,QAAQ;GACtD,IAAI,UAAU,IACZ,KAAK,mBAAmB,OAAO,OAAO,CAAC;EAE3C;CACF;CAEA,QAAc,CAEd;CAEA,QAAc;EACZ,KAAK,WAAW;CAClB;AACF;AAEA,IAAa,yBAAb,MAA8D;CAI5D,cAAc;EACZ,KAAK,QAAQ,IAAI,oBAAoB;EACrC,KAAK,QAAQ,IAAI,oBAAoB;EAErC,KAAK,MAAM,YAAY,KAAK;EAC5B,KAAK,MAAM,YAAY,KAAK;CAC9B;AACF"}
@@ -0,0 +1,12 @@
1
+ import { EventPolyfill } from './Event';
2
+ /**
3
+ * https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interface
4
+ */
5
+ export declare class MessageEventPolyfill<T> extends EventPolyfill implements MessageEvent {
6
+ data?: T;
7
+ lastEventId: string;
8
+ origin: string;
9
+ ports: ReadonlyArray<MessagePort>;
10
+ source: MessageEventSource | null;
11
+ constructor(type: string, options: MessageEventInit<T>);
12
+ }
@@ -0,0 +1,20 @@
1
+ import { EventPolyfill } from "./Event.js";
2
+
3
+ //#region src/ssr/polyfills/MessageEvent.ts
4
+ /**
5
+ * https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interface
6
+ */
7
+ var MessageEventPolyfill = class extends EventPolyfill {
8
+ constructor(type, options) {
9
+ super(type, options);
10
+ this.data = options.data;
11
+ this.lastEventId = options.lastEventId ?? "";
12
+ this.origin = options.origin ?? "";
13
+ this.ports = options.ports ?? [];
14
+ this.source = options.source ?? null;
15
+ }
16
+ };
17
+
18
+ //#endregion
19
+ export { MessageEventPolyfill };
20
+ //# sourceMappingURL=MessageEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageEvent.js","names":[],"sources":["../../../../src/ssr/polyfills/MessageEvent.ts"],"sourcesContent":["import { EventPolyfill } from './Event';\n\n/**\n * https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interface\n */\n// @ts-expect-error Not all the properties are implemented\nexport class MessageEventPolyfill<T>\n extends EventPolyfill\n implements MessageEvent\n{\n data?: T;\n lastEventId: string;\n origin: string;\n ports: ReadonlyArray<MessagePort>;\n source: MessageEventSource | null;\n\n constructor(type: string, options: MessageEventInit<T>) {\n super(type, options);\n this.data = options.data;\n this.lastEventId = options.lastEventId ?? '';\n this.origin = options.origin ?? '';\n this.ports = options.ports ?? [];\n this.source = options.source ?? null;\n }\n}\n"],"mappings":";;;;;;AAMA,IAAa,uBAAb,cACU,cAEV;CAOE,YAAY,MAAc,SAA8B;EACtD,MAAM,MAAM,OAAO;EACnB,KAAK,OAAO,QAAQ;EACpB,KAAK,cAAc,QAAQ,eAAe;EAC1C,KAAK,SAAS,QAAQ,UAAU;EAChC,KAAK,QAAQ,QAAQ,SAAS,CAAC;EAC/B,KAAK,SAAS,QAAQ,UAAU;CAClC;AACF"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The `ossido_ssr` V8 runtime only exposes `globalThis` — it defines neither
3
+ * `window` nor `global`.
4
+ *
5
+ * Some UMD-style polyfills install themselves onto whichever global object
6
+ * they can find, falling back to `undefined`:
7
+ *
8
+ * ```js
9
+ * // fast-text-encoding
10
+ * })(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : void 0)
11
+ * // url-search-params-polyfill
12
+ * })(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : void 0)
13
+ * ```
14
+ *
15
+ * With no `window`/`global`, that scope resolves to `undefined` and the init
16
+ * IIFE throws `Cannot read properties of undefined (reading 'TextEncoder')`.
17
+ *
18
+ * Aliasing `global` to `globalThis` gives those polyfills a valid target.
19
+ *
20
+ * @warning We intentionally do NOT define `window`. Server-side detection
21
+ * across the framework relies on `typeof window === 'undefined'` (see
22
+ * `ossido-router`'s `RouterContext`), so defining it would make SSR code paths
23
+ * believe they are running in the browser.
24
+ *
25
+ * This module must be imported before any polyfill that reads `global`.
26
+ */
27
+ declare global {
28
+ var global: typeof globalThis;
29
+ }
30
+ export {};
@@ -0,0 +1,5 @@
1
+ //#region src/ssr/polyfills/globalScope.ts
2
+ globalThis.global ??= globalThis;
3
+
4
+ //#endregion
5
+ //# sourceMappingURL=globalScope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globalScope.js","names":[],"sources":["../../../../src/ssr/polyfills/globalScope.ts"],"sourcesContent":["/**\n * The `ossido_ssr` V8 runtime only exposes `globalThis` — it defines neither\n * `window` nor `global`.\n *\n * Some UMD-style polyfills install themselves onto whichever global object\n * they can find, falling back to `undefined`:\n *\n * ```js\n * // fast-text-encoding\n * })(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : void 0)\n * // url-search-params-polyfill\n * })(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : void 0)\n * ```\n *\n * With no `window`/`global`, that scope resolves to `undefined` and the init\n * IIFE throws `Cannot read properties of undefined (reading 'TextEncoder')`.\n *\n * Aliasing `global` to `globalThis` gives those polyfills a valid target.\n *\n * @warning We intentionally do NOT define `window`. Server-side detection\n * across the framework relies on `typeof window === 'undefined'` (see\n * `ossido-router`'s `RouterContext`), so defining it would make SSR code paths\n * believe they are running in the browser.\n *\n * This module must be imported before any polyfill that reads `global`.\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var global: typeof globalThis;\n}\n\n(globalThis as { global?: typeof globalThis }).global ??= globalThis;\n\nexport {};\n"],"mappings":";AA+BA,AAAC,WAA8C,WAAW"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Ossido internally uses a V8 JS engine that implements very few
3
+ * browser/node/deno APIs in order to make it super fast and
4
+ * share it within a multi thread runtime.
5
+ *
6
+ * While this is the reason of its speed, server side rendering
7
+ * requires some JS APIs that need to be polyfilled.
8
+ *
9
+ * We basically have three ways to polyfill APIs:
10
+ * 1. Create them with rust and expose them directly through the V8 engine to
11
+ * the JS source.
12
+ * 2. Polyfill them at the beginning of the JS source
13
+ * (what we are doing here)
14
+ * 3. Inject them via rollup-inject plugin, when needed
15
+ *
16
+ * Q: Why not all the libraries can be just injected with rollup-inject?
17
+ * A: Leaving to rollup the duty of linking them can cause to declare them after their usage.
18
+ * The following APIs are JS classes, and are not hoisted, hence this might
19
+ * cause ReferenceError(s).
20
+ *
21
+ * The best solution is to create these polyfills within the rust environment
22
+ * and share the classes in the JS scope by passing them through the V8 engine
23
+ * (best for speed and code quality).
24
+ *
25
+ * This function might be a good entry point for adding such polyfills
26
+ * (see `Ssr::add_global_fn` in crates/ossido_ssr/src/ssr.rs)
27
+ */
28
+ import './polyfills/globalScope';
29
+ import 'fast-text-encoding';
30
+ import 'url-search-params-polyfill';
31
+ import type { createRoute } from '@ossido-labs/ossido-router';
32
+ type RouteTree = ReturnType<typeof createRoute>;
33
+ interface ServerSideRenderer {
34
+ /** Buffered render — resolves the whole page to a single HTML string. */
35
+ renderFn: (payload: string | undefined) => Promise<string>;
36
+ /** Streaming render — flushes each HTML chunk via `__ssr_write`. */
37
+ renderStream: (payload: string | undefined) => Promise<void>;
38
+ }
39
+ export declare function serverSideRendering(routeTree: RouteTree): ServerSideRenderer;
40
+ export {};
@@ -0,0 +1,61 @@
1
+ import "./polyfills/globalScope.js";
2
+ import { MessageChannelPolyfill } from "./polyfills/MessageChannel.js";
3
+ import { OssidoEntryPoint } from "../shared/OssidoEntryPoint.js";
4
+ import { createUtf8Streamer, streamToString } from "./utils.js";
5
+ import { createRouter, preloadRouteChain } from "@ossido-labs/ossido-router";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import "fast-text-encoding";
8
+ import "url-search-params-polyfill";
9
+ import { renderToReadableStream } from "react-dom/server";
10
+
11
+ //#region src/ssr/server.tsx
12
+ (function(scope = {}) {
13
+ scope["MessageChannel"] = scope["MessageChannel"] ?? MessageChannelPolyfill;
14
+ })(void 0);
15
+ function serverSideRendering(routeTree) {
16
+ const element = async (payload) => {
17
+ const serverPayload = payload ? JSON.parse(payload) : {};
18
+ const router = createRouter({ routeTree });
19
+ await preloadRouteChain(router, serverPayload.location?.pathname);
20
+ return /* @__PURE__ */ jsx(OssidoEntryPoint, {
21
+ router,
22
+ serverPayload,
23
+ rawServerPayload: payload
24
+ });
25
+ };
26
+ return {
27
+ /**
28
+ * Buffered render: resolve the whole page to a single string. Used for
29
+ * error pages, static export (SSG), `catch_all`, and the dev fallback —
30
+ * anywhere the caller needs the complete HTML up front.
31
+ */
32
+ async renderFn(payload) {
33
+ const stream = await renderToReadableStream(await element(payload));
34
+ await stream.allReady;
35
+ return await streamToString(stream);
36
+ },
37
+ /**
38
+ * Streaming render: flush each HTML chunk to Rust via
39
+ * `__ssr_write` as React produces it, so the shell reaches the
40
+ * client without waiting for the full page. `renderToReadableStream`
41
+ * rejects on a *shell* error before any chunk is written, so Rust can still
42
+ * send a 500 instead of a partial 200 in that case.
43
+ */
44
+ async renderStream(payload) {
45
+ const write = __ssr_write;
46
+ if (typeof write !== "function") throw new Error("__ssr_write is not registered by the runtime");
47
+ const stream = await renderToReadableStream(await element(payload));
48
+ const streamer = createUtf8Streamer();
49
+ for await (const chunk of stream) {
50
+ const text = streamer.push(chunk);
51
+ if (text) write(text);
52
+ }
53
+ const tail = streamer.flush();
54
+ if (tail) write(tail);
55
+ }
56
+ };
57
+ }
58
+
59
+ //#endregion
60
+ export { serverSideRendering };
61
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../../src/ssr/server.tsx"],"sourcesContent":["// #region POLYFILLS\n/**\n * Ossido internally uses a V8 JS engine that implements very few\n * browser/node/deno APIs in order to make it super fast and\n * share it within a multi thread runtime.\n *\n * While this is the reason of its speed, server side rendering\n * requires some JS APIs that need to be polyfilled.\n *\n * We basically have three ways to polyfill APIs:\n * 1. Create them with rust and expose them directly through the V8 engine to\n * the JS source.\n * 2. Polyfill them at the beginning of the JS source\n * (what we are doing here)\n * 3. Inject them via rollup-inject plugin, when needed\n *\n * Q: Why not all the libraries can be just injected with rollup-inject?\n * A: Leaving to rollup the duty of linking them can cause to declare them after their usage.\n * The following APIs are JS classes, and are not hoisted, hence this might\n * cause ReferenceError(s).\n *\n * The best solution is to create these polyfills within the rust environment\n * and share the classes in the JS scope by passing them through the V8 engine\n * (best for speed and code quality).\n *\n * This function might be a good entry point for adding such polyfills\n * (see `Ssr::add_global_fn` in crates/ossido_ssr/src/ssr.rs)\n */\n// Must run before the polyfills below: aliases `global` to `globalThis` so\n// their UMD init IIFEs find a valid scope in the `window`/`global`-less\n// ossido_ssr V8 runtime instead of dereferencing `undefined`.\nimport './polyfills/globalScope';\nimport 'fast-text-encoding';\nimport 'url-search-params-polyfill';\n\n/* eslint-disable import/order, import/newline-after-import */\nimport { MessageChannelPolyfill } from './polyfills/MessageChannel';\n(function (\n scope: Partial<Pick<typeof globalThis, 'MessageChannel'>> = {},\n): void {\n scope['MessageChannel'] = scope['MessageChannel'] ?? MessageChannelPolyfill;\n})(this);\n/* eslint-enable import/order, import/newline-after-import */\n// #endregion POLYFILLS\n\nimport type { ReadableStream } from 'node:stream/web';\n\nimport type { JSX } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport { createRouter, preloadRouteChain } from '@ossido-labs/ossido-router';\nimport type { createRoute } from '@ossido-labs/ossido-router';\n\nimport { OssidoEntryPoint } from '../shared/OssidoEntryPoint';\nimport type { ServerPayload } from '../types';\n\nimport { streamToString, createUtf8Streamer } from './utils';\n\ntype RouteTree = ReturnType<typeof createRoute>;\n\n/**\n * Chunk sink injected by the Rust `ossido_ssr` runtime for streaming renders (see\n * `Ssr::render_to_stream`). Each call hands one HTML fragment to Rust, which\n * forwards it to the client immediately instead of buffering the whole page.\n */\ndeclare const __ssr_write: ((chunk: string) => void) | undefined;\n\ninterface ServerSideRenderer {\n /** Buffered render — resolves the whole page to a single HTML string. */\n renderFn: (payload: string | undefined) => Promise<string>;\n /** Streaming render — flushes each HTML chunk via `__ssr_write`. */\n renderStream: (payload: string | undefined) => Promise<void>;\n}\n\nexport function serverSideRendering(routeTree: RouteTree): ServerSideRenderer {\n // Build the React element for a request. Shared by the buffered and streaming\n // entry points so they render exactly the same tree.\n //\n // Async because the matched route's code (page + wrapping layouts) is\n // preloaded first: routes are `React.lazy`-wrapped, and rendering one that\n // hasn't loaded suspends its boundary, which pushes the page content into an\n // out-of-order late chunk (empty shell paints first — a visible flash on\n // every cold load). In the bundled SSR output the dynamic imports are\n // inlined, so this resolves in a microtask.\n const element = async (payload: string | undefined): Promise<JSX.Element> => {\n const serverPayload = (payload ? JSON.parse(payload) : {}) as ServerPayload;\n const router = createRouter({ routeTree });\n await preloadRouteChain(router, serverPayload.location?.pathname);\n return (\n // `rawServerPayload` is the exact JSON Rust already produced; passing it\n // lets `OssidoScripts` embed it verbatim instead of re-stringifying the\n // parsed payload inside V8.\n <OssidoEntryPoint\n router={router}\n serverPayload={serverPayload}\n rawServerPayload={payload}\n />\n );\n };\n\n return {\n /**\n * Buffered render: resolve the whole page to a single string. Used for\n * error pages, static export (SSG), `catch_all`, and the dev fallback —\n * anywhere the caller needs the complete HTML up front.\n */\n async renderFn(payload: string | undefined): Promise<string> {\n const stream = await renderToReadableStream(await element(payload));\n await stream.allReady;\n return await streamToString(\n // ReadableStream should be implemented in node)\n stream as unknown as ReadableStream<Uint8Array>,\n );\n },\n\n /**\n * Streaming render: flush each HTML chunk to Rust via\n * `__ssr_write` as React produces it, so the shell reaches the\n * client without waiting for the full page. `renderToReadableStream`\n * rejects on a *shell* error before any chunk is written, so Rust can still\n * send a 500 instead of a partial 200 in that case.\n */\n async renderStream(payload: string | undefined): Promise<void> {\n const write = __ssr_write;\n if (typeof write !== 'function') {\n throw new Error('__ssr_write is not registered by the runtime');\n }\n\n const stream = await renderToReadableStream(await element(payload));\n\n const streamer = createUtf8Streamer();\n for await (const chunk of stream as unknown as ReadableStream<Uint8Array>) {\n const text = streamer.push(chunk);\n if (text) write(text);\n }\n const tail = streamer.flush();\n if (tail) write(tail);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;CAqCC,SACC,QAA4D,CAAC,GACvD;CACN,MAAM,oBAAoB,MAAM,qBAAqB;AACvD,EAAC,OAAM;AAgCP,SAAgB,oBAAoB,WAA0C;CAU5E,MAAM,UAAU,OAAO,YAAsD;EAC3E,MAAM,gBAAiB,UAAU,KAAK,MAAM,OAAO,IAAI,CAAC;EACxD,MAAM,SAAS,aAAa,EAAE,UAAU,CAAC;EACzC,MAAM,kBAAkB,QAAQ,cAAc,UAAU,QAAQ;EAChE,OAIE,oBAAC,kBAAD;GACU;GACO;GACf,kBAAkB;EACnB;CAEL;CAEA,OAAO;;;;;;EAML,MAAM,SAAS,SAA8C;GAC3D,MAAM,SAAS,MAAM,uBAAuB,MAAM,QAAQ,OAAO,CAAC;GAClE,MAAM,OAAO;GACb,OAAO,MAAM,eAEX,MACF;EACF;;;;;;;;EASA,MAAM,aAAa,SAA4C;GAC7D,MAAM,QAAQ;GACd,IAAI,OAAO,UAAU,YACnB,MAAM,IAAI,MAAM,8CAA8C;GAGhE,MAAM,SAAS,MAAM,uBAAuB,MAAM,QAAQ,OAAO,CAAC;GAElE,MAAM,WAAW,mBAAmB;GACpC,WAAW,MAAM,SAAS,QAAiD;IACzE,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,IAAI,MAAM,MAAM,IAAI;GACtB;GACA,MAAM,OAAO,SAAS,MAAM;GAC5B,IAAI,MAAM,MAAM,IAAI;EACtB;CACF;AACF"}
@@ -0,0 +1,19 @@
1
+ import type { ReadableStream } from 'node:stream/web';
2
+ /**
3
+ * This function awaits for the whole stream before returning the string.
4
+ *
5
+ * NOTE: we should improve the bond between the custom V8 runtime and the
6
+ * renderToReadableStream React function to return a stream directly to the client.
7
+ */
8
+ export declare function streamToString(stream: ReadableStream<Uint8Array>): Promise<string>;
9
+ /**
10
+ * Incremental UTF-8 decoder for the streaming SSR path. The ossido_ssr runtime's
11
+ * `TextDecoder` polyfill (`fast-text-encoding`) does not support the
12
+ * `{ stream: true }` option, so we buffer any incomplete trailing byte
13
+ * sequence ourselves and only decode up to a character boundary — keeping
14
+ * multi-byte characters that straddle React's chunk boundaries intact.
15
+ */
16
+ export declare function createUtf8Streamer(): {
17
+ push: (chunk: Uint8Array) => string;
18
+ flush: () => string;
19
+ };
@@ -0,0 +1,78 @@
1
+ //#region src/ssr/utils.ts
2
+ function concatArrayBuffers(chunks) {
3
+ const result = new Uint8Array(chunks.reduce((a, c) => a + c.length, 0));
4
+ let offset = 0;
5
+ for (const chunk of chunks) {
6
+ result.set(chunk, offset);
7
+ offset += chunk.length;
8
+ }
9
+ return result;
10
+ }
11
+ async function streamToArrayBuffer(stream) {
12
+ const chunks = [];
13
+ for await (const chunk of stream) chunks.push(chunk);
14
+ return concatArrayBuffers(chunks);
15
+ }
16
+ /**
17
+ * This function awaits for the whole stream before returning the string.
18
+ *
19
+ * NOTE: we should improve the bond between the custom V8 runtime and the
20
+ * renderToReadableStream React function to return a stream directly to the client.
21
+ */
22
+ async function streamToString(stream) {
23
+ const buffer = await streamToArrayBuffer(stream);
24
+ return new TextDecoder().decode(buffer);
25
+ }
26
+ /**
27
+ * Index where the last *complete* UTF-8 character ends: bytes `[0, end)` are
28
+ * safe to decode now, `[end, length)` is an incomplete trailing multi-byte
29
+ * sequence to carry into the next chunk. Malformed input decodes as-is.
30
+ */
31
+ function completeUtf8End(buf) {
32
+ if (buf.length === 0) return 0;
33
+ let i = buf.length - 1;
34
+ while (i >= 0 && (buf[i] & 192) === 128) i--;
35
+ if (i < 0) return buf.length;
36
+ const lead = buf[i];
37
+ let need;
38
+ if ((lead & 128) === 0) need = 1;
39
+ else if ((lead & 224) === 192) need = 2;
40
+ else if ((lead & 240) === 224) need = 3;
41
+ else if ((lead & 248) === 240) need = 4;
42
+ else return buf.length;
43
+ return buf.length - i >= need ? buf.length : i;
44
+ }
45
+ /**
46
+ * Incremental UTF-8 decoder for the streaming SSR path. The ossido_ssr runtime's
47
+ * `TextDecoder` polyfill (`fast-text-encoding`) does not support the
48
+ * `{ stream: true }` option, so we buffer any incomplete trailing byte
49
+ * sequence ourselves and only decode up to a character boundary — keeping
50
+ * multi-byte characters that straddle React's chunk boundaries intact.
51
+ */
52
+ function createUtf8Streamer() {
53
+ const decoder = new TextDecoder();
54
+ let pending = /* @__PURE__ */ new Uint8Array(0);
55
+ return {
56
+ push(chunk) {
57
+ let buf;
58
+ if (pending.length) {
59
+ buf = new Uint8Array(pending.length + chunk.length);
60
+ buf.set(pending, 0);
61
+ buf.set(chunk, pending.length);
62
+ } else buf = chunk;
63
+ const end = completeUtf8End(buf);
64
+ pending = buf.slice(end);
65
+ return end > 0 ? decoder.decode(buf.subarray(0, end)) : "";
66
+ },
67
+ flush() {
68
+ if (pending.length === 0) return "";
69
+ const out = decoder.decode(pending);
70
+ pending = /* @__PURE__ */ new Uint8Array(0);
71
+ return out;
72
+ }
73
+ };
74
+ }
75
+
76
+ //#endregion
77
+ export { createUtf8Streamer, streamToString };
78
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/ssr/utils.ts"],"sourcesContent":["// react ReadableStream type is an empty interface so we are using the one from\n// node which match the runtime value\nimport type { ReadableStream } from 'node:stream/web';\n\nfunction concatArrayBuffers(chunks: Array<Uint8Array>): Uint8Array {\n const result = new Uint8Array(chunks.reduce((a, c) => a + c.length, 0));\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n}\n\nasync function streamToArrayBuffer(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const chunks: Array<Uint8Array> = [];\n\n for await (const chunk of stream) {\n chunks.push(chunk);\n }\n\n return concatArrayBuffers(chunks);\n}\n\n/**\n * This function awaits for the whole stream before returning the string.\n *\n * NOTE: we should improve the bond between the custom V8 runtime and the\n * renderToReadableStream React function to return a stream directly to the client.\n */\nexport async function streamToString(\n stream: ReadableStream<Uint8Array>,\n): Promise<string> {\n const buffer = await streamToArrayBuffer(stream);\n return new TextDecoder().decode(buffer);\n}\n\n/**\n * Index where the last *complete* UTF-8 character ends: bytes `[0, end)` are\n * safe to decode now, `[end, length)` is an incomplete trailing multi-byte\n * sequence to carry into the next chunk. Malformed input decodes as-is.\n */\nfunction completeUtf8End(buf: Uint8Array): number {\n if (buf.length === 0) return 0;\n // Walk back over continuation bytes (0b10xxxxxx) to the sequence's lead byte.\n let i = buf.length - 1;\n while (i >= 0 && (buf[i]! & 0xc0) === 0x80) i--;\n if (i < 0) return buf.length; // all continuations (malformed) — decode as-is\n\n const lead = buf[i]!;\n let need: number;\n if ((lead & 0x80) === 0) need = 1;\n else if ((lead & 0xe0) === 0xc0) need = 2;\n else if ((lead & 0xf0) === 0xe0) need = 3;\n else if ((lead & 0xf8) === 0xf0) need = 4;\n else return buf.length; // malformed lead byte — decode as-is\n\n // Complete sequence → the whole buffer is safe; incomplete → hold it back.\n return buf.length - i >= need ? buf.length : i;\n}\n\n/**\n * Incremental UTF-8 decoder for the streaming SSR path. The ossido_ssr runtime's\n * `TextDecoder` polyfill (`fast-text-encoding`) does not support the\n * `{ stream: true }` option, so we buffer any incomplete trailing byte\n * sequence ourselves and only decode up to a character boundary — keeping\n * multi-byte characters that straddle React's chunk boundaries intact.\n */\nexport function createUtf8Streamer(): {\n push: (chunk: Uint8Array) => string;\n flush: () => string;\n} {\n const decoder = new TextDecoder();\n let pending = new Uint8Array(0);\n\n return {\n push(chunk: Uint8Array): string {\n let buf: Uint8Array;\n if (pending.length) {\n buf = new Uint8Array(pending.length + chunk.length);\n buf.set(pending, 0);\n buf.set(chunk, pending.length);\n } else {\n buf = chunk;\n }\n const end = completeUtf8End(buf);\n pending = buf.slice(end);\n return end > 0 ? decoder.decode(buf.subarray(0, end)) : '';\n },\n flush(): string {\n if (pending.length === 0) return '';\n const out = decoder.decode(pending);\n pending = new Uint8Array(0);\n return out;\n },\n };\n}\n"],"mappings":";AAIA,SAAS,mBAAmB,QAAuC;CACjE,MAAM,SAAS,IAAI,WAAW,OAAO,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,CAAC,CAAC;CACtE,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,OAAO,IAAI,OAAO,MAAM;EACxB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;AAEA,eAAe,oBACb,QACqB;CACrB,MAAM,SAA4B,CAAC;CAEnC,WAAW,MAAM,SAAS,QACxB,OAAO,KAAK,KAAK;CAGnB,OAAO,mBAAmB,MAAM;AAClC;;;;;;;AAQA,eAAsB,eACpB,QACiB;CACjB,MAAM,SAAS,MAAM,oBAAoB,MAAM;CAC/C,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,MAAM;AACxC;;;;;;AAOA,SAAS,gBAAgB,KAAyB;CAChD,IAAI,IAAI,WAAW,GAAG,OAAO;CAE7B,IAAI,IAAI,IAAI,SAAS;CACrB,OAAO,KAAK,MAAM,IAAI,KAAM,SAAU,KAAM;CAC5C,IAAI,IAAI,GAAG,OAAO,IAAI;CAEtB,MAAM,OAAO,IAAI;CACjB,IAAI;CACJ,KAAK,OAAO,SAAU,GAAG,OAAO;MAC3B,KAAK,OAAO,SAAU,KAAM,OAAO;MACnC,KAAK,OAAO,SAAU,KAAM,OAAO;MACnC,KAAK,OAAO,SAAU,KAAM,OAAO;MACnC,OAAO,IAAI;CAGhB,OAAO,IAAI,SAAS,KAAK,OAAO,IAAI,SAAS;AAC/C;;;;;;;;AASA,SAAgB,qBAGd;CACA,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,0BAAU,IAAI,WAAW,CAAC;CAE9B,OAAO;EACL,KAAK,OAA2B;GAC9B,IAAI;GACJ,IAAI,QAAQ,QAAQ;IAClB,MAAM,IAAI,WAAW,QAAQ,SAAS,MAAM,MAAM;IAClD,IAAI,IAAI,SAAS,CAAC;IAClB,IAAI,IAAI,OAAO,QAAQ,MAAM;GAC/B,OACE,MAAM;GAER,MAAM,MAAM,gBAAgB,GAAG;GAC/B,UAAU,IAAI,MAAM,GAAG;GACvB,OAAO,MAAM,IAAI,QAAQ,OAAO,IAAI,SAAS,GAAG,GAAG,CAAC,IAAI;EAC1D;EACA,QAAgB;GACd,IAAI,QAAQ,WAAW,GAAG,OAAO;GACjC,MAAM,MAAM,QAAQ,OAAO,OAAO;GAClC,0BAAU,IAAI,WAAW,CAAC;GAC1B,OAAO;EACT;CACF;AACF"}
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ServerErrorPayload } from '@ossido-labs/ossido-router';
3
+ import type { OssidoConfigServer } from './config';
4
+ export type Mode = 'Dev' | 'Prod';
5
+ /**
6
+ * Provided by the rust server and used in the ssr env
7
+ * @see ossido-router {@link ServerInitialLocation}
8
+ */
9
+ export interface ServerPayloadLocation {
10
+ href: string;
11
+ pathname: string;
12
+ searchStr: string;
13
+ }
14
+ /**
15
+ * @see crates/ossido/src/payload.rs
16
+ */
17
+ export type ServerPayload<TData = unknown> = {
18
+ location: ServerPayloadLocation;
19
+ data: TData;
20
+ /** Wrapping `layout.rs` handlers' data, keyed by each layout's `dataKey`. */
21
+ layoutData?: Record<string, unknown>;
22
+ /** Present (dev only) when the route's Rust handler panicked. */
23
+ serverError?: ServerErrorPayload;
24
+ } & ({
25
+ mode: 'Prod';
26
+ jsBundles: Array<string>;
27
+ cssBundles: Array<string>;
28
+ } | {
29
+ mode: 'Dev';
30
+ devServerConfig?: OssidoConfigServer;
31
+ });
32
+ export interface OssidoLayoutProps {
33
+ children: ReactNode;
34
+ }
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "@ossido-labs/ossido",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Superfast React fullstack framework",
8
+ "homepage": "https://ossido.dev",
9
+ "scripts": {
10
+ "dev": "tsdown --watch",
11
+ "build": "tsdown && tsc -p tsconfig.build.json",
12
+ "prepack": "bun run build",
13
+ "lint": "oxlint",
14
+ "format": "oxfmt --check .",
15
+ "format:fix": "oxfmt .",
16
+ "typecheck": "tsc --noEmit",
17
+ "test:watch": "vitest",
18
+ "test": "vitest run"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ossido-labs/ossido.git",
23
+ "directory": "packages/ossido"
24
+ },
25
+ "type": "module",
26
+ "types": "dist/esm/index.d.ts",
27
+ "main": "dist/esm/index.js",
28
+ "module": "dist/esm/index.js",
29
+ "exports": {
30
+ "./build": {
31
+ "types": "./dist/esm/build/index.d.ts",
32
+ "default": "./dist/esm/build/index.js"
33
+ },
34
+ "./build-client": {
35
+ "types": "./dist/esm/build-client/index.d.ts"
36
+ },
37
+ "./client": {
38
+ "types": "./dist/esm/client/index.d.ts",
39
+ "default": "./dist/esm/client/index.js"
40
+ },
41
+ "./config": {
42
+ "types": "./dist/esm/config/index.d.ts",
43
+ "default": "./dist/esm/config/index.js"
44
+ },
45
+ "./ssr": {
46
+ "types": "./dist/esm/ssr/index.d.ts",
47
+ "default": "./dist/esm/ssr/index.js"
48
+ },
49
+ "./hydration": {
50
+ "types": "./dist/esm/hydration/index.d.ts",
51
+ "default": "./dist/esm/hydration/index.js"
52
+ },
53
+ ".": {
54
+ "types": "./dist/esm/index.d.ts",
55
+ "default": "./dist/esm/index.js"
56
+ },
57
+ "./package.json": "./package.json"
58
+ },
59
+ "bin": {
60
+ "ossido-dev-ssr": "./bin/dev-ssr.js",
61
+ "ossido-dev-watch": "./bin/watch.js",
62
+ "ossido-build-prod": "./bin/build-prod.js",
63
+ "ossido-build-config": "./bin/build-config.js",
64
+ "ossido-run-build-hook": "./bin/run-build-hook.js"
65
+ },
66
+ "files": [
67
+ "dist",
68
+ "README.md",
69
+ "bin/**"
70
+ ],
71
+ "peerDependencies": {
72
+ "react": ">=19.0.0",
73
+ "react-dom": ">=19.0.0",
74
+ "vite": "^8.2.0"
75
+ },
76
+ "dependencies": {
77
+ "@rollup/plugin-inject": "^5.0.5",
78
+ "@vitejs/plugin-react-swc": "^4.3.2",
79
+ "fast-text-encoding": "^1.0.6",
80
+ "@ossido-labs/ossido-react-vite-plugin": "0.1.0",
81
+ "@ossido-labs/ossido-router": "0.1.0",
82
+ "@ossido-labs/ossido-ui": "0.1.0",
83
+ "url-search-params-polyfill": "^8.2.5",
84
+ "web-streams-polyfill": "^4.0.0"
85
+ },
86
+ "devDependencies": {
87
+ "@types/node": "22.16.5",
88
+ "@types/react": "19.1.8",
89
+ "@types/react-dom": "19.1.6",
90
+ "react": "^19.2.8",
91
+ "react-dom": "^19.2.8",
92
+ "tsdown": "0.22.14",
93
+ "vite": "^8.2.0",
94
+ "vite-config": "1.0.0",
95
+ "vitest": "4.1.10"
96
+ },
97
+ "sideEffects": [
98
+ "**/ssr/server.*",
99
+ "**/ssr/polyfills/globalScope.*"
100
+ ],
101
+ "keywords": [
102
+ "react",
103
+ "typescript",
104
+ "fullstack",
105
+ "framework",
106
+ "rust"
107
+ ],
108
+ "author": "Chris Schofield <chris@childishforces.com>",
109
+ "license": "MIT"
110
+ }