@gravito/ion 1.0.0-beta.2

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 ADDED
@@ -0,0 +1,101 @@
1
+ # 🛰️ Orbit Inertia
2
+
3
+ > Inertia.js adapter for Gravito. Build modern monoliths with React/Vue.
4
+
5
+ **Orbit Inertia** allows you to build single-page apps using classic server-side routing and controllers. It acts as the glue between Gravito (Hono) and your frontend framework.
6
+
7
+ ## ✨ Features
8
+
9
+ - **Server-Side Routing**: Use Gravito's elegant routing and controllers.
10
+ - **Client-Side Rendering**: Build your UI with React, Vue, or Svelte.
11
+ - **No API required**: Pass data directly from controllers to components as props.
12
+ - **SEO Ready**: Compatible with SSR (Server-Side Rendering) patterns since we control the initial page load.
13
+
14
+ ## 📦 Installation
15
+
16
+ ```bash
17
+ bun add @gravito/ion
18
+ ```
19
+
20
+ ## 🚀 Usage
21
+
22
+ ### 1. Register the Orbit
23
+
24
+ In your `bootstrap.ts`:
25
+
26
+ ```typescript
27
+ import { OrbitIon } from '@gravito/ion';
28
+ import { OrbitPrism } from '@gravito/prism'; // Required for root template
29
+
30
+ const config = defineConfig({
31
+ orbits: [OrbitPrism, OrbitIon],
32
+ });
33
+ ```
34
+
35
+ ### 2. Create the Root Template
36
+
37
+ By default, Inertia looks for `src/views/app.html`. This is the "shell" of your application.
38
+
39
+ ```html
40
+ <!DOCTYPE html>
41
+ <html>
42
+ <head>
43
+ <meta charset="utf-8" />
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
45
+ <!-- Include your compiled assets -->
46
+ <script type="module" src="/static/build/assets/index.js"></script>
47
+ <link rel="stylesheet" href="/static/build/assets/index.css">
48
+ </head>
49
+ <body>
50
+ <!-- The data-page attribute is crucial for Inertia -->
51
+ <div id="app" data-page='{{{ page }}}'></div>
52
+ </body>
53
+ </html>
54
+ ```
55
+
56
+ ### 3. Return Responses
57
+
58
+ In your controllers, simply use `inertia.render()`:
59
+
60
+ ```typescript
61
+ import { Context } from 'hono';
62
+ import { InertiaService } from '@gravito/ion';
63
+
64
+ export class HomeController {
65
+ index = async (c: Context) => {
66
+ const inertia = c.get('inertia') as InertiaService;
67
+
68
+ return inertia.render('Home', {
69
+ user: 'Carl',
70
+ stats: { visits: 100 }
71
+ });
72
+ };
73
+ }
74
+ ```
75
+
76
+ The `'Home'` string corresponds to your frontend component path (e.g., `src/client/pages/Home.tsx`).
77
+
78
+ ## 🔧 Client-Side Setup (React Example)
79
+
80
+ You need to set up your client entry point (e.g., `src/client/app.tsx`):
81
+
82
+ ```tsx
83
+ import { createInertiaApp } from '@inertiajs/react';
84
+ import { createRoot } from 'react-dom/client';
85
+
86
+ createInertiaApp({
87
+ resolve: name => {
88
+ const pages = import.meta.glob('./pages/**/*.tsx', { eager: true });
89
+ return pages[`./pages/${name}.tsx`];
90
+ },
91
+ setup({ el, App, props }) {
92
+ createRoot(el).render(<App {...props} />);
93
+ },
94
+ });
95
+ ```
96
+
97
+ See `templates/inertia-react` for a complete working example with Vite.
98
+
99
+ ## 📝 License
100
+
101
+ MIT
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview Inertia.js Service for Gravito
3
+ *
4
+ * Provides server-side Inertia.js integration for building modern
5
+ * single-page applications with server-side routing.
6
+ *
7
+ * @module @gravito/ion
8
+ * @since 1.0.0
9
+ */
10
+ import type { GravitoContext, GravitoVariables } from 'gravito-core';
11
+ /**
12
+ * Configuration options for InertiaService
13
+ */
14
+ export interface InertiaConfig {
15
+ /**
16
+ * The root view template name
17
+ * @default 'app'
18
+ */
19
+ rootView?: string;
20
+ /**
21
+ * Asset version for cache busting
22
+ */
23
+ version?: string;
24
+ }
25
+ /**
26
+ * InertiaService - Server-side Inertia.js adapter
27
+ *
28
+ * This service handles the Inertia.js protocol for seamless
29
+ * SPA-like navigation with server-side routing.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // In a controller
34
+ * async index(ctx: GravitoContext) {
35
+ * const inertia = ctx.get('inertia') as InertiaService
36
+ * return inertia.render('Home', { users: await User.all() })
37
+ * }
38
+ * ```
39
+ */
40
+ export declare class InertiaService {
41
+ private context;
42
+ private config;
43
+ private sharedProps;
44
+ /**
45
+ * Create a new InertiaService instance
46
+ *
47
+ * @param context - The Gravito request context
48
+ * @param config - Optional configuration
49
+ */
50
+ constructor(context: GravitoContext<GravitoVariables>, config?: InertiaConfig);
51
+ /**
52
+ * Escape a string for safe use in HTML attributes
53
+ */
54
+ private escapeForSingleQuotedHtmlAttribute;
55
+ /**
56
+ * Render an Inertia component
57
+ *
58
+ * @param component - The component name to render
59
+ * @param props - Props to pass to the component
60
+ * @param rootVars - Additional variables for the root template
61
+ * @returns HTTP Response
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * return inertia.render('Users/Index', {
66
+ * users: await User.all(),
67
+ * filters: { search: ctx.req.query('search') }
68
+ * })
69
+ * ```
70
+ */
71
+ render(component: string, props?: Record<string, unknown>, rootVars?: Record<string, unknown>): Response;
72
+ /**
73
+ * Share data with all Inertia responses
74
+ *
75
+ * Shared props are merged with component-specific props on every render.
76
+ *
77
+ * @param key - The prop key
78
+ * @param value - The prop value
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // In middleware
83
+ * inertia.share('auth', { user: ctx.get('auth')?.user() })
84
+ * inertia.share('flash', ctx.get('session')?.getFlash('message'))
85
+ * ```
86
+ */
87
+ share(key: string, value: unknown): void;
88
+ /**
89
+ * Share multiple props at once
90
+ *
91
+ * @param props - Object of props to share
92
+ */
93
+ shareAll(props: Record<string, unknown>): void;
94
+ /**
95
+ * Get all shared props
96
+ */
97
+ getSharedProps(): Record<string, unknown>;
98
+ }
99
+ //# sourceMappingURL=InertiaService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InertiaService.d.ts","sourceRoot":"","sources":["../src/InertiaService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAe,MAAM,cAAc,CAAA;AAEjF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAc;IAUvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAVhB,OAAO,CAAC,WAAW,CAA8B;IAEjD;;;;;OAKG;gBAEO,OAAO,EAAE,cAAc,CAAC,gBAAgB,CAAC,EACzC,MAAM,GAAE,aAAkB;IAGpC;;OAEG;IACH,OAAO,CAAC,kCAAkC;IAa1C;;;;;;;;;;;;;;;OAeG;IACI,MAAM,CACX,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,QAAQ;IAmDX;;;;;;;;;;;;;;OAcG;IACI,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI/C;;;;OAIG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIrD;;OAEG;IACI,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGjD"}
package/dist/index.cjs ADDED
@@ -0,0 +1,111 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ default: () => src_default,
33
+ OrbitIon: () => OrbitIon,
34
+ InertiaService: () => InertiaService
35
+ });
36
+ module.exports = __toCommonJS(exports_src);
37
+ var import_gravito_core = require("gravito-core");
38
+
39
+ // src/InertiaService.ts
40
+ class InertiaService {
41
+ context;
42
+ config;
43
+ sharedProps = {};
44
+ constructor(context, config = {}) {
45
+ this.context = context;
46
+ this.config = config;
47
+ }
48
+ escapeForSingleQuotedHtmlAttribute(value) {
49
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
50
+ }
51
+ render(component, props = {}, rootVars = {}) {
52
+ let pageUrl;
53
+ try {
54
+ const reqUrl = new URL(this.context.req.url, "http://localhost");
55
+ pageUrl = reqUrl.pathname + reqUrl.search;
56
+ } catch {
57
+ pageUrl = this.context.req.url;
58
+ }
59
+ const page = {
60
+ component,
61
+ props: { ...this.sharedProps, ...props },
62
+ url: pageUrl,
63
+ version: this.config.version
64
+ };
65
+ if (this.context.req.header("X-Inertia")) {
66
+ this.context.header("X-Inertia", "true");
67
+ this.context.header("Vary", "Accept");
68
+ return this.context.json(page);
69
+ }
70
+ const view = this.context.get("view");
71
+ const rootView = this.config.rootView ?? "app";
72
+ if (!view) {
73
+ throw new Error("OrbitPrism is required for the initial page load in OrbitIon");
74
+ }
75
+ const isDev = true;
76
+ return this.context.html(view.render(rootView, {
77
+ ...rootVars,
78
+ page: this.escapeForSingleQuotedHtmlAttribute(JSON.stringify(page)),
79
+ isDev
80
+ }, { layout: "" }));
81
+ }
82
+ share(key, value) {
83
+ this.sharedProps[key] = value;
84
+ }
85
+ shareAll(props) {
86
+ Object.assign(this.sharedProps, props);
87
+ }
88
+ getSharedProps() {
89
+ return { ...this.sharedProps };
90
+ }
91
+ }
92
+
93
+ // src/index.ts
94
+ class OrbitIon {
95
+ install(core) {
96
+ core.logger.info("\uD83D\uDEF0️ Orbit Inertia installed");
97
+ const appVersion = core.config.get("APP_VERSION", "1.0.0");
98
+ core.adapter.use("*", async (c, next) => {
99
+ const gravitoCtx = new import_gravito_core.HonoContextWrapper(c);
100
+ const inertia = new InertiaService(gravitoCtx, {
101
+ version: String(appVersion),
102
+ rootView: "app"
103
+ });
104
+ c.set("inertia", inertia);
105
+ await next();
106
+ });
107
+ }
108
+ }
109
+ var src_default = OrbitIon;
110
+
111
+ //# debugId=B25C37DA1DF924EE64756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/InertiaService.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @fileoverview Orbit Inertia - Inertia.js integration for Gravito\n *\n * Provides server-side Inertia.js integration for building modern\n * single-page applications with server-side routing.\n *\n * @module @gravito/ion\n * @since 1.0.0\n */\n\nimport type { GravitoContext, GravitoOrbit, GravitoVariables, PlanetCore } from 'gravito-core'\nimport { HonoContextWrapper } from 'gravito-core'\nimport { InertiaService } from './InertiaService'\n\nexport * from './InertiaService'\n\n// Module augmentation for type-safe context injection\n// This extends Hono's ContextVariableMap for backward compat\ndeclare module 'hono' {\n interface ContextVariableMap {\n inertia: InertiaService\n }\n}\n\n/**\n * OrbitIon - Inertia.js integration orbit\n *\n * This orbit provides seamless Inertia.js integration, enabling\n * SPA-like navigation with server-side routing.\n *\n * @example\n * ```typescript\n * import { PlanetCore, defineConfig } from 'gravito-core'\n * import { OrbitIon } from '@gravito/ion'\n *\n * const core = await PlanetCore.boot(defineConfig({\n * orbits: [OrbitIon]\n * }))\n * ```\n */\nexport class OrbitIon implements GravitoOrbit {\n /**\n * Install the Inertia orbit into PlanetCore\n */\n install(core: PlanetCore): void {\n core.logger.info('🛰️ Orbit Inertia installed')\n\n const appVersion = core.config.get('APP_VERSION', '1.0.0')\n\n // Register middleware to inject Inertia helper\n core.adapter.use('*', async (c: any, next: any) => {\n // Create GravitoContext wrapper for InertiaService\n // This allows InertiaService to use the abstraction layer\n const gravitoCtx = new HonoContextWrapper(c) as GravitoContext<GravitoVariables>\n\n // Initialize with config\n const inertia = new InertiaService(gravitoCtx, {\n version: String(appVersion),\n rootView: 'app', // Default to src/views/app.html\n })\n\n c.set('inertia', inertia)\n await next()\n })\n }\n}\n\n/**\n * Default export for convenience\n */\nexport default OrbitIon\n",
6
+ "/**\n * @fileoverview Inertia.js Service for Gravito\n *\n * Provides server-side Inertia.js integration for building modern\n * single-page applications with server-side routing.\n *\n * @module @gravito/ion\n * @since 1.0.0\n */\n\nimport type { GravitoContext, GravitoVariables, ViewService } from 'gravito-core'\n\n/**\n * Configuration options for InertiaService\n */\nexport interface InertiaConfig {\n /**\n * The root view template name\n * @default 'app'\n */\n rootView?: string\n\n /**\n * Asset version for cache busting\n */\n version?: string\n}\n\n/**\n * InertiaService - Server-side Inertia.js adapter\n *\n * This service handles the Inertia.js protocol for seamless\n * SPA-like navigation with server-side routing.\n *\n * @example\n * ```typescript\n * // In a controller\n * async index(ctx: GravitoContext) {\n * const inertia = ctx.get('inertia') as InertiaService\n * return inertia.render('Home', { users: await User.all() })\n * }\n * ```\n */\nexport class InertiaService {\n private sharedProps: Record<string, unknown> = {}\n\n /**\n * Create a new InertiaService instance\n *\n * @param context - The Gravito request context\n * @param config - Optional configuration\n */\n constructor(\n private context: GravitoContext<GravitoVariables>,\n private config: InertiaConfig = {}\n ) {}\n\n /**\n * Escape a string for safe use in HTML attributes\n */\n private escapeForSingleQuotedHtmlAttribute(value: string): string {\n return (\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n // Also escape double quotes so templates can safely use either:\n // data-page='{{{ page }}}' or data-page=\"{{{ page }}}\"\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;')\n )\n }\n\n /**\n * Render an Inertia component\n *\n * @param component - The component name to render\n * @param props - Props to pass to the component\n * @param rootVars - Additional variables for the root template\n * @returns HTTP Response\n *\n * @example\n * ```typescript\n * return inertia.render('Users/Index', {\n * users: await User.all(),\n * filters: { search: ctx.req.query('search') }\n * })\n * ```\n */\n public render(\n component: string,\n props: Record<string, unknown> = {},\n rootVars: Record<string, unknown> = {}\n ): Response {\n // For SSG, use relative URL (pathname only) to avoid cross-origin issues\n let pageUrl: string\n try {\n const reqUrl = new URL(this.context.req.url, 'http://localhost')\n pageUrl = reqUrl.pathname + reqUrl.search\n } catch {\n // Fallback if URL parsing fails\n pageUrl = this.context.req.url\n }\n\n const page = {\n component,\n props: { ...this.sharedProps, ...props },\n url: pageUrl,\n version: this.config.version,\n }\n\n // 1. If it's an Inertia request, return JSON\n if (this.context.req.header('X-Inertia')) {\n this.context.header('X-Inertia', 'true')\n this.context.header('Vary', 'Accept')\n return this.context.json(page)\n }\n\n // 2. Otherwise return the root HTML with data-page attribute\n // We assume there is a ViewService that handles the root template\n // The rootView should contain: <div id=\"app\" data-page='{{{ page }}}'></div>\n const view = this.context.get('view') as ViewService | undefined\n const rootView = this.config.rootView ?? 'app'\n\n if (!view) {\n throw new Error('OrbitPrism is required for the initial page load in OrbitIon')\n }\n\n // Detect development mode\n const isDev = process.env.NODE_ENV !== 'production'\n\n return this.context.html(\n view.render(\n rootView,\n {\n ...rootVars,\n page: this.escapeForSingleQuotedHtmlAttribute(JSON.stringify(page)),\n isDev,\n },\n { layout: '' }\n )\n )\n }\n\n /**\n * Share data with all Inertia responses\n *\n * Shared props are merged with component-specific props on every render.\n *\n * @param key - The prop key\n * @param value - The prop value\n *\n * @example\n * ```typescript\n * // In middleware\n * inertia.share('auth', { user: ctx.get('auth')?.user() })\n * inertia.share('flash', ctx.get('session')?.getFlash('message'))\n * ```\n */\n public share(key: string, value: unknown): void {\n this.sharedProps[key] = value\n }\n\n /**\n * Share multiple props at once\n *\n * @param props - Object of props to share\n */\n public shareAll(props: Record<string, unknown>): void {\n Object.assign(this.sharedProps, props)\n }\n\n /**\n * Get all shared props\n */\n public getSharedProps(): Record<string, unknown> {\n return { ...this.sharedProps }\n }\n}\n"
7
+ ],
8
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWmC,IAAnC;;;ACgCO,MAAM,eAAe;AAAA,EAUhB;AAAA,EACA;AAAA,EAVF,cAAuC,CAAC;AAAA,EAQhD,WAAW,CACD,SACA,SAAwB,CAAC,GACjC;AAAA,IAFQ;AAAA,IACA;AAAA;AAAA,EAMF,kCAAkC,CAAC,OAAuB;AAAA,IAChE,OACE,MACG,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EAGpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAAA;AAAA,EAoBtB,MAAM,CACX,WACA,QAAiC,CAAC,GAClC,WAAoC,CAAC,GAC3B;AAAA,IAEV,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,MAAM,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,kBAAkB;AAAA,MAC/D,UAAU,OAAO,WAAW,OAAO;AAAA,MACnC,MAAM;AAAA,MAEN,UAAU,KAAK,QAAQ,IAAI;AAAA;AAAA,IAG7B,MAAM,OAAO;AAAA,MACX;AAAA,MACA,OAAO,KAAK,KAAK,gBAAgB,MAAM;AAAA,MACvC,KAAK;AAAA,MACL,SAAS,KAAK,OAAO;AAAA,IACvB;AAAA,IAGA,IAAI,KAAK,QAAQ,IAAI,OAAO,WAAW,GAAG;AAAA,MACxC,KAAK,QAAQ,OAAO,aAAa,MAAM;AAAA,MACvC,KAAK,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MACpC,OAAO,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC/B;AAAA,IAKA,MAAM,OAAO,KAAK,QAAQ,IAAI,MAAM;AAAA,IACpC,MAAM,WAAW,KAAK,OAAO,YAAY;AAAA,IAEzC,IAAI,CAAC,MAAM;AAAA,MACT,MAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAAA,IAGA,MAAM,QAAQ;AAAA,IAEd,OAAO,KAAK,QAAQ,KAClB,KAAK,OACH,UACA;AAAA,SACK;AAAA,MACH,MAAM,KAAK,mCAAmC,KAAK,UAAU,IAAI,CAAC;AAAA,MAClE;AAAA,IACF,GACA,EAAE,QAAQ,GAAG,CACf,CACF;AAAA;AAAA,EAkBK,KAAK,CAAC,KAAa,OAAsB;AAAA,IAC9C,KAAK,YAAY,OAAO;AAAA;AAAA,EAQnB,QAAQ,CAAC,OAAsC;AAAA,IACpD,OAAO,OAAO,KAAK,aAAa,KAAK;AAAA;AAAA,EAMhC,cAAc,GAA4B;AAAA,IAC/C,OAAO,KAAK,KAAK,YAAY;AAAA;AAEjC;;;AD1IO,MAAM,SAAiC;AAAA,EAI5C,OAAO,CAAC,MAAwB;AAAA,IAC9B,KAAK,OAAO,KAAK,uCAA4B;AAAA,IAE7C,MAAM,aAAa,KAAK,OAAO,IAAI,eAAe,OAAO;AAAA,IAGzD,KAAK,QAAQ,IAAI,KAAK,OAAO,GAAQ,SAAc;AAAA,MAGjD,MAAM,aAAa,IAAI,uCAAmB,CAAC;AAAA,MAG3C,MAAM,UAAU,IAAI,eAAe,YAAY;AAAA,QAC7C,SAAS,OAAO,UAAU;AAAA,QAC1B,UAAU;AAAA,MACZ,CAAC;AAAA,MAED,EAAE,IAAI,WAAW,OAAO;AAAA,MACxB,MAAM,KAAK;AAAA,KACZ;AAAA;AAEL;AAKA,IAAe;",
9
+ "debugId": "B25C37DA1DF924EE64756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @fileoverview Orbit Inertia - Inertia.js integration for Gravito
3
+ *
4
+ * Provides server-side Inertia.js integration for building modern
5
+ * single-page applications with server-side routing.
6
+ *
7
+ * @module @gravito/ion
8
+ * @since 1.0.0
9
+ */
10
+ import type { GravitoOrbit, PlanetCore } from 'gravito-core';
11
+ import { InertiaService } from './InertiaService';
12
+ export * from './InertiaService';
13
+ declare module 'hono' {
14
+ interface ContextVariableMap {
15
+ inertia: InertiaService;
16
+ }
17
+ }
18
+ /**
19
+ * OrbitIon - Inertia.js integration orbit
20
+ *
21
+ * This orbit provides seamless Inertia.js integration, enabling
22
+ * SPA-like navigation with server-side routing.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { PlanetCore, defineConfig } from 'gravito-core'
27
+ * import { OrbitIon } from '@gravito/ion'
28
+ *
29
+ * const core = await PlanetCore.boot(defineConfig({
30
+ * orbits: [OrbitIon]
31
+ * }))
32
+ * ```
33
+ */
34
+ export declare class OrbitIon implements GravitoOrbit {
35
+ /**
36
+ * Install the Inertia orbit into PlanetCore
37
+ */
38
+ install(core: PlanetCore): void;
39
+ }
40
+ /**
41
+ * Default export for convenience
42
+ */
43
+ export default OrbitIon;
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAkB,YAAY,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9F,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,cAAc,kBAAkB,CAAA;AAIhC,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,OAAO,EAAE,cAAc,CAAA;KACxB;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,QAAS,YAAW,YAAY;IAC3C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;CAqBhC;AAED;;GAEG;AACH,eAAe,QAAQ,CAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,81 @@
1
+ // src/index.ts
2
+ import { HonoContextWrapper } from "gravito-core";
3
+
4
+ // src/InertiaService.ts
5
+ class InertiaService {
6
+ context;
7
+ config;
8
+ sharedProps = {};
9
+ constructor(context, config = {}) {
10
+ this.context = context;
11
+ this.config = config;
12
+ }
13
+ escapeForSingleQuotedHtmlAttribute(value) {
14
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
15
+ }
16
+ render(component, props = {}, rootVars = {}) {
17
+ let pageUrl;
18
+ try {
19
+ const reqUrl = new URL(this.context.req.url, "http://localhost");
20
+ pageUrl = reqUrl.pathname + reqUrl.search;
21
+ } catch {
22
+ pageUrl = this.context.req.url;
23
+ }
24
+ const page = {
25
+ component,
26
+ props: { ...this.sharedProps, ...props },
27
+ url: pageUrl,
28
+ version: this.config.version
29
+ };
30
+ if (this.context.req.header("X-Inertia")) {
31
+ this.context.header("X-Inertia", "true");
32
+ this.context.header("Vary", "Accept");
33
+ return this.context.json(page);
34
+ }
35
+ const view = this.context.get("view");
36
+ const rootView = this.config.rootView ?? "app";
37
+ if (!view) {
38
+ throw new Error("OrbitPrism is required for the initial page load in OrbitIon");
39
+ }
40
+ const isDev = true;
41
+ return this.context.html(view.render(rootView, {
42
+ ...rootVars,
43
+ page: this.escapeForSingleQuotedHtmlAttribute(JSON.stringify(page)),
44
+ isDev
45
+ }, { layout: "" }));
46
+ }
47
+ share(key, value) {
48
+ this.sharedProps[key] = value;
49
+ }
50
+ shareAll(props) {
51
+ Object.assign(this.sharedProps, props);
52
+ }
53
+ getSharedProps() {
54
+ return { ...this.sharedProps };
55
+ }
56
+ }
57
+
58
+ // src/index.ts
59
+ class OrbitIon {
60
+ install(core) {
61
+ core.logger.info("\uD83D\uDEF0️ Orbit Inertia installed");
62
+ const appVersion = core.config.get("APP_VERSION", "1.0.0");
63
+ core.adapter.use("*", async (c, next) => {
64
+ const gravitoCtx = new HonoContextWrapper(c);
65
+ const inertia = new InertiaService(gravitoCtx, {
66
+ version: String(appVersion),
67
+ rootView: "app"
68
+ });
69
+ c.set("inertia", inertia);
70
+ await next();
71
+ });
72
+ }
73
+ }
74
+ var src_default = OrbitIon;
75
+ export {
76
+ src_default as default,
77
+ OrbitIon,
78
+ InertiaService
79
+ };
80
+
81
+ //# debugId=2FCC28A47DC570F964756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/InertiaService.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @fileoverview Orbit Inertia - Inertia.js integration for Gravito\n *\n * Provides server-side Inertia.js integration for building modern\n * single-page applications with server-side routing.\n *\n * @module @gravito/ion\n * @since 1.0.0\n */\n\nimport type { GravitoContext, GravitoOrbit, GravitoVariables, PlanetCore } from 'gravito-core'\nimport { HonoContextWrapper } from 'gravito-core'\nimport { InertiaService } from './InertiaService'\n\nexport * from './InertiaService'\n\n// Module augmentation for type-safe context injection\n// This extends Hono's ContextVariableMap for backward compat\ndeclare module 'hono' {\n interface ContextVariableMap {\n inertia: InertiaService\n }\n}\n\n/**\n * OrbitIon - Inertia.js integration orbit\n *\n * This orbit provides seamless Inertia.js integration, enabling\n * SPA-like navigation with server-side routing.\n *\n * @example\n * ```typescript\n * import { PlanetCore, defineConfig } from 'gravito-core'\n * import { OrbitIon } from '@gravito/ion'\n *\n * const core = await PlanetCore.boot(defineConfig({\n * orbits: [OrbitIon]\n * }))\n * ```\n */\nexport class OrbitIon implements GravitoOrbit {\n /**\n * Install the Inertia orbit into PlanetCore\n */\n install(core: PlanetCore): void {\n core.logger.info('🛰️ Orbit Inertia installed')\n\n const appVersion = core.config.get('APP_VERSION', '1.0.0')\n\n // Register middleware to inject Inertia helper\n core.adapter.use('*', async (c: any, next: any) => {\n // Create GravitoContext wrapper for InertiaService\n // This allows InertiaService to use the abstraction layer\n const gravitoCtx = new HonoContextWrapper(c) as GravitoContext<GravitoVariables>\n\n // Initialize with config\n const inertia = new InertiaService(gravitoCtx, {\n version: String(appVersion),\n rootView: 'app', // Default to src/views/app.html\n })\n\n c.set('inertia', inertia)\n await next()\n })\n }\n}\n\n/**\n * Default export for convenience\n */\nexport default OrbitIon\n",
6
+ "/**\n * @fileoverview Inertia.js Service for Gravito\n *\n * Provides server-side Inertia.js integration for building modern\n * single-page applications with server-side routing.\n *\n * @module @gravito/ion\n * @since 1.0.0\n */\n\nimport type { GravitoContext, GravitoVariables, ViewService } from 'gravito-core'\n\n/**\n * Configuration options for InertiaService\n */\nexport interface InertiaConfig {\n /**\n * The root view template name\n * @default 'app'\n */\n rootView?: string\n\n /**\n * Asset version for cache busting\n */\n version?: string\n}\n\n/**\n * InertiaService - Server-side Inertia.js adapter\n *\n * This service handles the Inertia.js protocol for seamless\n * SPA-like navigation with server-side routing.\n *\n * @example\n * ```typescript\n * // In a controller\n * async index(ctx: GravitoContext) {\n * const inertia = ctx.get('inertia') as InertiaService\n * return inertia.render('Home', { users: await User.all() })\n * }\n * ```\n */\nexport class InertiaService {\n private sharedProps: Record<string, unknown> = {}\n\n /**\n * Create a new InertiaService instance\n *\n * @param context - The Gravito request context\n * @param config - Optional configuration\n */\n constructor(\n private context: GravitoContext<GravitoVariables>,\n private config: InertiaConfig = {}\n ) {}\n\n /**\n * Escape a string for safe use in HTML attributes\n */\n private escapeForSingleQuotedHtmlAttribute(value: string): string {\n return (\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n // Also escape double quotes so templates can safely use either:\n // data-page='{{{ page }}}' or data-page=\"{{{ page }}}\"\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;')\n )\n }\n\n /**\n * Render an Inertia component\n *\n * @param component - The component name to render\n * @param props - Props to pass to the component\n * @param rootVars - Additional variables for the root template\n * @returns HTTP Response\n *\n * @example\n * ```typescript\n * return inertia.render('Users/Index', {\n * users: await User.all(),\n * filters: { search: ctx.req.query('search') }\n * })\n * ```\n */\n public render(\n component: string,\n props: Record<string, unknown> = {},\n rootVars: Record<string, unknown> = {}\n ): Response {\n // For SSG, use relative URL (pathname only) to avoid cross-origin issues\n let pageUrl: string\n try {\n const reqUrl = new URL(this.context.req.url, 'http://localhost')\n pageUrl = reqUrl.pathname + reqUrl.search\n } catch {\n // Fallback if URL parsing fails\n pageUrl = this.context.req.url\n }\n\n const page = {\n component,\n props: { ...this.sharedProps, ...props },\n url: pageUrl,\n version: this.config.version,\n }\n\n // 1. If it's an Inertia request, return JSON\n if (this.context.req.header('X-Inertia')) {\n this.context.header('X-Inertia', 'true')\n this.context.header('Vary', 'Accept')\n return this.context.json(page)\n }\n\n // 2. Otherwise return the root HTML with data-page attribute\n // We assume there is a ViewService that handles the root template\n // The rootView should contain: <div id=\"app\" data-page='{{{ page }}}'></div>\n const view = this.context.get('view') as ViewService | undefined\n const rootView = this.config.rootView ?? 'app'\n\n if (!view) {\n throw new Error('OrbitPrism is required for the initial page load in OrbitIon')\n }\n\n // Detect development mode\n const isDev = process.env.NODE_ENV !== 'production'\n\n return this.context.html(\n view.render(\n rootView,\n {\n ...rootVars,\n page: this.escapeForSingleQuotedHtmlAttribute(JSON.stringify(page)),\n isDev,\n },\n { layout: '' }\n )\n )\n }\n\n /**\n * Share data with all Inertia responses\n *\n * Shared props are merged with component-specific props on every render.\n *\n * @param key - The prop key\n * @param value - The prop value\n *\n * @example\n * ```typescript\n * // In middleware\n * inertia.share('auth', { user: ctx.get('auth')?.user() })\n * inertia.share('flash', ctx.get('session')?.getFlash('message'))\n * ```\n */\n public share(key: string, value: unknown): void {\n this.sharedProps[key] = value\n }\n\n /**\n * Share multiple props at once\n *\n * @param props - Object of props to share\n */\n public shareAll(props: Record<string, unknown>): void {\n Object.assign(this.sharedProps, props)\n }\n\n /**\n * Get all shared props\n */\n public getSharedProps(): Record<string, unknown> {\n return { ...this.sharedProps }\n }\n}\n"
7
+ ],
8
+ "mappings": ";AAWA;;;ACgCO,MAAM,eAAe;AAAA,EAUhB;AAAA,EACA;AAAA,EAVF,cAAuC,CAAC;AAAA,EAQhD,WAAW,CACD,SACA,SAAwB,CAAC,GACjC;AAAA,IAFQ;AAAA,IACA;AAAA;AAAA,EAMF,kCAAkC,CAAC,OAAuB;AAAA,IAChE,OACE,MACG,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EAGpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAAA;AAAA,EAoBtB,MAAM,CACX,WACA,QAAiC,CAAC,GAClC,WAAoC,CAAC,GAC3B;AAAA,IAEV,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,MAAM,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,kBAAkB;AAAA,MAC/D,UAAU,OAAO,WAAW,OAAO;AAAA,MACnC,MAAM;AAAA,MAEN,UAAU,KAAK,QAAQ,IAAI;AAAA;AAAA,IAG7B,MAAM,OAAO;AAAA,MACX;AAAA,MACA,OAAO,KAAK,KAAK,gBAAgB,MAAM;AAAA,MACvC,KAAK;AAAA,MACL,SAAS,KAAK,OAAO;AAAA,IACvB;AAAA,IAGA,IAAI,KAAK,QAAQ,IAAI,OAAO,WAAW,GAAG;AAAA,MACxC,KAAK,QAAQ,OAAO,aAAa,MAAM;AAAA,MACvC,KAAK,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MACpC,OAAO,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC/B;AAAA,IAKA,MAAM,OAAO,KAAK,QAAQ,IAAI,MAAM;AAAA,IACpC,MAAM,WAAW,KAAK,OAAO,YAAY;AAAA,IAEzC,IAAI,CAAC,MAAM;AAAA,MACT,MAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAAA,IAGA,MAAM,QAAQ;AAAA,IAEd,OAAO,KAAK,QAAQ,KAClB,KAAK,OACH,UACA;AAAA,SACK;AAAA,MACH,MAAM,KAAK,mCAAmC,KAAK,UAAU,IAAI,CAAC;AAAA,MAClE;AAAA,IACF,GACA,EAAE,QAAQ,GAAG,CACf,CACF;AAAA;AAAA,EAkBK,KAAK,CAAC,KAAa,OAAsB;AAAA,IAC9C,KAAK,YAAY,OAAO;AAAA;AAAA,EAQnB,QAAQ,CAAC,OAAsC;AAAA,IACpD,OAAO,OAAO,KAAK,aAAa,KAAK;AAAA;AAAA,EAMhC,cAAc,GAA4B;AAAA,IAC/C,OAAO,KAAK,KAAK,YAAY;AAAA;AAEjC;;;AD1IO,MAAM,SAAiC;AAAA,EAI5C,OAAO,CAAC,MAAwB;AAAA,IAC9B,KAAK,OAAO,KAAK,uCAA4B;AAAA,IAE7C,MAAM,aAAa,KAAK,OAAO,IAAI,eAAe,OAAO;AAAA,IAGzD,KAAK,QAAQ,IAAI,KAAK,OAAO,GAAQ,SAAc;AAAA,MAGjD,MAAM,aAAa,IAAI,mBAAmB,CAAC;AAAA,MAG3C,MAAM,UAAU,IAAI,eAAe,YAAY;AAAA,QAC7C,SAAS,OAAO,UAAU;AAAA,QAC1B,UAAU;AAAA,MACZ,CAAC;AAAA,MAED,EAAE,IAAI,WAAW,OAAO;AAAA,MACxB,MAAM,KAAK;AAAA,KACZ;AAAA;AAEL;AAKA,IAAe;",
9
+ "debugId": "2FCC28A47DC570F964756E2164756E21",
10
+ "names": []
11
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@gravito/ion",
3
+ "version": "1.0.0-beta.2",
4
+ "description": "Inertia.js adapter for Gravito",
5
+ "module": "./dist/index.mjs",
6
+ "main": "./dist/index.cjs",
7
+ "type": "module",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "bun run build.ts",
23
+ "test": "bun test",
24
+ "typecheck": "tsc --noEmit"
25
+ },
26
+ "peerDependencies": {
27
+ "gravito-core": "1.0.0-beta.2",
28
+ "hono": "^4.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "gravito-core": "1.0.0-beta.2",
32
+ "hono": "^4.0.0",
33
+ "bun-types": "latest",
34
+ "typescript": "latest"
35
+ },
36
+ "keywords": [
37
+ "gravito",
38
+ "orbit",
39
+ "inertia",
40
+ "react",
41
+ "vue",
42
+ "adapter"
43
+ ],
44
+ "author": "Carl Lee <carllee0520@gmail.com>",
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "homepage": "https://github.com/gravito-framework/gravito#readme",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/gravito-framework/gravito.git",
53
+ "directory": "packages/ion"
54
+ }
55
+ }