@ruvyxa/adapter-netlify 1.1.3 → 1.1.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/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +54 -31
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAgD,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAgD,MAAM,cAAc,CAAA;AAgBzF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AA8ND;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CA8JpE;eAEc,OAAO"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { CLIENT_BUNDLE_PREFIX, clientBuildOutput, DEFAULT_SECURITY_HEADERS, IMMUTABLE_CACHE_CONTROL, nonPublishableStrategies, projectRelativeOutDir, PUBLIC_ASSET_CACHE_CONTROL, publicAssetGlobs, runtimeBuildPolicy, validateBuildContext, } from '@ruvyxa/core';
|
|
2
|
+
import { CLIENT_BUNDLE_PREFIX, clientBuildOutput, documentCacheOptionsSource, isrTemporaryCacheSource, DEFAULT_SECURITY_HEADERS, IMMUTABLE_CACHE_CONTROL, nonPublishableStrategies, projectRelativeOutDir, PUBLIC_ASSET_CACHE_CONTROL, publicAssetGlobs, runtimeBuildPolicy, validateBuildContext, } from '@ruvyxa/core';
|
|
3
3
|
/**
|
|
4
4
|
* Netlify Functions v2 handler source code.
|
|
5
5
|
*
|
|
@@ -7,9 +7,9 @@ import { CLIENT_BUNDLE_PREFIX, clientBuildOutput, DEFAULT_SECURITY_HEADERS, IMMU
|
|
|
7
7
|
* handler using the Web-standard Request/Response API. Reads the route
|
|
8
8
|
* manifest and handles SSR/API/ISR/PPR requests.
|
|
9
9
|
*/
|
|
10
|
-
function netlifyHandlerSource(runtimePolicy, functionConfig) {
|
|
10
|
+
function netlifyHandlerSource(runtimePolicy, functionConfig, buildId) {
|
|
11
11
|
return `import { createHandler, prerenderRelativePath } from './serverless-handler.mjs';
|
|
12
|
-
import { applyPluginHttp, loadActionModule, loadRouteModule } from './route-modules.mjs';
|
|
12
|
+
import { applyPluginHttp, documentCacheHandler, loadActionModule, loadRouteModule } from './route-modules.mjs';
|
|
13
13
|
// Netlify bundles the function with esbuild, so anything the deployed code
|
|
14
14
|
// needs must be reachable through the module graph. A sibling manifest.json
|
|
15
15
|
// read from import.meta.dirname is not, and never reaches /var/task.
|
|
@@ -27,10 +27,7 @@ const runtimePolicy = ${JSON.stringify(runtimePolicy ?? {})};
|
|
|
27
27
|
// serves SSG pages from the publish directory before the function is ever
|
|
28
28
|
// invoked (config.preferStatic).
|
|
29
29
|
const prerenderDir = path.join(import.meta.dirname, 'prerender');
|
|
30
|
-
|
|
31
|
-
// tmp directory accepts writes. ISR revalidations land there and are read
|
|
32
|
-
// back before the bundled deploy-time prerender output.
|
|
33
|
-
const isrCacheDir = path.join(os.tmpdir(), 'ruvyxa-isr-cache');
|
|
30
|
+
${isrTemporaryCacheSource(buildId)}
|
|
34
31
|
|
|
35
32
|
/**
|
|
36
33
|
* The cache tag a document is stored under, so one path can be dropped from
|
|
@@ -73,6 +70,35 @@ const readEntry = (htmlPath, revalidate) => {
|
|
|
73
70
|
return { html, stale };
|
|
74
71
|
};
|
|
75
72
|
|
|
73
|
+
// Named rather than passed inline, so a project that declares \`cache.handler\`
|
|
74
|
+
// can stand its own store in front of these. See \`documentCacheOptionsSource\`
|
|
75
|
+
// in \`@ruvyxa/core\`; the registry supplies \`documentCacheHandler\`.
|
|
76
|
+
const platformReadPrerendered = (pathname, revalidate = 60) => {
|
|
77
|
+
// prerenderRelativePath rejects any request path that cannot be mapped to a
|
|
78
|
+
// location inside the cache directories, so reads can never escape them.
|
|
79
|
+
const relative = prerenderRelativePath(pathname);
|
|
80
|
+
if (relative === null) return null;
|
|
81
|
+
try {
|
|
82
|
+
return readEntry(path.join(isrCacheDir, relative), revalidate);
|
|
83
|
+
} catch {
|
|
84
|
+
// fall through to the bundled prerender output
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return readEntry(path.join(prerenderDir, relative), revalidate);
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const platformWritePrerendered = (pathname, html, revalidate, forced) => {
|
|
94
|
+
const relative = prerenderRelativePath(pathname);
|
|
95
|
+
if (relative === null) return;
|
|
96
|
+
const htmlPath = path.join(isrCacheDir, relative);
|
|
97
|
+
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
98
|
+
writeFileSync(htmlPath, html, 'utf8');
|
|
99
|
+
if (forced === true) return purgeDurableCache(pathname);
|
|
100
|
+
};
|
|
101
|
+
|
|
76
102
|
const handler = createHandler({
|
|
77
103
|
routes: manifest.routes,
|
|
78
104
|
middleware: runtimePolicy.middleware,
|
|
@@ -82,35 +108,21 @@ const handler = createHandler({
|
|
|
82
108
|
importAction: loadActionModule,
|
|
83
109
|
pluginHttp: applyPluginHttp,
|
|
84
110
|
security: runtimePolicy.security,
|
|
85
|
-
|
|
86
|
-
// prerenderRelativePath rejects any request path that cannot be mapped to a
|
|
87
|
-
// location inside the cache directories, so reads can never escape them.
|
|
88
|
-
const relative = prerenderRelativePath(pathname);
|
|
89
|
-
if (relative === null) return null;
|
|
90
|
-
try {
|
|
91
|
-
return readEntry(path.join(isrCacheDir, relative), revalidate);
|
|
92
|
-
} catch {
|
|
93
|
-
// fall through to the bundled prerender output
|
|
94
|
-
}
|
|
95
|
-
try {
|
|
96
|
-
return readEntry(path.join(prerenderDir, relative), revalidate);
|
|
97
|
-
} catch {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
writePrerendered: (pathname, html, revalidate, forced) => {
|
|
102
|
-
const relative = prerenderRelativePath(pathname);
|
|
103
|
-
if (relative === null) return;
|
|
104
|
-
const htmlPath = path.join(isrCacheDir, relative);
|
|
105
|
-
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
106
|
-
writeFileSync(htmlPath, html, 'utf8');
|
|
107
|
-
if (forced === true) return purgeDurableCache(pathname);
|
|
108
|
-
},
|
|
111
|
+
${documentCacheOptionsSource('platformReadPrerendered', 'platformWritePrerendered')}
|
|
109
112
|
// The project's own not-found page, pre-rendered by the build and carried
|
|
110
113
|
// inline in the manifest: an unmatched URL is answered with the page the
|
|
111
114
|
// application actually wrote, on every host.
|
|
112
115
|
notFoundDocument: manifest.notFoundDocument,
|
|
113
116
|
supportedStrategies: ['ssr', 'ssg', 'csr', 'isr', 'ppr', 'api'],
|
|
117
|
+
// Netlify's own edge writes \`X-Nf-Client-Connection-IP\` and overwrites any
|
|
118
|
+
// inbound copy of it, so on this platform the header *is* the client.
|
|
119
|
+
// Declared here rather than assumed by the handler: the same handler runs on
|
|
120
|
+
// a self-hosted server where nothing writes it. Without a declaration
|
|
121
|
+
// identity fell through to a right-to-left scan of \`X-Forwarded-For\`, whose
|
|
122
|
+
// rightmost entry here is a fixed platform address — so the built-in \`rate\`
|
|
123
|
+
// middleware, the server-action rate limiter, and the replay quota all
|
|
124
|
+
// counted every visitor into one bucket.
|
|
125
|
+
clientIpHeaders: ['x-nf-client-connection-ip'],
|
|
114
126
|
});
|
|
115
127
|
|
|
116
128
|
// Netlify Functions v2 — Web-standard Request/Response
|
|
@@ -272,7 +284,7 @@ export function netlify(options = {}) {
|
|
|
272
284
|
{
|
|
273
285
|
kind: 'function',
|
|
274
286
|
path: 'deploy/netlify/functions/ruvyxa-handler',
|
|
275
|
-
handlerSource: netlifyHandlerSource(runtimePolicy, functionConfigFor()),
|
|
287
|
+
handlerSource: netlifyHandlerSource(runtimePolicy, functionConfigFor(), ctx.deployManifest?.buildId ?? ''),
|
|
276
288
|
},
|
|
277
289
|
// Netlify config for the deploy directory
|
|
278
290
|
{
|
|
@@ -289,7 +301,7 @@ export function netlify(options = {}) {
|
|
|
289
301
|
kind: 'function',
|
|
290
302
|
path: '.netlify/v1/functions/ruvyxa-handler',
|
|
291
303
|
scope: 'project',
|
|
292
|
-
handlerSource: netlifyHandlerSource(runtimePolicy, functionConfigFor()),
|
|
304
|
+
handlerSource: netlifyHandlerSource(runtimePolicy, functionConfigFor(), ctx.deployManifest?.buildId ?? ''),
|
|
293
305
|
},
|
|
294
306
|
{
|
|
295
307
|
kind: 'file',
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGtC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,cAAc,CAAA;AA0CrB;;;;;;GAMG;AACH,SAAS,oBAAoB,CAC3B,aAAsB,EACtB,cAAqC;
|
|
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.1.
|
|
3
|
+
"version": "1.1.4",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@ruvyxa/core": "^1.1.
|
|
59
|
+
"@ruvyxa/core": "^1.1.4"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "24.13.3"
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { Adapter, AdapterArtifact, AdapterOutput, BuildContext } from '@ruv
|
|
|
4
4
|
import {
|
|
5
5
|
CLIENT_BUNDLE_PREFIX,
|
|
6
6
|
clientBuildOutput,
|
|
7
|
+
documentCacheOptionsSource,
|
|
8
|
+
isrTemporaryCacheSource,
|
|
7
9
|
DEFAULT_SECURITY_HEADERS,
|
|
8
10
|
IMMUTABLE_CACHE_CONTROL,
|
|
9
11
|
nonPublishableStrategies,
|
|
@@ -64,9 +66,10 @@ export interface NetlifyAdapterOptions {
|
|
|
64
66
|
function netlifyHandlerSource(
|
|
65
67
|
runtimePolicy: unknown,
|
|
66
68
|
functionConfig: NetlifyFunctionConfig,
|
|
69
|
+
buildId: string,
|
|
67
70
|
): string {
|
|
68
71
|
return `import { createHandler, prerenderRelativePath } from './serverless-handler.mjs';
|
|
69
|
-
import { applyPluginHttp, loadActionModule, loadRouteModule } from './route-modules.mjs';
|
|
72
|
+
import { applyPluginHttp, documentCacheHandler, loadActionModule, loadRouteModule } from './route-modules.mjs';
|
|
70
73
|
// Netlify bundles the function with esbuild, so anything the deployed code
|
|
71
74
|
// needs must be reachable through the module graph. A sibling manifest.json
|
|
72
75
|
// read from import.meta.dirname is not, and never reaches /var/task.
|
|
@@ -84,10 +87,7 @@ const runtimePolicy = ${JSON.stringify(runtimePolicy ?? {})};
|
|
|
84
87
|
// serves SSG pages from the publish directory before the function is ever
|
|
85
88
|
// invoked (config.preferStatic).
|
|
86
89
|
const prerenderDir = path.join(import.meta.dirname, 'prerender');
|
|
87
|
-
|
|
88
|
-
// tmp directory accepts writes. ISR revalidations land there and are read
|
|
89
|
-
// back before the bundled deploy-time prerender output.
|
|
90
|
-
const isrCacheDir = path.join(os.tmpdir(), 'ruvyxa-isr-cache');
|
|
90
|
+
${isrTemporaryCacheSource(buildId)}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* The cache tag a document is stored under, so one path can be dropped from
|
|
@@ -130,6 +130,35 @@ const readEntry = (htmlPath, revalidate) => {
|
|
|
130
130
|
return { html, stale };
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
+
// Named rather than passed inline, so a project that declares \`cache.handler\`
|
|
134
|
+
// can stand its own store in front of these. See \`documentCacheOptionsSource\`
|
|
135
|
+
// in \`@ruvyxa/core\`; the registry supplies \`documentCacheHandler\`.
|
|
136
|
+
const platformReadPrerendered = (pathname, revalidate = 60) => {
|
|
137
|
+
// prerenderRelativePath rejects any request path that cannot be mapped to a
|
|
138
|
+
// location inside the cache directories, so reads can never escape them.
|
|
139
|
+
const relative = prerenderRelativePath(pathname);
|
|
140
|
+
if (relative === null) return null;
|
|
141
|
+
try {
|
|
142
|
+
return readEntry(path.join(isrCacheDir, relative), revalidate);
|
|
143
|
+
} catch {
|
|
144
|
+
// fall through to the bundled prerender output
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return readEntry(path.join(prerenderDir, relative), revalidate);
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const platformWritePrerendered = (pathname, html, revalidate, forced) => {
|
|
154
|
+
const relative = prerenderRelativePath(pathname);
|
|
155
|
+
if (relative === null) return;
|
|
156
|
+
const htmlPath = path.join(isrCacheDir, relative);
|
|
157
|
+
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
158
|
+
writeFileSync(htmlPath, html, 'utf8');
|
|
159
|
+
if (forced === true) return purgeDurableCache(pathname);
|
|
160
|
+
};
|
|
161
|
+
|
|
133
162
|
const handler = createHandler({
|
|
134
163
|
routes: manifest.routes,
|
|
135
164
|
middleware: runtimePolicy.middleware,
|
|
@@ -139,35 +168,21 @@ const handler = createHandler({
|
|
|
139
168
|
importAction: loadActionModule,
|
|
140
169
|
pluginHttp: applyPluginHttp,
|
|
141
170
|
security: runtimePolicy.security,
|
|
142
|
-
|
|
143
|
-
// prerenderRelativePath rejects any request path that cannot be mapped to a
|
|
144
|
-
// location inside the cache directories, so reads can never escape them.
|
|
145
|
-
const relative = prerenderRelativePath(pathname);
|
|
146
|
-
if (relative === null) return null;
|
|
147
|
-
try {
|
|
148
|
-
return readEntry(path.join(isrCacheDir, relative), revalidate);
|
|
149
|
-
} catch {
|
|
150
|
-
// fall through to the bundled prerender output
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
return readEntry(path.join(prerenderDir, relative), revalidate);
|
|
154
|
-
} catch {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
writePrerendered: (pathname, html, revalidate, forced) => {
|
|
159
|
-
const relative = prerenderRelativePath(pathname);
|
|
160
|
-
if (relative === null) return;
|
|
161
|
-
const htmlPath = path.join(isrCacheDir, relative);
|
|
162
|
-
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
163
|
-
writeFileSync(htmlPath, html, 'utf8');
|
|
164
|
-
if (forced === true) return purgeDurableCache(pathname);
|
|
165
|
-
},
|
|
171
|
+
${documentCacheOptionsSource('platformReadPrerendered', 'platformWritePrerendered')}
|
|
166
172
|
// The project's own not-found page, pre-rendered by the build and carried
|
|
167
173
|
// inline in the manifest: an unmatched URL is answered with the page the
|
|
168
174
|
// application actually wrote, on every host.
|
|
169
175
|
notFoundDocument: manifest.notFoundDocument,
|
|
170
176
|
supportedStrategies: ['ssr', 'ssg', 'csr', 'isr', 'ppr', 'api'],
|
|
177
|
+
// Netlify's own edge writes \`X-Nf-Client-Connection-IP\` and overwrites any
|
|
178
|
+
// inbound copy of it, so on this platform the header *is* the client.
|
|
179
|
+
// Declared here rather than assumed by the handler: the same handler runs on
|
|
180
|
+
// a self-hosted server where nothing writes it. Without a declaration
|
|
181
|
+
// identity fell through to a right-to-left scan of \`X-Forwarded-For\`, whose
|
|
182
|
+
// rightmost entry here is a fixed platform address — so the built-in \`rate\`
|
|
183
|
+
// middleware, the server-action rate limiter, and the replay quota all
|
|
184
|
+
// counted every visitor into one bucket.
|
|
185
|
+
clientIpHeaders: ['x-nf-client-connection-ip'],
|
|
171
186
|
});
|
|
172
187
|
|
|
173
188
|
// Netlify Functions v2 — Web-standard Request/Response
|
|
@@ -386,7 +401,11 @@ export function netlify(options: NetlifyAdapterOptions = {}): Adapter {
|
|
|
386
401
|
{
|
|
387
402
|
kind: 'function',
|
|
388
403
|
path: 'deploy/netlify/functions/ruvyxa-handler',
|
|
389
|
-
handlerSource: netlifyHandlerSource(
|
|
404
|
+
handlerSource: netlifyHandlerSource(
|
|
405
|
+
runtimePolicy,
|
|
406
|
+
functionConfigFor(),
|
|
407
|
+
ctx.deployManifest?.buildId ?? '',
|
|
408
|
+
),
|
|
390
409
|
},
|
|
391
410
|
// Netlify config for the deploy directory
|
|
392
411
|
{
|
|
@@ -403,7 +422,11 @@ export function netlify(options: NetlifyAdapterOptions = {}): Adapter {
|
|
|
403
422
|
kind: 'function',
|
|
404
423
|
path: '.netlify/v1/functions/ruvyxa-handler',
|
|
405
424
|
scope: 'project',
|
|
406
|
-
handlerSource: netlifyHandlerSource(
|
|
425
|
+
handlerSource: netlifyHandlerSource(
|
|
426
|
+
runtimePolicy,
|
|
427
|
+
functionConfigFor(),
|
|
428
|
+
ctx.deployManifest?.buildId ?? '',
|
|
429
|
+
),
|
|
407
430
|
} satisfies AdapterArtifact,
|
|
408
431
|
{
|
|
409
432
|
kind: 'file',
|