adjacent-next 0.1.0
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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/app-router-bridges.d.ts +11 -0
- package/dist/app-router-bridges.d.ts.map +1 -0
- package/dist/app-router-bridges.js +38 -0
- package/dist/app-router-bridges.js.map +1 -0
- package/dist/base-path.d.ts +27 -0
- package/dist/base-path.d.ts.map +1 -0
- package/dist/base-path.js +112 -0
- package/dist/base-path.js.map +1 -0
- package/dist/create-adjacent.d.ts +22 -0
- package/dist/create-adjacent.d.ts.map +1 -0
- package/dist/create-adjacent.js +31 -0
- package/dist/create-adjacent.js.map +1 -0
- package/dist/dev-channel-client.d.ts +6 -0
- package/dist/dev-channel-client.d.ts.map +1 -0
- package/dist/dev-channel-client.js +68 -0
- package/dist/dev-channel-client.js.map +1 -0
- package/dist/dev-channel.d.ts +7 -0
- package/dist/dev-channel.d.ts.map +1 -0
- package/dist/dev-channel.js +8 -0
- package/dist/dev-channel.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +7 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +13 -0
- package/dist/proxy.js.map +1 -0
- package/dist/service-worker-registration.d.ts +6 -0
- package/dist/service-worker-registration.d.ts.map +1 -0
- package/dist/service-worker-registration.js +35 -0
- package/dist/service-worker-registration.js.map +1 -0
- package/dist/service-worker.d.ts +23 -0
- package/dist/service-worker.d.ts.map +1 -0
- package/dist/service-worker.js +393 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/with-adjacent-layout.d.ts +10 -0
- package/dist/with-adjacent-layout.d.ts.map +1 -0
- package/dist/with-adjacent-layout.js +11 -0
- package/dist/with-adjacent-layout.js.map +1 -0
- package/dist/with-adjacent.d.ts +17 -0
- package/dist/with-adjacent.d.ts.map +1 -0
- package/dist/with-adjacent.js +139 -0
- package/dist/with-adjacent.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { WORKER_COMMAND } from "adjacent-core";
|
|
2
|
+
import { basePath, withBasePath } from "./base-path.js";
|
|
3
|
+
/**
|
|
4
|
+
* Enough for an application's own scripts, styles, fonts and the images of a
|
|
5
|
+
* few screens, and small enough that a long session cannot fill the origin's
|
|
6
|
+
* storage quota with them.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_ASSET_LIMIT = 64;
|
|
9
|
+
function stableHash(value) {
|
|
10
|
+
let hash = 2_166_136_261;
|
|
11
|
+
for (const character of value) {
|
|
12
|
+
hash ^= character.charCodeAt(0);
|
|
13
|
+
hash = Math.imul(hash, 16_777_619);
|
|
14
|
+
}
|
|
15
|
+
return (hash >>> 0).toString(36);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A trailing slash is dropped, because `/admin` and `/admin/` exclude the same
|
|
19
|
+
* thing and two spellings of one rule would be two rules in the worker. Sorted
|
|
20
|
+
* and deduplicated so reordering the configuration does not change the worker,
|
|
21
|
+
* which would otherwise read as a new deployment.
|
|
22
|
+
*/
|
|
23
|
+
function normaliseExclusions(paths) {
|
|
24
|
+
if (!paths)
|
|
25
|
+
return [];
|
|
26
|
+
return [...new Set(paths.map((path) => path.replace(/\/+$/, "")))].sort();
|
|
27
|
+
}
|
|
28
|
+
export function resolveOfflineRuntime(offline) {
|
|
29
|
+
if (!offline) {
|
|
30
|
+
return {
|
|
31
|
+
assetLimit: DEFAULT_ASSET_LIMIT,
|
|
32
|
+
enabled: false,
|
|
33
|
+
exclude: [],
|
|
34
|
+
navigation: "network-first",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (offline === true) {
|
|
38
|
+
return {
|
|
39
|
+
assetLimit: DEFAULT_ASSET_LIMIT,
|
|
40
|
+
enabled: true,
|
|
41
|
+
exclude: [],
|
|
42
|
+
navigation: "network-first",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
assetLimit: offline.assetLimit ?? DEFAULT_ASSET_LIMIT,
|
|
47
|
+
enabled: true,
|
|
48
|
+
exclude: normaliseExclusions(offline.exclude),
|
|
49
|
+
fallback: offline.fallback,
|
|
50
|
+
navigation: offline.navigation ?? "network-first",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function createServiceWorkerSource(config, development = process.env.NODE_ENV === "development") {
|
|
54
|
+
const offline = resolveOfflineRuntime(config.offline);
|
|
55
|
+
const base = basePath();
|
|
56
|
+
const startUrl = withBasePath(config.navigation?.startUrl ?? "/", base);
|
|
57
|
+
const revision = stableHash(JSON.stringify({
|
|
58
|
+
appName: config.app.name,
|
|
59
|
+
// Without this the worker is identical between builds, so the browser
|
|
60
|
+
// rightly concludes there is no update: no update-available state, and
|
|
61
|
+
// `checkForUpdate()` that can never answer true.
|
|
62
|
+
buildId: process.env["ADJACENT_BUILD_ID"],
|
|
63
|
+
assetLimit: offline.assetLimit,
|
|
64
|
+
exclude: offline.exclude,
|
|
65
|
+
selfDestroying: config.selfDestroying,
|
|
66
|
+
base,
|
|
67
|
+
fallback: offline.fallback,
|
|
68
|
+
navigation: offline.navigation,
|
|
69
|
+
slug: config.app.slug,
|
|
70
|
+
startUrl,
|
|
71
|
+
version: config.app.version,
|
|
72
|
+
}));
|
|
73
|
+
const cachePrefix = `adjacent-${config.app.slug}-`;
|
|
74
|
+
if (config.selfDestroying) {
|
|
75
|
+
return `/* Generated by Adjacent: this worker removes itself. */
|
|
76
|
+
const CACHE_PREFIX = ${JSON.stringify(cachePrefix)};
|
|
77
|
+
|
|
78
|
+
self.addEventListener("install", () => {
|
|
79
|
+
self.skipWaiting();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* Everything is inside waitUntil, because a worker that has unregistered may
|
|
84
|
+
* be terminated at any moment and half-deleted caches would be left behind.
|
|
85
|
+
*
|
|
86
|
+
* Only this application's caches are deleted. Another application on the same
|
|
87
|
+
* origin keeps its own, which is why they carry a prefix.
|
|
88
|
+
*/
|
|
89
|
+
self.addEventListener("activate", (event) => {
|
|
90
|
+
event.waitUntil((async () => {
|
|
91
|
+
const keys = await caches.keys();
|
|
92
|
+
await Promise.all(
|
|
93
|
+
keys.filter((key) => key.startsWith(CACHE_PREFIX)).map((key) => caches.delete(key)),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await self.registration.unregister();
|
|
97
|
+
|
|
98
|
+
// Reload what is open, so a window stops running against a worker that has
|
|
99
|
+
// gone rather than waiting for the visitor to navigate.
|
|
100
|
+
const windows = await self.clients.matchAll({ type: "window" });
|
|
101
|
+
for (const client of windows) client.navigate(client.url);
|
|
102
|
+
})());
|
|
103
|
+
});
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
return `/* Generated by Adjacent. */
|
|
107
|
+
const CACHE_PREFIX = ${JSON.stringify(`adjacent-${config.app.slug}-`)};
|
|
108
|
+
const PAGE_CACHE = CACHE_PREFIX + "pages-${revision}";
|
|
109
|
+
const ASSET_CACHE = CACHE_PREFIX + "assets-${revision}";
|
|
110
|
+
const PRECACHE_URLS = ${JSON.stringify([
|
|
111
|
+
...new Set([startUrl, offline.fallback ? withBasePath(offline.fallback, base) : undefined].filter(Boolean)),
|
|
112
|
+
])};
|
|
113
|
+
const NAVIGATION_STRATEGY = ${JSON.stringify(offline.navigation)};
|
|
114
|
+
const DEVELOPMENT = ${JSON.stringify(development)};
|
|
115
|
+
const COMMAND = ${JSON.stringify(WORKER_COMMAND)};
|
|
116
|
+
const APP_NAME = ${JSON.stringify(config.app.name)};
|
|
117
|
+
const ASSET_LIMIT = ${JSON.stringify(offline.assetLimit)};
|
|
118
|
+
const EXCLUDED_PATHS = ${JSON.stringify([
|
|
119
|
+
...offline.exclude.map((path) => withBasePath(path, base)),
|
|
120
|
+
...["/sw.js", "/_adjacent", "/.well-known", "/api"].map((path) => withBasePath(path, base)),
|
|
121
|
+
])};
|
|
122
|
+
const START_URL = ${JSON.stringify(startUrl)};
|
|
123
|
+
|
|
124
|
+
self.addEventListener("install", (event) => {
|
|
125
|
+
if (DEVELOPMENT) {
|
|
126
|
+
event.waitUntil(self.skipWaiting());
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
event.waitUntil(
|
|
130
|
+
caches.open(PAGE_CACHE)
|
|
131
|
+
.then((cache) => cache.addAll(PRECACHE_URLS))
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
self.addEventListener("activate", (event) => {
|
|
136
|
+
event.waitUntil(
|
|
137
|
+
caches.keys()
|
|
138
|
+
.then((keys) => Promise.all(
|
|
139
|
+
keys
|
|
140
|
+
.filter((key) => key.startsWith(CACHE_PREFIX) && (
|
|
141
|
+
DEVELOPMENT || (key !== PAGE_CACHE && key !== ASSET_CACHE)
|
|
142
|
+
))
|
|
143
|
+
.map((key) => caches.delete(key)),
|
|
144
|
+
))
|
|
145
|
+
.then(() => self.clients.claim()),
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* Writing to a cache can fail, and the usual reason is the origin's storage
|
|
151
|
+
* quota. A failed write must not take the response with it: the fetch worked,
|
|
152
|
+
* and the caller wants the page or the asset whether or not it could be kept.
|
|
153
|
+
*/
|
|
154
|
+
async function store(cache, request, response, limit) {
|
|
155
|
+
try {
|
|
156
|
+
await cache.put(request, response);
|
|
157
|
+
if (limit === undefined) return;
|
|
158
|
+
// Oldest first, which is the order the Cache API keeps them in. Approximate
|
|
159
|
+
// as an eviction policy, and enough to stop unbounded growth.
|
|
160
|
+
const keys = await cache.keys();
|
|
161
|
+
if (keys.length <= limit) return;
|
|
162
|
+
await Promise.all(keys.slice(0, keys.length - limit).map((key) => cache.delete(key)));
|
|
163
|
+
} catch {
|
|
164
|
+
// Out of quota, or evicted mid-write. Serving the response still works.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function networkFirst(request, cacheName, fallbackUrl) {
|
|
169
|
+
const cache = await caches.open(cacheName);
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(request);
|
|
172
|
+
if (isCacheable(response)) await store(cache, request, response.clone());
|
|
173
|
+
return response;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const cached = await cache.match(request);
|
|
176
|
+
if (cached) return cached;
|
|
177
|
+
if (fallbackUrl) {
|
|
178
|
+
const fallback = await cache.match(fallbackUrl);
|
|
179
|
+
if (fallback) return fallback;
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function cacheFirst(request, cacheName) {
|
|
186
|
+
const cache = await caches.open(cacheName);
|
|
187
|
+
const cached = await cache.match(request);
|
|
188
|
+
if (cached) return cached;
|
|
189
|
+
const response = await fetch(request);
|
|
190
|
+
if (isCacheable(response)) await store(cache, request, response.clone(), ASSET_LIMIT);
|
|
191
|
+
return response;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function cacheFirstNavigation(request, fallbackUrl) {
|
|
195
|
+
try {
|
|
196
|
+
return await cacheFirst(request, PAGE_CACHE);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (fallbackUrl) {
|
|
199
|
+
const fallback = await caches.match(fallbackUrl);
|
|
200
|
+
if (fallback) return fallback;
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function isCacheable(response) {
|
|
207
|
+
const cacheControl = response.headers.get("cache-control") || "";
|
|
208
|
+
return response.ok &&
|
|
209
|
+
(response.type === "basic" || response.type === "default") &&
|
|
210
|
+
!/(?:^|,)\\s*(?:no-store|private)(?:\\s|,|$)/i.test(cacheControl);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* A path excludes itself and everything under it, and nothing else: /admin
|
|
214
|
+
* covers /admin and /admin/users, and leaves /administrators alone. */
|
|
215
|
+
function isExcludedPath(pathname) {
|
|
216
|
+
return EXCLUDED_PATHS.some(
|
|
217
|
+
(path) => pathname === path || pathname.startsWith(path + "/"),
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isExcludedRequest(request, url) {
|
|
222
|
+
return request.method !== "GET" ||
|
|
223
|
+
isExcludedPath(url.pathname) ||
|
|
224
|
+
url.origin !== self.location.origin ||
|
|
225
|
+
request.headers.has("authorization") ||
|
|
226
|
+
request.headers.get("rsc") === "1" ||
|
|
227
|
+
request.headers.has("next-router-state-tree");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function clearAdjacentCache(target) {
|
|
231
|
+
const names = target === "pages"
|
|
232
|
+
? [PAGE_CACHE]
|
|
233
|
+
: target === "assets"
|
|
234
|
+
? [ASSET_CACHE]
|
|
235
|
+
: [PAGE_CACHE, ASSET_CACHE];
|
|
236
|
+
await Promise.all(names.map((name) => caches.delete(name)));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function revalidatePages(urls) {
|
|
240
|
+
const cache = await caches.open(PAGE_CACHE);
|
|
241
|
+
await Promise.all(urls.map(async (input) => {
|
|
242
|
+
const url = new URL(input, self.location.origin);
|
|
243
|
+
if (url.origin !== self.location.origin) {
|
|
244
|
+
throw new Error("Adjacent can only revalidate same-origin URLs.");
|
|
245
|
+
}
|
|
246
|
+
const request = new Request(url, { headers: { accept: "text/html" } });
|
|
247
|
+
const response = await fetch(request);
|
|
248
|
+
if (!isCacheable(response)) {
|
|
249
|
+
throw new Error("Could not cache " + url.pathname + ": HTTP " + response.status);
|
|
250
|
+
}
|
|
251
|
+
await cache.put(request, response);
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
self.addEventListener("message", (event) => {
|
|
256
|
+
const command = event.data;
|
|
257
|
+
const reply = (response) => event.ports[0]?.postMessage(response);
|
|
258
|
+
event.waitUntil((async () => {
|
|
259
|
+
try {
|
|
260
|
+
if (!command || typeof command !== "object") throw new Error("Invalid Adjacent command.");
|
|
261
|
+
if (command.type === COMMAND.activateUpdate) {
|
|
262
|
+
await self.skipWaiting();
|
|
263
|
+
} else if (command.type === COMMAND.clearCache) {
|
|
264
|
+
if (!["all", "assets", "pages"].includes(command.target)) {
|
|
265
|
+
throw new Error("Invalid Adjacent cache target.");
|
|
266
|
+
}
|
|
267
|
+
await clearAdjacentCache(command.target);
|
|
268
|
+
} else if (command.type === COMMAND.revalidate) {
|
|
269
|
+
if (!Array.isArray(command.urls) || !command.urls.every((url) => typeof url === "string")) {
|
|
270
|
+
throw new Error("Invalid Adjacent revalidation URLs.");
|
|
271
|
+
}
|
|
272
|
+
await revalidatePages(command.urls);
|
|
273
|
+
} else {
|
|
274
|
+
throw new Error("Unknown Adjacent command.");
|
|
275
|
+
}
|
|
276
|
+
reply({ ok: true });
|
|
277
|
+
} catch (error) {
|
|
278
|
+
reply({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
279
|
+
}
|
|
280
|
+
})());
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
self.addEventListener("fetch", (event) => {
|
|
284
|
+
if (DEVELOPMENT) return;
|
|
285
|
+
const { request } = event;
|
|
286
|
+
const url = new URL(request.url);
|
|
287
|
+
if (isExcludedRequest(request, url)) return;
|
|
288
|
+
|
|
289
|
+
if (request.mode === "navigate") {
|
|
290
|
+
const fallbackUrl = ${JSON.stringify(offline.fallback)};
|
|
291
|
+
event.respondWith(
|
|
292
|
+
NAVIGATION_STRATEGY === "cache-first"
|
|
293
|
+
? cacheFirstNavigation(request, fallbackUrl)
|
|
294
|
+
: networkFirst(request, PAGE_CACHE, fallbackUrl),
|
|
295
|
+
);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (["font", "image", "script", "style", "worker"].includes(request.destination)) {
|
|
300
|
+
event.respondWith(cacheFirst(request, ASSET_CACHE));
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
* A push must be shown. Browsers require the subscription to be
|
|
306
|
+
* userVisibleOnly, and one that arrives without a notification is reported by
|
|
307
|
+
* the browser with a notification of its own saying so, which is worse than
|
|
308
|
+
* anything an application would have written.
|
|
309
|
+
*/
|
|
310
|
+
function readPushPayload(event) {
|
|
311
|
+
if (!event.data) return { title: APP_NAME };
|
|
312
|
+
try {
|
|
313
|
+
const payload = event.data.json();
|
|
314
|
+
return payload && typeof payload === "object" ? payload : { title: APP_NAME, body: String(payload) };
|
|
315
|
+
} catch {
|
|
316
|
+
// Not JSON, so it is the message itself.
|
|
317
|
+
return { title: APP_NAME, body: event.data.text() };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
self.addEventListener("push", (event) => {
|
|
322
|
+
const payload = readPushPayload(event);
|
|
323
|
+
const shown = self.registration.showNotification(payload.title ?? APP_NAME, {
|
|
324
|
+
actions: payload.actions,
|
|
325
|
+
badge: payload.badge,
|
|
326
|
+
body: payload.body,
|
|
327
|
+
data: { ...payload.data, url: payload.url ?? START_URL },
|
|
328
|
+
icon: payload.icon,
|
|
329
|
+
renotify: payload.renotify,
|
|
330
|
+
requireInteraction: payload.requireInteraction,
|
|
331
|
+
silent: payload.silent,
|
|
332
|
+
tag: payload.tag,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// The count on the application icon travels in the same message, so a push
|
|
336
|
+
// that clears a queue can clear the badge with it.
|
|
337
|
+
const badged =
|
|
338
|
+
typeof payload.badgeCount === "number" && navigator.setAppBadge
|
|
339
|
+
? payload.badgeCount > 0
|
|
340
|
+
? navigator.setAppBadge(payload.badgeCount)
|
|
341
|
+
: navigator.clearAppBadge()
|
|
342
|
+
: undefined;
|
|
343
|
+
|
|
344
|
+
event.waitUntil(Promise.all([shown, badged]));
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
self.addEventListener("notificationclick", (event) => {
|
|
348
|
+
event.notification.close();
|
|
349
|
+
|
|
350
|
+
const data = event.notification.data ?? {};
|
|
351
|
+
const target = new URL(data.url ?? START_URL, self.location.origin);
|
|
352
|
+
|
|
353
|
+
event.waitUntil((async () => {
|
|
354
|
+
const windows = await self.clients.matchAll({ includeUncontrolled: true, type: "window" });
|
|
355
|
+
const message = {
|
|
356
|
+
action: event.action || undefined,
|
|
357
|
+
data,
|
|
358
|
+
type: "adjacent:notificationclick",
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Focus what is already open rather than opening a second window, and tell
|
|
362
|
+
// it which action was pressed, since only the application knows what a
|
|
363
|
+
// button of its own means.
|
|
364
|
+
for (const client of windows) {
|
|
365
|
+
if (new URL(client.url).origin !== target.origin) continue;
|
|
366
|
+
client.postMessage(message);
|
|
367
|
+
await client.focus();
|
|
368
|
+
if (client.url !== target.href) await client.navigate?.(target.href);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
await self.clients.openWindow(target.href);
|
|
373
|
+
})());
|
|
374
|
+
});
|
|
375
|
+
`;
|
|
376
|
+
}
|
|
377
|
+
export function createServiceWorkerRoute(config) {
|
|
378
|
+
const source = createServiceWorkerSource(config);
|
|
379
|
+
return {
|
|
380
|
+
GET() {
|
|
381
|
+
return new Response(source, {
|
|
382
|
+
headers: {
|
|
383
|
+
"cache-control": "no-cache, no-store, must-revalidate",
|
|
384
|
+
"content-security-policy": "default-src 'self'; script-src 'self'",
|
|
385
|
+
"content-type": "application/javascript; charset=utf-8",
|
|
386
|
+
"service-worker-allowed": "/",
|
|
387
|
+
"x-content-type-options": "nosniff",
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=service-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../src/service-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ComponentType, type FunctionComponent, type ReactNode } from "react";
|
|
2
|
+
export interface AdjacentLayoutProps {
|
|
3
|
+
readonly children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
export interface AdjacentLayoutRuntime {
|
|
6
|
+
readonly devChannel?: ReactNode;
|
|
7
|
+
readonly serviceWorkerRegistration: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare function withAdjacentLayout(runtime: AdjacentLayoutRuntime): <Props extends AdjacentLayoutProps>(Layout: ComponentType<Props>) => FunctionComponent<Props>;
|
|
10
|
+
//# sourceMappingURL=with-adjacent-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-adjacent-layout.d.ts","sourceRoot":"","sources":["../src/with-adjacent-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;IAChC,QAAQ,CAAC,yBAAyB,EAAE,SAAS,CAAC;CAC/C;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAC7B,CAAC,KAAK,SAAS,mBAAmB,EACnC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,KACzB,iBAAiB,CAAC,KAAK,CAAC,CAiB5B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createElement, Fragment, } from "react";
|
|
2
|
+
export function withAdjacentLayout(runtime) {
|
|
3
|
+
return function wrapAdjacentLayout(Layout) {
|
|
4
|
+
function AdjacentLayout(props) {
|
|
5
|
+
const children = createElement(Fragment, null, props.children, runtime.serviceWorkerRegistration, runtime.devChannel);
|
|
6
|
+
return createElement(Layout, { ...props, children });
|
|
7
|
+
}
|
|
8
|
+
return AdjacentLayout;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=with-adjacent-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-adjacent-layout.js","sourceRoot":"","sources":["../src/with-adjacent-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,QAAQ,GAIT,MAAM,OAAO,CAAC;AAWf,MAAM,UAAU,kBAAkB,CAChC,OAA8B;IAI9B,OAAO,SAAS,kBAAkB,CAChC,MAA4B;QAE5B,SAAS,cAAc,CAAC,KAAY;YAClC,MAAM,QAAQ,GAAG,aAAa,CAC5B,QAAQ,EACR,IAAI,EACJ,KAAK,CAAC,QAAQ,EACd,OAAO,CAAC,yBAAyB,EACjC,OAAO,CAAC,UAAU,CACnB,CAAC;YACF,OAAO,aAAa,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
export interface AdjacentNextConfigOptions {
|
|
3
|
+
readonly connectivityRecovery?: boolean;
|
|
4
|
+
readonly devOrigins?: readonly string[];
|
|
5
|
+
readonly discoverLocalOrigins?: boolean;
|
|
6
|
+
readonly installElementOriginTrial?: string;
|
|
7
|
+
readonly nextConfig?: NextConfig;
|
|
8
|
+
readonly partialPrefetching?: boolean;
|
|
9
|
+
readonly securityHeaders?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function discoverLocalDevOrigins(): string[];
|
|
12
|
+
export declare function shouldSendDevTunnelHeaders(tunnelOrigin: string | undefined, nodeEnvironment?: "development" | "production" | "test"): boolean;
|
|
13
|
+
export declare function shouldEnablePartialPrefetching(requested: boolean | undefined, nodeEnvironment?: "development" | "production" | "test"): boolean;
|
|
14
|
+
/** The name the build id travels under, in Next's `env` and in the worker. */
|
|
15
|
+
export declare const BUILD_ID_ENV = "ADJACENT_BUILD_ID";
|
|
16
|
+
export declare function withAdjacent(options?: AdjacentNextConfigOptions): NextConfig;
|
|
17
|
+
//# sourceMappingURL=with-adjacent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-adjacent.d.ts","sourceRoot":"","sources":["../src/with-adjacent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAYD,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAWlD;AAcD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,eAAe,wCAAuB,GACrC,OAAO,CAET;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,OAAO,GAAG,SAAS,EAC9B,eAAe,wCAAuB,GACrC,OAAO,CAET;AAED,8EAA8E;AAC9E,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAqBhD,wBAAgB,YAAY,CAC1B,OAAO,GAAE,yBAA8B,GACtC,UAAU,CAyFZ"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { networkInterfaces } from "node:os";
|
|
2
|
+
import { BASE_PATH_ENV, normaliseBasePath } from "./base-path.js";
|
|
3
|
+
import { DEV_CHANNEL_PATH, resolveDevChannel } from "./dev-channel.js";
|
|
4
|
+
const DEV_TUNNEL_HEADERS = [
|
|
5
|
+
{ key: "Cache-Control", value: "no-store, must-revalidate" },
|
|
6
|
+
];
|
|
7
|
+
const SECURITY_HEADERS = [
|
|
8
|
+
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
9
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
10
|
+
{ key: "X-Frame-Options", value: "DENY" },
|
|
11
|
+
];
|
|
12
|
+
export function discoverLocalDevOrigins() {
|
|
13
|
+
return Object.values(networkInterfaces())
|
|
14
|
+
.flatMap((addresses) => addresses ?? [])
|
|
15
|
+
.filter((address) => address.family === "IPv4" &&
|
|
16
|
+
!address.internal &&
|
|
17
|
+
isPrivateIpv4(address.address))
|
|
18
|
+
.map((address) => address.address)
|
|
19
|
+
.sort();
|
|
20
|
+
}
|
|
21
|
+
function isPrivateIpv4(address) {
|
|
22
|
+
const octets = address.split(".").map(Number);
|
|
23
|
+
if (octets.length !== 4 || octets.some(Number.isNaN))
|
|
24
|
+
return false;
|
|
25
|
+
const [first, second] = octets;
|
|
26
|
+
return (first === 10 ||
|
|
27
|
+
(first === 172 && second !== undefined && second >= 16 && second <= 31) ||
|
|
28
|
+
(first === 192 && second === 168));
|
|
29
|
+
}
|
|
30
|
+
export function shouldSendDevTunnelHeaders(tunnelOrigin, nodeEnvironment = process.env.NODE_ENV) {
|
|
31
|
+
return tunnelOrigin !== undefined && nodeEnvironment === "development";
|
|
32
|
+
}
|
|
33
|
+
export function shouldEnablePartialPrefetching(requested, nodeEnvironment = process.env.NODE_ENV) {
|
|
34
|
+
return requested === true && nodeEnvironment !== "development";
|
|
35
|
+
}
|
|
36
|
+
/** The name the build id travels under, in Next's `env` and in the worker. */
|
|
37
|
+
export const BUILD_ID_ENV = "ADJACENT_BUILD_ID";
|
|
38
|
+
/**
|
|
39
|
+
* Identifies one build, so the generated service worker changes when the
|
|
40
|
+
* application does.
|
|
41
|
+
*
|
|
42
|
+
* Read from the environment first, which is how a deployment makes it
|
|
43
|
+
* deterministic: setting it to a commit SHA means rebuilding the same commit
|
|
44
|
+
* produces the same worker and nobody is told there is an update when there is
|
|
45
|
+
* not. Otherwise it is fresh per build, which is what Next does for its own
|
|
46
|
+
* build id.
|
|
47
|
+
*
|
|
48
|
+
* It has to be decided here, while the configuration is evaluated, because Next
|
|
49
|
+
* writes `env` into the build. A value decided per server instance would differ
|
|
50
|
+
* between them, every update check would find a new worker, and the result is a
|
|
51
|
+
* reload that never settles.
|
|
52
|
+
*/
|
|
53
|
+
function resolveBuildId() {
|
|
54
|
+
return process.env[BUILD_ID_ENV] ?? crypto.randomUUID();
|
|
55
|
+
}
|
|
56
|
+
export function withAdjacent(options = {}) {
|
|
57
|
+
const { connectivityRecovery, devOrigins = [], discoverLocalOrigins = true, installElementOriginTrial, nextConfig = {}, partialPrefetching, securityHeaders = true, } = options;
|
|
58
|
+
const discoveredOrigins = discoverLocalOrigins
|
|
59
|
+
? discoverLocalDevOrigins()
|
|
60
|
+
: [];
|
|
61
|
+
const tunnelOrigin = process.env.ADJACENT_DEV_ORIGIN;
|
|
62
|
+
const assetLinksUrl = process.env.ADJACENT_ASSET_LINKS_URL;
|
|
63
|
+
const devChannel = resolveDevChannel();
|
|
64
|
+
const configuredRewrites = nextConfig.rewrites;
|
|
65
|
+
const configuredHeaders = nextConfig.headers;
|
|
66
|
+
const enablePartialPrefetching = shouldEnablePartialPrefetching(partialPrefetching);
|
|
67
|
+
return {
|
|
68
|
+
...nextConfig,
|
|
69
|
+
// After the spread, or the application's own `env` replaces this one.
|
|
70
|
+
env: {
|
|
71
|
+
...nextConfig.env,
|
|
72
|
+
// Nothing outside next.config.ts can see the base path, so it is
|
|
73
|
+
// published here for the worker, the manifest and the registration.
|
|
74
|
+
[BASE_PATH_ENV]: normaliseBasePath(nextConfig.basePath),
|
|
75
|
+
[BUILD_ID_ENV]: resolveBuildId(),
|
|
76
|
+
},
|
|
77
|
+
cacheComponents: enablePartialPrefetching
|
|
78
|
+
? true
|
|
79
|
+
: nextConfig.cacheComponents,
|
|
80
|
+
partialPrefetching: enablePartialPrefetching
|
|
81
|
+
? true
|
|
82
|
+
: nextConfig.partialPrefetching,
|
|
83
|
+
experimental: connectivityRecovery === undefined
|
|
84
|
+
? nextConfig.experimental
|
|
85
|
+
: {
|
|
86
|
+
...nextConfig.experimental,
|
|
87
|
+
useOffline: connectivityRecovery,
|
|
88
|
+
},
|
|
89
|
+
allowedDevOrigins: [
|
|
90
|
+
...new Set([
|
|
91
|
+
...(nextConfig.allowedDevOrigins ?? []),
|
|
92
|
+
...discoveredOrigins,
|
|
93
|
+
...devOrigins,
|
|
94
|
+
...(tunnelOrigin ? [new URL(tunnelOrigin).hostname] : []),
|
|
95
|
+
]),
|
|
96
|
+
],
|
|
97
|
+
async headers() {
|
|
98
|
+
const existing = configuredHeaders ? await configuredHeaders() : [];
|
|
99
|
+
const tunnelHeaders = shouldSendDevTunnelHeaders(tunnelOrigin)
|
|
100
|
+
? [{ headers: [...DEV_TUNNEL_HEADERS], source: "/(.*)" }]
|
|
101
|
+
: [];
|
|
102
|
+
if (!securityHeaders)
|
|
103
|
+
return [...existing, ...tunnelHeaders];
|
|
104
|
+
return [
|
|
105
|
+
...existing,
|
|
106
|
+
...tunnelHeaders,
|
|
107
|
+
{
|
|
108
|
+
headers: [
|
|
109
|
+
...SECURITY_HEADERS,
|
|
110
|
+
...(installElementOriginTrial
|
|
111
|
+
? [{ key: "Origin-Trial", value: installElementOriginTrial }]
|
|
112
|
+
: []),
|
|
113
|
+
],
|
|
114
|
+
source: "/(.*)",
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
},
|
|
118
|
+
async rewrites() {
|
|
119
|
+
const existing = configuredRewrites ? await configuredRewrites() : [];
|
|
120
|
+
const adjacentRewrites = [
|
|
121
|
+
...(assetLinksUrl
|
|
122
|
+
? [{ source: "/.well-known/assetlinks.json", destination: assetLinksUrl }]
|
|
123
|
+
: []),
|
|
124
|
+
...(devChannel.url
|
|
125
|
+
? [{ source: DEV_CHANNEL_PATH, destination: devChannel.url }]
|
|
126
|
+
: []),
|
|
127
|
+
];
|
|
128
|
+
if (adjacentRewrites.length === 0)
|
|
129
|
+
return existing;
|
|
130
|
+
if (Array.isArray(existing))
|
|
131
|
+
return [...existing, ...adjacentRewrites];
|
|
132
|
+
return {
|
|
133
|
+
...existing,
|
|
134
|
+
beforeFiles: [...(existing.beforeFiles ?? []), ...adjacentRewrites],
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=with-adjacent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-adjacent.js","sourceRoot":"","sources":["../src/with-adjacent.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "adjacent-next",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Next.js App Router integration for Adjacent: metadata, manifest, service worker, and development wiring.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pwa",
|
|
7
|
+
"nextjs",
|
|
8
|
+
"app-router",
|
|
9
|
+
"service-worker",
|
|
10
|
+
"adjacent"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Eric Kweyunga",
|
|
14
|
+
"homepage": "https://github.com/erickweyunga/adjacent-framework/blob/main/docs/nextjs.md",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/erickweyunga/adjacent-framework.git",
|
|
18
|
+
"directory": "packages/adjacent-next"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/erickweyunga/adjacent-framework/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./proxy": {
|
|
31
|
+
"types": "./dist/proxy.d.ts",
|
|
32
|
+
"import": "./dist/proxy.js",
|
|
33
|
+
"default": "./dist/proxy.js"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=20.9.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.build.json",
|
|
51
|
+
"test": "bun test",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"prepublishOnly": "npm run build"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"adjacent-core": "workspace:~"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"next": ">=16"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/bun": "^1.3.13",
|
|
63
|
+
"@types/node": "^20",
|
|
64
|
+
"@types/react": "^19",
|
|
65
|
+
"next": "^16.3.2",
|
|
66
|
+
"react": "^19.2.8",
|
|
67
|
+
"react-dom": "^19.2.8",
|
|
68
|
+
"typescript": "^5"
|
|
69
|
+
},
|
|
70
|
+
"main": "./dist/index.js",
|
|
71
|
+
"types": "./dist/index.d.ts"
|
|
72
|
+
}
|