@ruvyxa/adapter-netlify 1.0.16 → 1.0.18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ruvyxa/adapter-netlify
2
2
 
3
- Netlify static deployment adapter for Ruvyxa production builds.
3
+ Netlify deployment adapter for Ruvyxa production builds.
4
4
 
5
5
  ## Install
6
6
 
@@ -35,8 +35,11 @@ export default config({
35
35
  }
36
36
  ```
37
37
 
38
- `ruvyxa build` creates `.ruvyxa/deploy/netlify/` with `publish/` and `netlify.toml`. Deploy that
39
- directory on Netlify.
38
+ `ruvyxa build` creates `.ruvyxa/deploy/netlify/` with `publish/`, Netlify Functions handlers, and
39
+ `netlify.toml`. Deploy that directory on Netlify.
40
40
 
41
- This adapter safely supports static SSG/CSR output only. It rejects API, SSR, ISR, and PPR routes
42
- with `RUV2202`; no Netlify Function handler is generated.
41
+ This adapter supports SSR, API, ISR, PPR, SSG, and CSR routes via the serverless runtime. Static
42
+ assets and pre-rendered pages are served through Netlify's publish directory; dynamic routes are
43
+ handled by Netlify Functions. Function output contains a compiled `.mjs` static route registry. ISR
44
+ checks file age against `revalidate` and coalesces concurrent stale refreshes within a warm function
45
+ instance.
package/dist/index.d.ts CHANGED
@@ -1,16 +1,25 @@
1
1
  import type { Adapter } from '@ruvyxa/core';
2
2
  /**
3
- * Options for Netlify metadata compatibility.
3
+ * Options for Netlify deployment.
4
4
  */
5
5
  export interface NetlifyAdapterOptions {
6
6
  /** Custom Netlify functions directory. Defaults to `${outDir}/netlify/functions`. */
7
7
  functionsDir?: string;
8
+ /**
9
+ * Also emit a `netlify.toml` at the project root pointing Netlify at the
10
+ * generated publish directory and functions, so a fresh site deploys
11
+ * without dashboard configuration. An existing project `netlify.toml` is
12
+ * never overwritten.
13
+ * @default true
14
+ */
15
+ projectConfig?: boolean;
8
16
  }
9
17
  /**
10
- * Create a Netlify static deployment adapter for Ruvyxa.
18
+ * Create a Netlify deployment adapter for Ruvyxa.
11
19
  *
12
- * Produces static assets and a `netlify.toml` config reference. Dynamic
13
- * serverless function output is rejected until a Netlify request handler exists.
20
+ * Produces a Functions v2 handler (Web-standard Request/Response) and static
21
+ * assets for deployment via Netlify CLI. Supports SSR, API routes, ISR, PPR,
22
+ * SSG, and CSR.
14
23
  *
15
24
  * @example
16
25
  * ```ts
@@ -18,7 +27,7 @@ export interface NetlifyAdapterOptions {
18
27
  * import { netlifyAdapter } from "@ruvyxa/adapter-netlify"
19
28
  *
20
29
  * export default config({
21
- * adapter: netlifyAdapter({ functionsDir: "netlify/functions" })
30
+ * adapter: netlifyAdapter()
22
31
  * })
23
32
  * ```
24
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA+B,MAAM,cAAc,CAAA;AAGxE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAqC3E;eAEc,cAAc"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgD,MAAM,cAAc,CAAA;AAGzF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAgED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAyE3E;eAEc,cAAc"}
package/dist/index.js CHANGED
@@ -1,9 +1,71 @@
1
1
  import { clientBuildOutput, validateBuildContext } from '@ruvyxa/core';
2
2
  /**
3
- * Create a Netlify static deployment adapter for Ruvyxa.
3
+ * Netlify Functions v2 handler source code.
4
4
  *
5
- * Produces static assets and a `netlify.toml` config reference. Dynamic
6
- * serverless function output is rejected until a Netlify request handler exists.
5
+ * Wraps the generic Ruvyxa serverless handler into a Netlify Functions v2
6
+ * handler using the Web-standard Request/Response API. Reads the route
7
+ * manifest and handles SSR/API/ISR/PPR requests.
8
+ */
9
+ function netlifyHandlerSource() {
10
+ return `import { createHandler, prerenderRelativePath } from './serverless-handler.mjs';
11
+ import { loadRouteModule } from './route-modules.mjs';
12
+ import { readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
13
+ import path from 'node:path';
14
+
15
+ const manifestPath = path.join(import.meta.dirname, 'manifest.json');
16
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
17
+ const prerenderDir = path.join(import.meta.dirname, 'prerender');
18
+
19
+ const handler = createHandler({
20
+ routes: manifest.routes,
21
+ importPage: loadRouteModule,
22
+ importApi: loadRouteModule,
23
+ readPrerendered: (pathname, revalidate = 60) => {
24
+ // prerenderRelativePath rejects any request path that cannot be mapped to a
25
+ // location inside prerenderDir, so the cache read can never escape it.
26
+ const relative = prerenderRelativePath(pathname);
27
+ if (relative === null) return null;
28
+ try {
29
+ const htmlPath = path.join(prerenderDir, relative);
30
+ const html = readFileSync(htmlPath, 'utf8');
31
+ const stale = Date.now() - statSync(htmlPath).mtimeMs >= revalidate * 1000;
32
+ return { html, stale };
33
+ } catch {
34
+ return null;
35
+ }
36
+ },
37
+ writePrerendered: (pathname, html, revalidate) => {
38
+ const relative = prerenderRelativePath(pathname);
39
+ if (relative === null) return;
40
+ const htmlPath = path.join(prerenderDir, relative);
41
+ try {
42
+ mkdirSync(path.dirname(htmlPath), { recursive: true });
43
+ writeFileSync(htmlPath, html, 'utf8');
44
+ } catch {
45
+ // ISR cache write failures are non-fatal
46
+ }
47
+ },
48
+ supportedStrategies: ['ssr', 'ssg', 'csr', 'isr', 'ppr', 'api'],
49
+ });
50
+
51
+ // Netlify Functions v2 — Web-standard Request/Response
52
+ export default async function(request, context) {
53
+ return handler(request, context);
54
+ }
55
+
56
+ // Netlify Functions v2 config
57
+ export const config = {
58
+ path: '/*',
59
+ preferStatic: true,
60
+ };
61
+ `;
62
+ }
63
+ /**
64
+ * Create a Netlify deployment adapter for Ruvyxa.
65
+ *
66
+ * Produces a Functions v2 handler (Web-standard Request/Response) and static
67
+ * assets for deployment via Netlify CLI. Supports SSR, API routes, ISR, PPR,
68
+ * SSG, and CSR.
7
69
  *
8
70
  * @example
9
71
  * ```ts
@@ -11,7 +73,7 @@ import { clientBuildOutput, validateBuildContext } from '@ruvyxa/core';
11
73
  * import { netlifyAdapter } from "@ruvyxa/adapter-netlify"
12
74
  *
13
75
  * export default config({
14
- * adapter: netlifyAdapter({ functionsDir: "netlify/functions" })
76
+ * adapter: netlifyAdapter()
15
77
  * })
16
78
  * ```
17
79
  */
@@ -25,9 +87,21 @@ export function netlifyAdapter(options = {}) {
25
87
  return {
26
88
  name: 'netlify',
27
89
  target: 'serverless',
90
+ supports: ['ssr', 'ssg', 'csr', 'isr', 'ppr', 'api'],
28
91
  build(ctx) {
29
92
  validateBuildContext(ctx, 'netlifyAdapter');
30
93
  const functionsDir = options.functionsDir ?? `${ctx.outDir}/netlify/functions`;
94
+ // Netlify.toml for the deploy directory
95
+ const netlifyToml = '[build]\n publish = "publish"\n functions = "functions"\n\n' +
96
+ '[[headers]]\n for = "/client/*"\n [headers.values]\n' +
97
+ ' Cache-Control = "public, max-age=31536000, immutable"\n';
98
+ // Project-root netlify.toml pointing at the build output
99
+ const projectNetlifyToml = '[build]\n' +
100
+ ' command = "npx --no-install ruvyxa build"\n' +
101
+ ` publish = "${ctx.outDir}/deploy/netlify/publish"\n` +
102
+ ` functions = "${ctx.outDir}/deploy/netlify/functions"\n\n` +
103
+ '[[headers]]\n for = "/client/*"\n [headers.values]\n' +
104
+ ' Cache-Control = "public, max-age=31536000, immutable"\n';
31
105
  return {
32
106
  name: 'netlify',
33
107
  target: 'serverless',
@@ -38,12 +112,31 @@ export function netlifyAdapter(options = {}) {
38
112
  functionsDir,
39
113
  configFiles: ['netlify.toml'],
40
114
  artifacts: [
115
+ // Static assets (pre-rendered pages + client bundles)
41
116
  { kind: 'static-site', path: 'deploy/netlify/publish' },
117
+ // Serverless function bundle
118
+ {
119
+ kind: 'function',
120
+ path: 'deploy/netlify/functions/ruvyxa-handler',
121
+ handlerSource: netlifyHandlerSource(),
122
+ },
123
+ // Netlify config for the deploy directory
42
124
  {
43
125
  kind: 'file',
44
126
  path: 'deploy/netlify/netlify.toml',
45
- contents: '[build]\n publish = "publish"\n',
127
+ contents: netlifyToml,
46
128
  },
129
+ ...(options.projectConfig === false
130
+ ? []
131
+ : [
132
+ {
133
+ kind: 'file',
134
+ path: 'netlify.toml',
135
+ scope: 'project',
136
+ skipIfExists: true,
137
+ contents: projectNetlifyToml,
138
+ },
139
+ ]),
47
140
  ],
48
141
  };
49
142
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAUtE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,GAA0B,EAAE;IAChE,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnF,MAAM,IAAI,KAAK,CACb,kEAAkE,OAAO,OAAO,CAAC,YAAY,EAAE,CAChG,CAAA;IACH,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;IACzF,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,YAAY;QACpB,KAAK,CAAC,GAAiB;YACrB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAA;YAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,GAAG,GAAG,CAAC,MAAM,oBAAoB,CAAA;YAC9E,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,aAAa;gBACjC,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,SAAS;gBACjC,GAAG,iBAAiB,CAAC,GAAG,CAAC;gBACzB,YAAY;gBACZ,WAAW,EAAE,CAAC,cAAc,CAAC;gBAC7B,SAAS,EAAE;oBACT,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,wBAAwB,EAAE;oBACvD;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,6BAA6B;wBACnC,QAAQ,EAAE,kCAAkC;qBAC7C;iBACF;aACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAkBtE;;;;;;GAMG;AACH,SAAS,oBAAoB;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDR,CAAA;AACD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,GAA0B,EAAE;IAChE,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnF,MAAM,IAAI,KAAK,CACb,kEAAkE,OAAO,OAAO,CAAC,YAAY,EAAE,CAChG,CAAA;IACH,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;IACzF,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,YAAY;QACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;QACpD,KAAK,CAAC,GAAiB;YACrB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAA;YAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,GAAG,GAAG,CAAC,MAAM,oBAAoB,CAAA;YAE9E,wCAAwC;YACxC,MAAM,WAAW,GACf,+DAA+D;gBAC/D,wDAAwD;gBACxD,6DAA6D,CAAA;YAE/D,yDAAyD;YACzD,MAAM,kBAAkB,GACtB,WAAW;gBACX,+CAA+C;gBAC/C,gBAAgB,GAAG,CAAC,MAAM,4BAA4B;gBACtD,kBAAkB,GAAG,CAAC,MAAM,gCAAgC;gBAC5D,wDAAwD;gBACxD,6DAA6D,CAAA;YAE/D,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,aAAa;gBACjC,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,SAAS;gBACjC,GAAG,iBAAiB,CAAC,GAAG,CAAC;gBACzB,YAAY;gBACZ,WAAW,EAAE,CAAC,cAAc,CAAC;gBAC7B,SAAS,EAAE;oBACT,sDAAsD;oBACtD,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,wBAAwB,EAAE;oBACvD,6BAA6B;oBAC7B;wBACE,IAAI,EAAE,UAAU;wBAChB,IAAI,EAAE,yCAAyC;wBAC/C,aAAa,EAAE,oBAAoB,EAAE;qBACtC;oBACD,0CAA0C;oBAC1C;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,6BAA6B;wBACnC,QAAQ,EAAE,WAAW;qBACtB;oBACD,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,KAAK;wBACjC,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC;4BACE;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,cAAc;gCACpB,KAAK,EAAE,SAAS;gCAChB,YAAY,EAAE,IAAI;gCAClB,QAAQ,EAAE,kBAAkB;6BACH;yBAC5B,CAAC;iBACP;aACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,eAAe,cAAc,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruvyxa/adapter-netlify",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Shape Ruvyxa builds for Netlify functions and assets with a small typed adapter contract.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  }
39
39
  },
40
40
  "dependencies": {
41
- "@ruvyxa/core": "^1.0.16"
41
+ "@ruvyxa/core": "^1.0.18"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.json",