@gravito/ion 1.0.0-beta.2 → 1.0.0-beta.4
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.zh-TW.md +36 -0
- package/dist/InertiaService.d.ts +13 -0
- package/dist/InertiaService.d.ts.map +1 -1
- package/dist/index.cjs +11 -3
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +11 -3
- package/dist/index.mjs.map +4 -4
- package/package.json +3 -3
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Orbit Inertia
|
|
2
|
+
|
|
3
|
+
> Gravito 的 Inertia.js 轉接器,可用 React/Vue 建立現代化單體應用。
|
|
4
|
+
|
|
5
|
+
## 安裝
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @gravito/ion
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速開始
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { OrbitIon } from '@gravito/ion'
|
|
15
|
+
import { OrbitPrism } from '@gravito/prism'
|
|
16
|
+
|
|
17
|
+
const config = defineConfig({
|
|
18
|
+
orbits: [OrbitPrism, OrbitIon],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Context } from 'hono'
|
|
24
|
+
import { InertiaService } from '@gravito/ion'
|
|
25
|
+
|
|
26
|
+
export class HomeController {
|
|
27
|
+
index = async (c: Context) => {
|
|
28
|
+
const inertia = c.get('inertia') as InertiaService
|
|
29
|
+
|
|
30
|
+
return inertia.render('Home', {
|
|
31
|
+
user: 'Carl',
|
|
32
|
+
stats: { visits: 100 }
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
package/dist/InertiaService.d.ts
CHANGED
|
@@ -50,6 +50,17 @@ export declare class InertiaService {
|
|
|
50
50
|
constructor(context: GravitoContext<GravitoVariables>, config?: InertiaConfig);
|
|
51
51
|
/**
|
|
52
52
|
* Escape a string for safe use in HTML attributes
|
|
53
|
+
*
|
|
54
|
+
* Strategy: JSON.stringify already escapes special characters including
|
|
55
|
+
* quotes as \". We need to escape these for HTML attributes, but we must
|
|
56
|
+
* be careful not to break JSON escape sequences.
|
|
57
|
+
*
|
|
58
|
+
* The solution: Escape backslash-quote sequences (\" from JSON.stringify)
|
|
59
|
+
* as \\" so they become \\" in HTML, which the browser decodes
|
|
60
|
+
* to \\" (valid JSON), not \" (invalid JSON).
|
|
61
|
+
*
|
|
62
|
+
* @param value - The string to escape.
|
|
63
|
+
* @returns The escaped string.
|
|
53
64
|
*/
|
|
54
65
|
private escapeForSingleQuotedHtmlAttribute;
|
|
55
66
|
/**
|
|
@@ -93,6 +104,8 @@ export declare class InertiaService {
|
|
|
93
104
|
shareAll(props: Record<string, unknown>): void;
|
|
94
105
|
/**
|
|
95
106
|
* Get all shared props
|
|
107
|
+
*
|
|
108
|
+
* @returns A shallow copy of the shared props object.
|
|
96
109
|
*/
|
|
97
110
|
getSharedProps(): Record<string, unknown>;
|
|
98
111
|
}
|
|
@@ -1 +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
|
|
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;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kCAAkC;IAc1C;;;;;;;;;;;;;;;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;IA4DX;;;;;;;;;;;;;;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;;;;OAIG;IACI,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGjD"}
|
package/dist/index.cjs
CHANGED
|
@@ -46,7 +46,7 @@ class InertiaService {
|
|
|
46
46
|
this.config = config;
|
|
47
47
|
}
|
|
48
48
|
escapeForSingleQuotedHtmlAttribute(value) {
|
|
49
|
-
return value.replace(/&/g, "&").replace(
|
|
49
|
+
return value.replace(/&/g, "&").replace(/\\"/g, "\\"").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
50
50
|
}
|
|
51
51
|
render(component, props = {}, rootVars = {}) {
|
|
52
52
|
let pageUrl;
|
|
@@ -56,9 +56,16 @@ class InertiaService {
|
|
|
56
56
|
} catch {
|
|
57
57
|
pageUrl = this.context.req.url;
|
|
58
58
|
}
|
|
59
|
+
const resolveProps = (p) => {
|
|
60
|
+
const resolved = {};
|
|
61
|
+
for (const [key, value] of Object.entries(p)) {
|
|
62
|
+
resolved[key] = typeof value === "function" ? value() : value;
|
|
63
|
+
}
|
|
64
|
+
return resolved;
|
|
65
|
+
};
|
|
59
66
|
const page = {
|
|
60
67
|
component,
|
|
61
|
-
props: { ...this.sharedProps, ...props },
|
|
68
|
+
props: resolveProps({ ...this.sharedProps, ...props }),
|
|
62
69
|
url: pageUrl,
|
|
63
70
|
version: this.config.version
|
|
64
71
|
};
|
|
@@ -103,9 +110,10 @@ class OrbitIon {
|
|
|
103
110
|
});
|
|
104
111
|
c.set("inertia", inertia);
|
|
105
112
|
await next();
|
|
113
|
+
return;
|
|
106
114
|
});
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
var src_default = OrbitIon;
|
|
110
118
|
|
|
111
|
-
//# debugId=
|
|
119
|
+
//# debugId=28569892347C834C64756E2164756E21
|
package/dist/index.cjs.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/InertiaService.ts"],
|
|
4
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\
|
|
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
|
|
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\ndeclare module 'gravito-core' {\n interface GravitoVariables {\n /** Inertia.js service for SPA rendering */\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 return undefined\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 * Strategy: JSON.stringify already escapes special characters including\n * quotes as \\\". We need to escape these for HTML attributes, but we must\n * be careful not to break JSON escape sequences.\n *\n * The solution: Escape backslash-quote sequences (\\\" from JSON.stringify)\n * as \\\\" so they become \\\\" in HTML, which the browser decodes\n * to \\\\\" (valid JSON), not \\" (invalid JSON).\n *\n * @param value - The string to escape.\n * @returns The escaped string.\n */\n private escapeForSingleQuotedHtmlAttribute(value: string): string {\n // First escape ampersands to prevent breaking existing HTML entities\n // Then escape backslash-quote sequences (from JSON.stringify) as \\\\"\n // This ensures \\\" becomes \\\\" which decodes to \\\\\" (valid JSON)\n return value\n .replace(/&/g, '&')\n .replace(/\\\\\"/g, '\\\\"') // Escape \\\" as \\\\" (becomes \\\\\" after decode)\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/'/g, ''')\n // Note: We don't escape standalone \" because JSON.stringify already\n // escaped all quotes as \\\", so any remaining \" would be invalid JSON anyway\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 // Resolve lazy props (functions)\n const resolveProps = (p: Record<string, unknown>) => {\n const resolved: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(p)) {\n resolved[key] = typeof value === 'function' ? value() : value\n }\n return resolved\n }\n\n const page = {\n component,\n props: resolveProps({ ...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 * @returns A shallow copy of the shared props object.\n */\n public getSharedProps(): Record<string, unknown> {\n return { ...this.sharedProps }\n }\n}\n"
|
|
7
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,
|
|
9
|
-
"debugId": "
|
|
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,EAiBF,kCAAkC,CAAC,OAAuB;AAAA,IAIhE,OAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,QAAQ,UAAU,EAC1B,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAAA;AAAA,EAqBpB,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,IAI7B,MAAM,eAAe,CAAC,MAA+B;AAAA,MACnD,MAAM,WAAoC,CAAC;AAAA,MAC3C,YAAY,KAAK,UAAU,OAAO,QAAQ,CAAC,GAAG;AAAA,QAC5C,SAAS,OAAO,OAAO,UAAU,aAAa,MAAM,IAAI;AAAA,MAC1D;AAAA,MACA,OAAO;AAAA;AAAA,IAGT,MAAM,OAAO;AAAA,MACX;AAAA,MACA,OAAO,aAAa,KAAK,KAAK,gBAAgB,MAAM,CAAC;AAAA,MACrD,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,EAQhC,cAAc,GAA4B;AAAA,IAC/C,OAAO,KAAK,KAAK,YAAY;AAAA;AAEjC;;;ADjKO,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,MACX;AAAA,KACD;AAAA;AAEL;AAKA,IAAe;",
|
|
9
|
+
"debugId": "28569892347C834C64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
import type { GravitoOrbit, PlanetCore } from 'gravito-core';
|
|
11
11
|
import { InertiaService } from './InertiaService';
|
|
12
12
|
export * from './InertiaService';
|
|
13
|
-
declare module '
|
|
14
|
-
interface
|
|
15
|
-
|
|
13
|
+
declare module 'gravito-core' {
|
|
14
|
+
interface GravitoVariables {
|
|
15
|
+
/** Inertia.js service for SPA rendering */
|
|
16
|
+
inertia?: InertiaService;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
/**
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;AAGhC,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,2CAA2C;QAC3C,OAAO,CAAC,EAAE,cAAc,CAAA;KACzB;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,QAAS,YAAW,YAAY;IAC3C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;CAsBhC;AAED;;GAEG;AACH,eAAe,QAAQ,CAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ class InertiaService {
|
|
|
11
11
|
this.config = config;
|
|
12
12
|
}
|
|
13
13
|
escapeForSingleQuotedHtmlAttribute(value) {
|
|
14
|
-
return value.replace(/&/g, "&").replace(
|
|
14
|
+
return value.replace(/&/g, "&").replace(/\\"/g, "\\"").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
15
15
|
}
|
|
16
16
|
render(component, props = {}, rootVars = {}) {
|
|
17
17
|
let pageUrl;
|
|
@@ -21,9 +21,16 @@ class InertiaService {
|
|
|
21
21
|
} catch {
|
|
22
22
|
pageUrl = this.context.req.url;
|
|
23
23
|
}
|
|
24
|
+
const resolveProps = (p) => {
|
|
25
|
+
const resolved = {};
|
|
26
|
+
for (const [key, value] of Object.entries(p)) {
|
|
27
|
+
resolved[key] = typeof value === "function" ? value() : value;
|
|
28
|
+
}
|
|
29
|
+
return resolved;
|
|
30
|
+
};
|
|
24
31
|
const page = {
|
|
25
32
|
component,
|
|
26
|
-
props: { ...this.sharedProps, ...props },
|
|
33
|
+
props: resolveProps({ ...this.sharedProps, ...props }),
|
|
27
34
|
url: pageUrl,
|
|
28
35
|
version: this.config.version
|
|
29
36
|
};
|
|
@@ -68,6 +75,7 @@ class OrbitIon {
|
|
|
68
75
|
});
|
|
69
76
|
c.set("inertia", inertia);
|
|
70
77
|
await next();
|
|
78
|
+
return;
|
|
71
79
|
});
|
|
72
80
|
}
|
|
73
81
|
}
|
|
@@ -78,4 +86,4 @@ export {
|
|
|
78
86
|
InertiaService
|
|
79
87
|
};
|
|
80
88
|
|
|
81
|
-
//# debugId=
|
|
89
|
+
//# debugId=7A0FC6E9ACB58F0F64756E2164756E21
|
package/dist/index.mjs.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/InertiaService.ts"],
|
|
4
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\
|
|
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
|
|
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\ndeclare module 'gravito-core' {\n interface GravitoVariables {\n /** Inertia.js service for SPA rendering */\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 return undefined\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 * Strategy: JSON.stringify already escapes special characters including\n * quotes as \\\". We need to escape these for HTML attributes, but we must\n * be careful not to break JSON escape sequences.\n *\n * The solution: Escape backslash-quote sequences (\\\" from JSON.stringify)\n * as \\\\" so they become \\\\" in HTML, which the browser decodes\n * to \\\\\" (valid JSON), not \\" (invalid JSON).\n *\n * @param value - The string to escape.\n * @returns The escaped string.\n */\n private escapeForSingleQuotedHtmlAttribute(value: string): string {\n // First escape ampersands to prevent breaking existing HTML entities\n // Then escape backslash-quote sequences (from JSON.stringify) as \\\\"\n // This ensures \\\" becomes \\\\" which decodes to \\\\\" (valid JSON)\n return value\n .replace(/&/g, '&')\n .replace(/\\\\\"/g, '\\\\"') // Escape \\\" as \\\\" (becomes \\\\\" after decode)\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/'/g, ''')\n // Note: We don't escape standalone \" because JSON.stringify already\n // escaped all quotes as \\\", so any remaining \" would be invalid JSON anyway\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 // Resolve lazy props (functions)\n const resolveProps = (p: Record<string, unknown>) => {\n const resolved: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(p)) {\n resolved[key] = typeof value === 'function' ? value() : value\n }\n return resolved\n }\n\n const page = {\n component,\n props: resolveProps({ ...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 * @returns A shallow copy of the shared props object.\n */\n public getSharedProps(): Record<string, unknown> {\n return { ...this.sharedProps }\n }\n}\n"
|
|
7
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,
|
|
9
|
-
"debugId": "
|
|
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,EAiBF,kCAAkC,CAAC,OAAuB;AAAA,IAIhE,OAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,QAAQ,UAAU,EAC1B,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAAA;AAAA,EAqBpB,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,IAI7B,MAAM,eAAe,CAAC,MAA+B;AAAA,MACnD,MAAM,WAAoC,CAAC;AAAA,MAC3C,YAAY,KAAK,UAAU,OAAO,QAAQ,CAAC,GAAG;AAAA,QAC5C,SAAS,OAAO,OAAO,UAAU,aAAa,MAAM,IAAI;AAAA,MAC1D;AAAA,MACA,OAAO;AAAA;AAAA,IAGT,MAAM,OAAO;AAAA,MACX;AAAA,MACA,OAAO,aAAa,KAAK,KAAK,gBAAgB,MAAM,CAAC;AAAA,MACrD,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,EAQhC,cAAc,GAA4B;AAAA,IAC/C,OAAO,KAAK,KAAK,YAAY;AAAA;AAEjC;;;ADjKO,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,MACX;AAAA,KACD;AAAA;AAEL;AAKA,IAAe;",
|
|
9
|
+
"debugId": "7A0FC6E9ACB58F0F64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravito/ion",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "Inertia.js adapter for Gravito",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"typecheck": "tsc --noEmit"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"gravito-core": "1.0.0-beta.
|
|
27
|
+
"gravito-core": "1.0.0-beta.4",
|
|
28
28
|
"hono": "^4.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"gravito-core": "1.0.0-beta.
|
|
31
|
+
"gravito-core": "1.0.0-beta.4",
|
|
32
32
|
"hono": "^4.0.0",
|
|
33
33
|
"bun-types": "latest",
|
|
34
34
|
"typescript": "latest"
|