@ruvyxa/adapter-netlify 1.0.17 → 1.0.19

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/dist/index.d.ts CHANGED
@@ -7,12 +7,24 @@ export interface NetlifyAdapterOptions {
7
7
  functionsDir?: string;
8
8
  /**
9
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
10
+ * generated publish directory and functions. An existing project
11
+ * `netlify.toml` is never overwritten.
12
+ *
13
+ * Off by default: the adapter already emits the serverless function and
14
+ * cache headers through Netlify's Frameworks API (`.netlify/v1/`, a
15
+ * gitignored build artifact), so the only remaining setup is the publish
16
+ * directory — set once in the Netlify dashboard, or opt into this file.
17
+ * @default false
14
18
  */
15
19
  projectConfig?: boolean;
20
+ /**
21
+ * Emit the Netlify Frameworks API directory (`.netlify/v1/`) at the project
22
+ * root during `ruvyxa build`: the SSR/API function and the immutable cache
23
+ * headers for hashed client bundles. Netlify picks the directory up
24
+ * automatically on the next deploy — no config file at the project root.
25
+ * @default true
26
+ */
27
+ frameworksApi?: boolean;
16
28
  }
17
29
  /**
18
30
  * Create a Netlify deployment adapter for Ruvyxa.
@@ -1 +1 @@
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"}
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;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AA6ED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAmH3E;eAEc,cAAc"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { clientBuildOutput, validateBuildContext } from '@ruvyxa/core';
1
+ import { clientBuildOutput, projectRelativeOutDir, validateBuildContext } from '@ruvyxa/core';
2
2
  /**
3
3
  * Netlify Functions v2 handler source code.
4
4
  *
@@ -10,11 +10,22 @@ function netlifyHandlerSource() {
10
10
  return `import { createHandler, prerenderRelativePath } from './serverless-handler.mjs';
11
11
  import { loadRouteModule } from './route-modules.mjs';
12
12
  import { readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
13
+ import os from 'node:os';
13
14
  import path from 'node:path';
14
15
 
15
16
  const manifestPath = path.join(import.meta.dirname, 'manifest.json');
16
17
  const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
17
18
  const prerenderDir = path.join(import.meta.dirname, 'prerender');
19
+ // The function bundle directory is read-only at runtime; only the platform
20
+ // tmp directory accepts writes. ISR revalidations land there and are read
21
+ // back before the bundled deploy-time prerender output.
22
+ const isrCacheDir = path.join(os.tmpdir(), 'ruvyxa-isr-cache');
23
+
24
+ const readEntry = (htmlPath, revalidate) => {
25
+ const html = readFileSync(htmlPath, 'utf8');
26
+ const stale = Date.now() - statSync(htmlPath).mtimeMs >= revalidate * 1000;
27
+ return { html, stale };
28
+ };
18
29
 
19
30
  const handler = createHandler({
20
31
  routes: manifest.routes,
@@ -22,14 +33,16 @@ const handler = createHandler({
22
33
  importApi: loadRouteModule,
23
34
  readPrerendered: (pathname, revalidate = 60) => {
24
35
  // prerenderRelativePath rejects any request path that cannot be mapped to a
25
- // location inside prerenderDir, so the cache read can never escape it.
36
+ // location inside the cache directories, so reads can never escape them.
26
37
  const relative = prerenderRelativePath(pathname);
27
38
  if (relative === null) return null;
28
39
  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 };
40
+ return readEntry(path.join(isrCacheDir, relative), revalidate);
41
+ } catch {
42
+ // fall through to the bundled prerender output
43
+ }
44
+ try {
45
+ return readEntry(path.join(prerenderDir, relative), revalidate);
33
46
  } catch {
34
47
  return null;
35
48
  }
@@ -37,7 +50,7 @@ const handler = createHandler({
37
50
  writePrerendered: (pathname, html, revalidate) => {
38
51
  const relative = prerenderRelativePath(pathname);
39
52
  if (relative === null) return;
40
- const htmlPath = path.join(prerenderDir, relative);
53
+ const htmlPath = path.join(isrCacheDir, relative);
41
54
  try {
42
55
  mkdirSync(path.dirname(htmlPath), { recursive: true });
43
56
  writeFileSync(htmlPath, html, 'utf8');
@@ -91,17 +104,30 @@ export function netlifyAdapter(options = {}) {
91
104
  build(ctx) {
92
105
  validateBuildContext(ctx, 'netlifyAdapter');
93
106
  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' +
107
+ // Config files are committed or read on other machines; never embed the
108
+ // absolute build-machine outDir in them.
109
+ const relativeOutDir = projectRelativeOutDir(ctx);
110
+ // Hashed client bundles are served under /__ruvyxa/client/ (see the
111
+ // chunk manifest src fields); the immutable header must match that URL.
112
+ const immutableHeaderToml = '[[headers]]\n for = "/__ruvyxa/client/*"\n [headers.values]\n' +
97
113
  ' Cache-Control = "public, max-age=31536000, immutable"\n';
98
- // Project-root netlify.toml pointing at the build output
114
+ // Netlify.toml for the deploy directory
115
+ const netlifyToml = '[build]\n publish = "publish"\n functions = "functions"\n\n' + immutableHeaderToml;
116
+ // Project-root netlify.toml pointing at the build output (opt-in)
99
117
  const projectNetlifyToml = '[build]\n' +
100
118
  ' 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';
119
+ ` publish = "${relativeOutDir}/deploy/netlify/publish"\n` +
120
+ ` functions = "${relativeOutDir}/deploy/netlify/functions"\n\n` +
121
+ immutableHeaderToml;
122
+ // Frameworks API deploy configuration (.netlify/v1/config.json)
123
+ const frameworksApiConfig = JSON.stringify({
124
+ headers: [
125
+ {
126
+ for: '/__ruvyxa/client/*',
127
+ values: { 'Cache-Control': 'public, max-age=31536000, immutable' },
128
+ },
129
+ ],
130
+ }, null, 2);
105
131
  return {
106
132
  name: 'netlify',
107
133
  target: 'serverless',
@@ -112,8 +138,11 @@ export function netlifyAdapter(options = {}) {
112
138
  functionsDir,
113
139
  configFiles: ['netlify.toml'],
114
140
  artifacts: [
115
- // Static assets (pre-rendered pages + client bundles)
116
- { kind: 'static-site', path: 'deploy/netlify/publish' },
141
+ // Static assets (pre-rendered pages + client bundles). `optional`:
142
+ // an API-only or all-SSR app has no prerendered pages; the function
143
+ // still serves every route, so the missing prerender directory must
144
+ // not fail the build.
145
+ { kind: 'static-site', path: 'deploy/netlify/publish', optional: true },
117
146
  // Serverless function bundle
118
147
  {
119
148
  kind: 'function',
@@ -126,9 +155,27 @@ export function netlifyAdapter(options = {}) {
126
155
  path: 'deploy/netlify/netlify.toml',
127
156
  contents: netlifyToml,
128
157
  },
129
- ...(options.projectConfig === false
158
+ // Frameworks API artifacts: Netlify discovers .netlify/v1/ at the
159
+ // project root automatically, so a fresh site needs only the publish
160
+ // directory set once in the dashboard — no committed config file.
161
+ ...(options.frameworksApi === false
130
162
  ? []
131
163
  : [
164
+ {
165
+ kind: 'function',
166
+ path: '.netlify/v1/functions/ruvyxa-handler',
167
+ scope: 'project',
168
+ handlerSource: netlifyHandlerSource(),
169
+ },
170
+ {
171
+ kind: 'file',
172
+ path: '.netlify/v1/config.json',
173
+ scope: 'project',
174
+ contents: frameworksApiConfig + '\n',
175
+ },
176
+ ]),
177
+ ...(options.projectConfig === true
178
+ ? [
132
179
  {
133
180
  kind: 'file',
134
181
  path: 'netlify.toml',
@@ -136,7 +183,8 @@ export function netlifyAdapter(options = {}) {
136
183
  skipIfExists: true,
137
184
  contents: projectNetlifyToml,
138
185
  },
139
- ]),
186
+ ]
187
+ : []),
140
188
  ],
141
189
  };
142
190
  },
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;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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruvyxa/adapter-netlify",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
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.17"
41
+ "@ruvyxa/core": "^1.0.19"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.json",