oxidejs 0.3.1 → 0.3.3
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 +43 -41
- package/client.d.ts +15 -9
- package/dist/client-DOfB6wxk.mjs +207 -0
- package/dist/context-CmChiQ2W.d.mts +98 -0
- package/dist/context-DUN_VWJF.mjs +108 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +73 -52
- package/dist/plugin-BIb3YnN7.mjs +674 -0
- package/dist/plugin.d.mts +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/rpc/client.d.mts +16 -7
- package/dist/rpc/client.mjs +1 -1
- package/dist/{rpc-DzUkWWgQ.mjs → rpc-gCOSJXql.mjs} +359 -325
- package/dist/rpc.d.mts +42 -33
- package/dist/rpc.mjs +2 -2
- package/dist/rsbuild.d.mts +1 -1
- package/dist/rsbuild.mjs +1 -1
- package/dist/{types-BM4NAnzy.d.mts → types-BaZeMYcP.d.mts} +22 -8
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +1 -1
- package/dist/worker-dom.d.mts +1 -1
- package/dist/worker-dom.mjs +15 -8
- package/package.json +2 -2
- package/virtual.d.ts +16 -3
- package/dist/client-C-s6XXpS.mjs +0 -148
- package/dist/context-C1UFQ0Zc.d.mts +0 -64
- package/dist/context-zrTZyYpF.mjs +0 -42
- package/dist/plugin-HZKRDuCS.mjs +0 -602
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
import { A as scanServerFiles, C as loadClientStub, D as parseExportedNames, E as nodeToWebRequest, O as parseStreamExports, S as isServerFileId, T as moduleKey, _ as generateActionsClientModule, b as generateClientStub, d as RESOLVED_VIRTUAL_CLIENT_ID, f as RESOLVED_VIRTUAL_WORKER_ID, g as VIRTUAL_WORKER_ID, j as sendWebResponseFrom, k as pluginShouldStub, l as ACTION_PATH, m as VIRTUAL_ACTIONS_ID, n as createActionHandler, p as RequestBodyTooLargeError, t as createWsHooks, u as RESOLVED_VIRTUAL_ACTIONS_ID, v as generateActionsModule, w as matchesActionPath, x as generateWorkerWrapper, y as generateClientModule } from "./rpc-gCOSJXql.mjs";
|
|
2
|
+
import { ensureWorkerDom } from "./worker-dom.mjs";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { createUnplugin } from "unplugin";
|
|
7
|
+
//#region src/core.ts
|
|
8
|
+
const CELLD_ALLOWED_KEYS = [
|
|
9
|
+
"name",
|
|
10
|
+
"main",
|
|
11
|
+
"compatibility_date",
|
|
12
|
+
"compatibility_flags",
|
|
13
|
+
"durable_objects",
|
|
14
|
+
"migrations",
|
|
15
|
+
"assets",
|
|
16
|
+
"services",
|
|
17
|
+
"vars"
|
|
18
|
+
];
|
|
19
|
+
const USER_FORBIDDEN_KEYS = ["main", "assets"];
|
|
20
|
+
const createEmitState = function createEmitState() {
|
|
21
|
+
return { emitted: false };
|
|
22
|
+
};
|
|
23
|
+
const validateWranglerOptions = function validateWranglerOptions(wrangler) {
|
|
24
|
+
const allowed = CELLD_ALLOWED_KEYS;
|
|
25
|
+
const invalid = Object.keys(wrangler).filter((key) => !allowed.includes(key));
|
|
26
|
+
if (invalid.length) throw new Error(`oxidejs: these wrangler keys are not supported by celld deploy: ${invalid.join(", ")}`);
|
|
27
|
+
const forbidden = USER_FORBIDDEN_KEYS.filter((key) => key in wrangler);
|
|
28
|
+
if (forbidden.length) throw new Error(`oxidejs: wrangler keys ${forbidden.join(", ")} are computed by the plugin and cannot be user-supplied`);
|
|
29
|
+
};
|
|
30
|
+
const assertContained = function assertContained(outDirAbs, childAbs, label) {
|
|
31
|
+
const outDir = path.resolve(outDirAbs);
|
|
32
|
+
const child = path.resolve(childAbs);
|
|
33
|
+
const relative = path.relative(outDir, child);
|
|
34
|
+
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`oxidejs: ${label} must resolve inside outDir (got ${relative || "."})`);
|
|
35
|
+
};
|
|
36
|
+
const requireWranglerFields = function requireWranglerFields(wrangler) {
|
|
37
|
+
if (!wrangler?.name || !wrangler.compatibility_date) throw new Error("oxidejs: wrangler.name and wrangler.compatibility_date are required when emitConfig is true");
|
|
38
|
+
return wrangler;
|
|
39
|
+
};
|
|
40
|
+
const flattenInput = function flattenInput(input) {
|
|
41
|
+
if (input === void 0) return [];
|
|
42
|
+
if (Array.isArray(input)) return input;
|
|
43
|
+
if (Object.prototype.toString.call(input) === "[object Object]") return Object.values(input);
|
|
44
|
+
return [input];
|
|
45
|
+
};
|
|
46
|
+
const envInput = function envInput(env) {
|
|
47
|
+
if (env === void 0) return;
|
|
48
|
+
return env.build?.rolldownOptions?.input || env.build?.rollupOptions?.input || env.build?.input || env.input;
|
|
49
|
+
};
|
|
50
|
+
/** Vite client input: rolldown/rollup `input`, else `path.resolve(root, "index.html")`. */
|
|
51
|
+
const hasHtmlEntry = function hasHtmlEntry(root, config) {
|
|
52
|
+
const explicit = envInput(config?.environments?.["client"]) || envInput(config?.environments?.["web"]) || config?.build?.rolldownOptions?.input || config?.build?.rollupOptions?.input;
|
|
53
|
+
return flattenInput(explicit || path.resolve(root, "index.html")).some((entry) => {
|
|
54
|
+
const file = path.resolve(root, entry);
|
|
55
|
+
return file.endsWith(".html") && fs.existsSync(file);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const resolveActions = function resolveActions(raw) {
|
|
59
|
+
if (raw === void 0) return {
|
|
60
|
+
path: ACTION_PATH,
|
|
61
|
+
sameOrigin: true,
|
|
62
|
+
transport: "http"
|
|
63
|
+
};
|
|
64
|
+
if (raw === "http" || raw === "ws") return {
|
|
65
|
+
path: ACTION_PATH,
|
|
66
|
+
sameOrigin: true,
|
|
67
|
+
transport: raw
|
|
68
|
+
};
|
|
69
|
+
if (Object.prototype.toString.call(raw) !== "[object Object]") throw new Error(`oxidejs: unknown actions transport "${String(raw)}"`);
|
|
70
|
+
const transport = raw.transport ?? "http";
|
|
71
|
+
if (transport !== "http" && transport !== "ws") throw new Error(`oxidejs: unknown actions transport "${String(transport)}"`);
|
|
72
|
+
const actionsPath = raw.path ?? "/__oxide/action";
|
|
73
|
+
if (!actionsPath.startsWith("/") || actionsPath.includes("?")) throw new Error(`oxidejs: actions.path must start with "/" and contain no query string (got "${actionsPath}")`);
|
|
74
|
+
return {
|
|
75
|
+
path: actionsPath,
|
|
76
|
+
sameOrigin: raw.sameOrigin ?? true,
|
|
77
|
+
transport
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const resolvePreset = function resolvePreset(raw) {
|
|
81
|
+
const preset = raw?.preset ?? "fetch";
|
|
82
|
+
if (preset !== "fetch" && preset !== "celld") throw new Error(`oxidejs: unknown preset "${String(preset)}"`);
|
|
83
|
+
return preset;
|
|
84
|
+
};
|
|
85
|
+
const resolvePaths = function resolvePaths(raw, root) {
|
|
86
|
+
const workerEntry = raw?.workerEntry ?? "src/server.ts";
|
|
87
|
+
const outDirInput = raw?.outDir ?? "dist";
|
|
88
|
+
const clientDir = raw?.clientDir ?? "client";
|
|
89
|
+
const rootAbs = path.resolve(root);
|
|
90
|
+
return {
|
|
91
|
+
clientDir,
|
|
92
|
+
outDir: path.resolve(rootAbs, outDirInput),
|
|
93
|
+
rootAbs,
|
|
94
|
+
workerEntry,
|
|
95
|
+
workerEntryAbs: path.resolve(rootAbs, workerEntry)
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const resolveWrangler = function resolveWrangler(raw, emitConfig) {
|
|
99
|
+
if (raw?.wrangler) validateWranglerOptions(raw.wrangler);
|
|
100
|
+
return emitConfig ? requireWranglerFields(raw?.wrangler) : raw?.wrangler;
|
|
101
|
+
};
|
|
102
|
+
const assertClientDir = function assertClientDir(outDir, clientDir, hasClient, hasPublic) {
|
|
103
|
+
if (hasClient || hasPublic) assertContained(outDir, path.resolve(outDir, clientDir), "clientDir");
|
|
104
|
+
};
|
|
105
|
+
const resolveOptions = function resolveOptions(raw, root, config) {
|
|
106
|
+
const preset = resolvePreset(raw);
|
|
107
|
+
const { transport: actions, path: actionPath, sameOrigin: actionSameOrigin } = resolveActions(raw?.actions);
|
|
108
|
+
if (actions === "ws" && preset === "celld") throw new Error("oxidejs: actions: \"ws\" is not supported with preset: \"celld\"");
|
|
109
|
+
const emitConfig = raw?.emitConfig ?? preset === "celld";
|
|
110
|
+
const { clientDir, outDir, rootAbs, workerEntry, workerEntryAbs } = resolvePaths(raw, root);
|
|
111
|
+
const hasClient = hasHtmlEntry(rootAbs, config);
|
|
112
|
+
const hasPublic = fs.existsSync(path.join(rootAbs, "public"));
|
|
113
|
+
assertClientDir(outDir, clientDir, hasClient, hasPublic);
|
|
114
|
+
const wrangler = resolveWrangler(raw, emitConfig);
|
|
115
|
+
return {
|
|
116
|
+
actionHeaders: raw?.actionHeaders,
|
|
117
|
+
actionPath,
|
|
118
|
+
actionSameOrigin,
|
|
119
|
+
actions,
|
|
120
|
+
bodyLimit: raw?.bodyLimit ?? 1048576,
|
|
121
|
+
clientDir,
|
|
122
|
+
emitConfig,
|
|
123
|
+
env: raw?.env,
|
|
124
|
+
hasClient,
|
|
125
|
+
hasPublic,
|
|
126
|
+
imports: raw?.imports ?? [],
|
|
127
|
+
middleware: raw?.middleware ?? [],
|
|
128
|
+
notFound: raw?.notFound,
|
|
129
|
+
outDir,
|
|
130
|
+
preset,
|
|
131
|
+
root: rootAbs,
|
|
132
|
+
workerEntry,
|
|
133
|
+
workerEntryAbs,
|
|
134
|
+
wrangler
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const copyPublicDir = function copyPublicDir(opts) {
|
|
138
|
+
if (opts.preset !== "fetch") return;
|
|
139
|
+
const src = path.join(opts.root, "public");
|
|
140
|
+
if (!fs.existsSync(src)) return;
|
|
141
|
+
fs.cpSync(src, path.join(opts.outDir, opts.clientDir), {
|
|
142
|
+
force: true,
|
|
143
|
+
recursive: true
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
const tryEmitWranglerConfig = function tryEmitWranglerConfig(opts, state) {
|
|
147
|
+
if (state.emitted || opts.emitConfig === false) return;
|
|
148
|
+
const wrangler = requireWranglerFields(opts.wrangler);
|
|
149
|
+
const serverFile = path.join(opts.outDir, "server.js");
|
|
150
|
+
const clientDirPath = path.join(opts.outDir, opts.clientDir);
|
|
151
|
+
if (!fs.existsSync(serverFile)) return;
|
|
152
|
+
if (opts.hasClient && !fs.existsSync(clientDirPath)) return;
|
|
153
|
+
assertContained(opts.outDir, serverFile, "main");
|
|
154
|
+
if (opts.hasClient) assertContained(opts.outDir, clientDirPath, "assets.directory");
|
|
155
|
+
const config = {
|
|
156
|
+
compatibility_date: wrangler.compatibility_date,
|
|
157
|
+
compatibility_flags: [.../* @__PURE__ */ new Set([...wrangler.compatibility_flags ?? [], "nodejs_compat"])],
|
|
158
|
+
main: "./server.js",
|
|
159
|
+
name: wrangler.name
|
|
160
|
+
};
|
|
161
|
+
if (wrangler.durable_objects) config.durable_objects = wrangler.durable_objects;
|
|
162
|
+
if (wrangler.migrations) config.migrations = wrangler.migrations;
|
|
163
|
+
if (wrangler.services) config.services = wrangler.services;
|
|
164
|
+
if (wrangler.vars) config.vars = wrangler.vars;
|
|
165
|
+
if (opts.hasClient) config.assets = {
|
|
166
|
+
binding: "ASSETS",
|
|
167
|
+
directory: `./${opts.clientDir}`
|
|
168
|
+
};
|
|
169
|
+
fs.writeFileSync(path.join(opts.outDir, "wrangler.jsonc"), `${JSON.stringify(config, null, 2)}\n`);
|
|
170
|
+
state.emitted = true;
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/worker-build.ts
|
|
174
|
+
/** Dev aliases so Vite transforms RPC helpers with the app's single effect copy. */
|
|
175
|
+
const oxideRpcAliases = function oxideRpcAliases() {
|
|
176
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
177
|
+
const client = path.join(root, "src/rpc/client.ts");
|
|
178
|
+
if (!fs.existsSync(client)) return [];
|
|
179
|
+
return [{
|
|
180
|
+
find: /^oxidejs\/rpc\/client$/u,
|
|
181
|
+
replacement: client
|
|
182
|
+
}, {
|
|
183
|
+
find: /^oxidejs\/rpc$/u,
|
|
184
|
+
replacement: path.join(root, "src/rpc/index.ts")
|
|
185
|
+
}];
|
|
186
|
+
};
|
|
187
|
+
const mergeAliases = function mergeAliases(config, extra) {
|
|
188
|
+
if (extra.length === 0) return;
|
|
189
|
+
config.resolve ??= {};
|
|
190
|
+
const current = config.resolve.alias;
|
|
191
|
+
if (!current) {
|
|
192
|
+
config.resolve.alias = extra;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (Array.isArray(current)) {
|
|
196
|
+
config.resolve.alias = [...current, ...extra];
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
config.resolve.alias = [...Object.entries(current).map(([find, replacement]) => ({
|
|
200
|
+
find,
|
|
201
|
+
replacement
|
|
202
|
+
})), ...extra];
|
|
203
|
+
};
|
|
204
|
+
const OPTIMIZE_DEPS = [
|
|
205
|
+
"effect",
|
|
206
|
+
"effect/unstable/rpc",
|
|
207
|
+
"effect/unstable/http",
|
|
208
|
+
"effect/unstable/socket",
|
|
209
|
+
"oxidejs",
|
|
210
|
+
"oxidejs/rpc/client"
|
|
211
|
+
];
|
|
212
|
+
const applyViteEnvironments = function applyViteEnvironments(config, opts) {
|
|
213
|
+
config.builder ??= {};
|
|
214
|
+
config.resolve ??= {};
|
|
215
|
+
const dedupe = /* @__PURE__ */ new Set([
|
|
216
|
+
...Array.isArray(config.resolve.dedupe) ? config.resolve.dedupe : [],
|
|
217
|
+
"effect",
|
|
218
|
+
"oxidejs"
|
|
219
|
+
]);
|
|
220
|
+
config.resolve.dedupe = [...dedupe];
|
|
221
|
+
config.optimizeDeps ??= {};
|
|
222
|
+
const optimizeInclude = /* @__PURE__ */ new Set([...Array.isArray(config.optimizeDeps.include) ? config.optimizeDeps.include : [], ...OPTIMIZE_DEPS]);
|
|
223
|
+
config.optimizeDeps.include = [...optimizeInclude];
|
|
224
|
+
const celld = opts.preset === "celld";
|
|
225
|
+
mergeAliases(config, oxideRpcAliases());
|
|
226
|
+
config.environments ??= {};
|
|
227
|
+
const ssrBuild = {
|
|
228
|
+
emptyOutDir: true,
|
|
229
|
+
outDir: opts.outDir,
|
|
230
|
+
rolldownOptions: {
|
|
231
|
+
external: celld ? [/^cloudflare:/u] : [],
|
|
232
|
+
input: VIRTUAL_WORKER_ID,
|
|
233
|
+
output: {
|
|
234
|
+
entryFileNames: "server.js",
|
|
235
|
+
format: "es"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
rollupOptions: {
|
|
239
|
+
external: celld ? [/^cloudflare:/u] : [],
|
|
240
|
+
input: VIRTUAL_WORKER_ID,
|
|
241
|
+
output: {
|
|
242
|
+
entryFileNames: "server.js",
|
|
243
|
+
format: "es"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
ssr: true
|
|
247
|
+
};
|
|
248
|
+
const ssrEnvironment = {
|
|
249
|
+
build: ssrBuild,
|
|
250
|
+
consumer: "server",
|
|
251
|
+
resolve: celld ? {
|
|
252
|
+
conditions: ["worker"],
|
|
253
|
+
noExternal: true
|
|
254
|
+
} : { noExternal: ["effect", "oxidejs"] },
|
|
255
|
+
ssr: celld ? {
|
|
256
|
+
external: [/^cloudflare:/u],
|
|
257
|
+
noExternal: true,
|
|
258
|
+
target: "webworker"
|
|
259
|
+
} : { noExternal: ["effect", "oxidejs"] }
|
|
260
|
+
};
|
|
261
|
+
config.environments["ssr"] = ssrEnvironment;
|
|
262
|
+
config.build ??= {};
|
|
263
|
+
if (opts.hasClient) {
|
|
264
|
+
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
265
|
+
const existingClient = config.environments["client"];
|
|
266
|
+
config.environments["client"] = {
|
|
267
|
+
...existingClient,
|
|
268
|
+
build: {
|
|
269
|
+
...existingClient?.build,
|
|
270
|
+
emptyOutDir: true,
|
|
271
|
+
manifest: true,
|
|
272
|
+
outDir: clientOutDir
|
|
273
|
+
},
|
|
274
|
+
consumer: "client"
|
|
275
|
+
};
|
|
276
|
+
ssrBuild.emptyOutDir = false;
|
|
277
|
+
config.build.outDir ??= clientOutDir;
|
|
278
|
+
config.build.manifest ??= true;
|
|
279
|
+
} else {
|
|
280
|
+
delete config.environments["client"];
|
|
281
|
+
config.appType = "custom";
|
|
282
|
+
config.build.outDir ??= opts.outDir;
|
|
283
|
+
config.build.emptyOutDir ??= true;
|
|
284
|
+
config.builder.buildApp ??= async (builder) => {
|
|
285
|
+
const server = builder.environments["ssr"];
|
|
286
|
+
if (server) await builder.build(server);
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return config;
|
|
290
|
+
};
|
|
291
|
+
const applyRsbuildEnvironments = function applyRsbuildEnvironments(config, opts) {
|
|
292
|
+
config.environments ??= {};
|
|
293
|
+
if (opts.hasClient) {
|
|
294
|
+
const clientOutDir = path.join(opts.outDir, opts.clientDir);
|
|
295
|
+
const existingClient = config.environments["web"] ?? config.environments["client"];
|
|
296
|
+
config.environments["web"] = {
|
|
297
|
+
...existingClient,
|
|
298
|
+
output: {
|
|
299
|
+
...existingClient?.output,
|
|
300
|
+
distPath: {
|
|
301
|
+
...existingClient?.output?.distPath,
|
|
302
|
+
root: clientOutDir
|
|
303
|
+
},
|
|
304
|
+
manifest: true,
|
|
305
|
+
target: "web"
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
} else {
|
|
309
|
+
delete config.environments["web"];
|
|
310
|
+
delete config.environments["client"];
|
|
311
|
+
}
|
|
312
|
+
const server = {
|
|
313
|
+
output: {
|
|
314
|
+
distPath: { root: opts.outDir },
|
|
315
|
+
filename: { js: "server.js" },
|
|
316
|
+
target: opts.preset === "celld" ? "web-worker" : "node"
|
|
317
|
+
},
|
|
318
|
+
source: { entry: { server: {
|
|
319
|
+
html: false,
|
|
320
|
+
import: VIRTUAL_WORKER_ID
|
|
321
|
+
} } }
|
|
322
|
+
};
|
|
323
|
+
if (opts.preset === "celld") server.resolve = { conditionNames: ["worker", "..."] };
|
|
324
|
+
config.environments["server"] = server;
|
|
325
|
+
return config;
|
|
326
|
+
};
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/plugin.ts
|
|
329
|
+
const isString = function isString(value) {
|
|
330
|
+
return typeof value === "string";
|
|
331
|
+
};
|
|
332
|
+
const isMiddlewareDefault = function isMiddlewareDefault(value) {
|
|
333
|
+
return typeof value === "function";
|
|
334
|
+
};
|
|
335
|
+
const isObject = function isObject(value) {
|
|
336
|
+
return typeof value === "object" && value !== null;
|
|
337
|
+
};
|
|
338
|
+
const resolveActionTransport = function resolveActionTransport(resolved, options) {
|
|
339
|
+
if (resolved?.actions) return resolved.actions;
|
|
340
|
+
const actions = options?.actions;
|
|
341
|
+
if (isString(actions) || actions === void 0) return actions ?? "http";
|
|
342
|
+
return actions.transport ?? "http";
|
|
343
|
+
};
|
|
344
|
+
const loadRpcModule = function loadRpcModule() {
|
|
345
|
+
return Promise.resolve({
|
|
346
|
+
createActionHandler,
|
|
347
|
+
createWsHooks
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
const actionMiddleware = function actionMiddleware(loadRouter, loadRpc, actionPath, sameOrigin, bodyLimit, onError) {
|
|
351
|
+
return (req, res, next) => {
|
|
352
|
+
if (!matchesActionPath((req.url ?? "").split("?")[0] ?? "", actionPath)) return next();
|
|
353
|
+
(async () => {
|
|
354
|
+
try {
|
|
355
|
+
const [mod, rpc] = await Promise.all([loadRouter(), loadRpc()]);
|
|
356
|
+
const response = await rpc.createActionHandler(mod.default, mod.actionsHandlers, {
|
|
357
|
+
path: actionPath,
|
|
358
|
+
sameOrigin
|
|
359
|
+
})(await nodeToWebRequest(req, bodyLimit));
|
|
360
|
+
await sendWebResponseFrom(req, res, response);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
if (res.headersSent) return;
|
|
363
|
+
if (error instanceof RequestBodyTooLargeError) {
|
|
364
|
+
res.statusCode = 413;
|
|
365
|
+
res.end();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
369
|
+
res.statusCode = 503;
|
|
370
|
+
res.setHeader("content-type", "application/json");
|
|
371
|
+
res.end(JSON.stringify({ error: "oxide action handler failed" }));
|
|
372
|
+
}
|
|
373
|
+
})();
|
|
374
|
+
};
|
|
375
|
+
};
|
|
376
|
+
const attachActionUpgrade = function attachActionUpgrade(httpServer, loadRouter, loadRpc, actionPath, sameOrigin) {
|
|
377
|
+
if (!httpServer) return;
|
|
378
|
+
const crosswsNode = "crossws/adapters/node";
|
|
379
|
+
(async () => {
|
|
380
|
+
const { default: crossws } = await import(
|
|
381
|
+
/* @vite-ignore */
|
|
382
|
+
crosswsNode
|
|
383
|
+
);
|
|
384
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
385
|
+
if (!matchesActionPath((req.url ?? "").split("?")[0] ?? "", actionPath)) return;
|
|
386
|
+
(async () => {
|
|
387
|
+
try {
|
|
388
|
+
const [mod, rpc] = await Promise.all([loadRouter(), loadRpc()]);
|
|
389
|
+
const hooks = rpc.createWsHooks(mod.default, mod.actionsHandlers, {
|
|
390
|
+
path: actionPath,
|
|
391
|
+
sameOrigin
|
|
392
|
+
});
|
|
393
|
+
await crossws({ hooks }).handleUpgrade(req, socket, head);
|
|
394
|
+
} catch {
|
|
395
|
+
socket.destroy();
|
|
396
|
+
}
|
|
397
|
+
})();
|
|
398
|
+
});
|
|
399
|
+
})();
|
|
400
|
+
};
|
|
401
|
+
const previewMiddleware = function previewMiddleware(file) {
|
|
402
|
+
return (req, res, next) => {
|
|
403
|
+
(async () => {
|
|
404
|
+
try {
|
|
405
|
+
const mod = await import(
|
|
406
|
+
/* @vite-ignore */
|
|
407
|
+
pathToFileURL(file).href
|
|
408
|
+
);
|
|
409
|
+
await sendWebResponseFrom(req, res, await mod.default.fetch(await nodeToWebRequest(req)));
|
|
410
|
+
} catch (error) {
|
|
411
|
+
return next(error instanceof Error ? error : new Error(String(error)));
|
|
412
|
+
}
|
|
413
|
+
})();
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
const loadActions = function loadActions(root) {
|
|
417
|
+
const code = generateActionsModule(scanServerFiles(root), { bust: true });
|
|
418
|
+
const dir = fs.mkdtempSync(path.join(root, ".oxide-actions-"));
|
|
419
|
+
const file = path.join(dir, "actions.mjs");
|
|
420
|
+
fs.writeFileSync(file, code);
|
|
421
|
+
return (async () => {
|
|
422
|
+
try {
|
|
423
|
+
return await import(pathToFileURL(file).href);
|
|
424
|
+
} finally {
|
|
425
|
+
fs.rmSync(dir, {
|
|
426
|
+
force: true,
|
|
427
|
+
recursive: true
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
})();
|
|
431
|
+
};
|
|
432
|
+
const loadVirtualClient = function loadVirtualClient(resolved, options) {
|
|
433
|
+
const transport = resolveActionTransport(resolved, options);
|
|
434
|
+
return generateClientModule(transport, resolved?.actionHeaders ?? options?.actionHeaders, resolved?.actionPath);
|
|
435
|
+
};
|
|
436
|
+
const loadVirtualActions = function loadVirtualActions(ctx, resolved, extra) {
|
|
437
|
+
const modules = scanServerFiles(resolved?.root ?? process.cwd());
|
|
438
|
+
if (pluginShouldStub(ctx, extra)) return generateActionsClientModule(modules);
|
|
439
|
+
for (const mod of modules) ctx.addWatchFile(mod.abs);
|
|
440
|
+
return generateActionsModule(modules);
|
|
441
|
+
};
|
|
442
|
+
const loadVirtualWorker = function loadVirtualWorker(ctx, resolved) {
|
|
443
|
+
ctx.addWatchFile(resolved.workerEntryAbs);
|
|
444
|
+
const modules = scanServerFiles(resolved.root);
|
|
445
|
+
for (const mod of modules) ctx.addWatchFile(mod.abs);
|
|
446
|
+
return generateWorkerWrapper(resolved.workerEntryAbs, {
|
|
447
|
+
actionPath: resolved.actionPath,
|
|
448
|
+
actionSameOrigin: resolved.actionSameOrigin,
|
|
449
|
+
actions: resolved.actions,
|
|
450
|
+
bodyLimit: resolved.bodyLimit,
|
|
451
|
+
clientDir: resolved.clientDir,
|
|
452
|
+
env: resolved.env,
|
|
453
|
+
hasActions: modules.length > 0,
|
|
454
|
+
hasClient: resolved.hasClient,
|
|
455
|
+
hasPublic: resolved.hasPublic,
|
|
456
|
+
imports: resolved.imports,
|
|
457
|
+
middleware: resolved.middleware,
|
|
458
|
+
notFound: resolved.notFound,
|
|
459
|
+
preset: resolved.preset
|
|
460
|
+
});
|
|
461
|
+
};
|
|
462
|
+
const loadPluginModule = function loadPluginModule(ctx, id, resolved, options, extra) {
|
|
463
|
+
if (id === RESOLVED_VIRTUAL_CLIENT_ID) return loadVirtualClient(resolved, options);
|
|
464
|
+
if (id === RESOLVED_VIRTUAL_WORKER_ID && pluginShouldStub(ctx, extra)) throw new Error(`oxidejs: ${VIRTUAL_WORKER_ID} is server-only`);
|
|
465
|
+
if (id === RESOLVED_VIRTUAL_ACTIONS_ID) return loadVirtualActions(ctx, resolved, extra);
|
|
466
|
+
if (id === RESOLVED_VIRTUAL_WORKER_ID) {
|
|
467
|
+
if (!resolved) return;
|
|
468
|
+
return loadVirtualWorker(ctx, resolved);
|
|
469
|
+
}
|
|
470
|
+
if (isServerFileId(id) && pluginShouldStub(ctx, extra)) {
|
|
471
|
+
const file = id.split("?")[0] ?? id;
|
|
472
|
+
ctx.addWatchFile(file);
|
|
473
|
+
return loadClientStub(id);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
const runMiddlewareHandlers = async function runMiddlewareHandlers(handlers, request, context, index) {
|
|
477
|
+
if (index >= handlers.length) return;
|
|
478
|
+
const handler = handlers[index];
|
|
479
|
+
if (!handler) return runMiddlewareHandlers(handlers, request, context, index + 1);
|
|
480
|
+
const hit = await handler(request, context);
|
|
481
|
+
if (hit) return hit;
|
|
482
|
+
return runMiddlewareHandlers(handlers, request, context, index + 1);
|
|
483
|
+
};
|
|
484
|
+
const loadDevMiddlewareHandlers = async function loadDevMiddlewareHandlers(server, resolved) {
|
|
485
|
+
try {
|
|
486
|
+
await Promise.all((resolved.imports ?? []).map((spec) => server.ssrLoadModule(spec)));
|
|
487
|
+
const entries = resolved.middleware ?? [];
|
|
488
|
+
const mods = await Promise.all(entries.map(async (entry) => {
|
|
489
|
+
const spec = isString(entry) ? entry : entry.module;
|
|
490
|
+
return await server.ssrLoadModule(spec);
|
|
491
|
+
}));
|
|
492
|
+
const handlers = [];
|
|
493
|
+
for (const mod of mods) {
|
|
494
|
+
if (!isMiddlewareDefault(mod.default)) continue;
|
|
495
|
+
const fn = mod.default;
|
|
496
|
+
handlers.push((request, context) => Promise.resolve(fn(request, context)));
|
|
497
|
+
}
|
|
498
|
+
return handlers;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
server.config.logger.error(`oxidejs: failed to wire dev middleware: ${String(error)}`);
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
/** Register the Connect bridge that runs production middleware modules in Vite dev. */
|
|
505
|
+
const attachDevMiddlewareBridge = function attachDevMiddlewareBridge(server, opts) {
|
|
506
|
+
if ((opts.middleware?.length ?? 0) === 0 && (opts.imports?.length ?? 0) === 0) return Promise.resolve([]);
|
|
507
|
+
const handlersPromise = loadDevMiddlewareHandlers(server, opts);
|
|
508
|
+
server.middlewares.use((creq, cres, next) => {
|
|
509
|
+
if (matchesActionPath((creq.url ?? "").split("?")[0] ?? "", opts.actionPath)) return next();
|
|
510
|
+
(async () => {
|
|
511
|
+
try {
|
|
512
|
+
const handlers = await handlersPromise;
|
|
513
|
+
if (handlers.length === 0) return next();
|
|
514
|
+
const request = await nodeToWebRequest(creq, opts.bodyLimit);
|
|
515
|
+
const context = {
|
|
516
|
+
ctx: void 0,
|
|
517
|
+
env: opts.env
|
|
518
|
+
};
|
|
519
|
+
const hit = await runMiddlewareHandlers(handlers, request, context, 0);
|
|
520
|
+
if (hit) {
|
|
521
|
+
await sendWebResponseFrom(creq, cres, hit);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
return next();
|
|
525
|
+
} catch (error) {
|
|
526
|
+
if (cres.headersSent) return;
|
|
527
|
+
cres.statusCode = error instanceof RequestBodyTooLargeError ? 413 : 500;
|
|
528
|
+
cres.end(error instanceof RequestBodyTooLargeError ? void 0 : String(error));
|
|
529
|
+
}
|
|
530
|
+
})();
|
|
531
|
+
});
|
|
532
|
+
return handlersPromise;
|
|
533
|
+
};
|
|
534
|
+
const unpluginFactory = (options) => {
|
|
535
|
+
let resolved;
|
|
536
|
+
const emitState = createEmitState();
|
|
537
|
+
return {
|
|
538
|
+
buildStart() {
|
|
539
|
+
resolved ??= resolveOptions(options, process.cwd());
|
|
540
|
+
emitState.emitted = false;
|
|
541
|
+
},
|
|
542
|
+
enforce: "pre",
|
|
543
|
+
load(id, extra) {
|
|
544
|
+
return loadPluginModule(this, id, resolved, options, extra);
|
|
545
|
+
},
|
|
546
|
+
name: "oxidejs",
|
|
547
|
+
resolveId(id) {
|
|
548
|
+
if (id === "virtual:oxide/actions") return RESOLVED_VIRTUAL_ACTIONS_ID;
|
|
549
|
+
if (id === "virtual:oxide/worker") return RESOLVED_VIRTUAL_WORKER_ID;
|
|
550
|
+
if (id === "virtual:oxide/client") return RESOLVED_VIRTUAL_CLIENT_ID;
|
|
551
|
+
return null;
|
|
552
|
+
},
|
|
553
|
+
rsbuild: { setup(api) {
|
|
554
|
+
api.modifyRsbuildConfig((config) => {
|
|
555
|
+
const root = isString(config.root) ? config.root : process.cwd();
|
|
556
|
+
resolved = resolveOptions(options, root, config);
|
|
557
|
+
applyRsbuildEnvironments(config, resolved);
|
|
558
|
+
});
|
|
559
|
+
api.onBeforeStartDevServer(({ server }) => {
|
|
560
|
+
const loadRouter = () => {
|
|
561
|
+
const root = resolved?.root ?? process.cwd();
|
|
562
|
+
return loadActions(root);
|
|
563
|
+
};
|
|
564
|
+
const opts = resolved;
|
|
565
|
+
if (!opts) return;
|
|
566
|
+
if (opts.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, loadRpcModule, opts.actionPath, opts.actionSameOrigin);
|
|
567
|
+
else server.middlewares.use(actionMiddleware(loadRouter, loadRpcModule, opts.actionPath, opts.actionSameOrigin, opts.bodyLimit));
|
|
568
|
+
});
|
|
569
|
+
api.onBeforeStartPreviewServer?.(({ server }) => {
|
|
570
|
+
if (resolved?.preset !== "fetch") return;
|
|
571
|
+
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
572
|
+
});
|
|
573
|
+
} },
|
|
574
|
+
transform(code, id, extra) {
|
|
575
|
+
const ctx = this;
|
|
576
|
+
if (!isServerFileId(id) || !pluginShouldStub(ctx, extra) || code.startsWith("// oxidejs:client-stub\n")) return;
|
|
577
|
+
return generateClientStub({
|
|
578
|
+
exports: parseExportedNames(code),
|
|
579
|
+
key: moduleKey(id.split("?")[0] ?? id),
|
|
580
|
+
streams: parseStreamExports(code)
|
|
581
|
+
});
|
|
582
|
+
},
|
|
583
|
+
vite: {
|
|
584
|
+
config(config) {
|
|
585
|
+
const root = isString(config.root) ? config.root : process.cwd();
|
|
586
|
+
resolved = resolveOptions(options, root, config);
|
|
587
|
+
applyViteEnvironments(config, resolved);
|
|
588
|
+
},
|
|
589
|
+
configurePreviewServer(server) {
|
|
590
|
+
if (resolved?.preset !== "fetch") return;
|
|
591
|
+
server.middlewares.use(previewMiddleware(path.join(resolved.outDir, "server.js")));
|
|
592
|
+
},
|
|
593
|
+
configureServer(server) {
|
|
594
|
+
if (resolved?.preset === "celld") {
|
|
595
|
+
ensureWorkerDom();
|
|
596
|
+
const { ssr } = server.environments;
|
|
597
|
+
const resolve = ssr?.config?.resolve;
|
|
598
|
+
if (resolve) {
|
|
599
|
+
resolve.conditions = [
|
|
600
|
+
"node",
|
|
601
|
+
"import",
|
|
602
|
+
"module",
|
|
603
|
+
"default"
|
|
604
|
+
];
|
|
605
|
+
resolve.noExternal = ["effect", "oxidejs"];
|
|
606
|
+
}
|
|
607
|
+
const ssrOpts = ssr?.config?.ssr;
|
|
608
|
+
if (ssrOpts && isObject(ssrOpts)) {
|
|
609
|
+
ssrOpts.target = "node";
|
|
610
|
+
ssrOpts.noExternal = ["effect", "oxidejs"];
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const invalidateActions = () => {
|
|
614
|
+
for (const env of Object.values(server.environments)) {
|
|
615
|
+
const mod = env.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ACTIONS_ID);
|
|
616
|
+
if (mod) env.moduleGraph.invalidateModule(mod);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
const fetchRouter = async () => {
|
|
620
|
+
return await server.ssrLoadModule(VIRTUAL_ACTIONS_ID);
|
|
621
|
+
};
|
|
622
|
+
let routerReady;
|
|
623
|
+
const loadRouter = async () => {
|
|
624
|
+
if (routerReady === void 0) routerReady = fetchRouter();
|
|
625
|
+
const current = routerReady;
|
|
626
|
+
try {
|
|
627
|
+
return await current;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
if (routerReady === current) routerReady = void 0;
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const refreshRouter = () => {
|
|
634
|
+
routerReady = void 0;
|
|
635
|
+
};
|
|
636
|
+
server.watcher.on("all", (_event, file) => {
|
|
637
|
+
if (isServerFileId(file)) {
|
|
638
|
+
invalidateActions();
|
|
639
|
+
refreshRouter();
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
const loadRpc = () => server.ssrLoadModule("oxidejs/rpc");
|
|
643
|
+
const logActionError = (error) => {
|
|
644
|
+
server.config.logger.error(`oxidejs: action handler failed: ${String(error)}`);
|
|
645
|
+
};
|
|
646
|
+
const opts = resolved;
|
|
647
|
+
const wireActions = () => {
|
|
648
|
+
if (!opts) return;
|
|
649
|
+
server.middlewares.use(actionMiddleware(loadRouter, loadRpc, opts.actionPath, opts.actionSameOrigin, opts.bodyLimit, logActionError));
|
|
650
|
+
};
|
|
651
|
+
if (opts?.actions === "ws") attachActionUpgrade(server.httpServer, loadRouter, loadRpc, opts.actionPath, opts.actionSameOrigin);
|
|
652
|
+
const handlersPromise = opts ? attachDevMiddlewareBridge(server, opts) : Promise.resolve([]);
|
|
653
|
+
if (opts?.actions !== "ws") wireActions();
|
|
654
|
+
return async () => {
|
|
655
|
+
try {
|
|
656
|
+
await loadRouter();
|
|
657
|
+
await handlersPromise;
|
|
658
|
+
} catch (error) {
|
|
659
|
+
server.config.logger.error(`oxidejs: failed to prewarm dev server: ${String(error)}`);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
writeBundle() {
|
|
665
|
+
if (!resolved) return;
|
|
666
|
+
copyPublicDir(resolved);
|
|
667
|
+
tryEmitWranglerConfig(resolved, emitState);
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
};
|
|
671
|
+
const oxidejs = /* @__PURE__ */ createUnplugin(unpluginFactory);
|
|
672
|
+
const vite = /* @__PURE__ */ (() => oxidejs.vite)();
|
|
673
|
+
//#endregion
|
|
674
|
+
export { unpluginFactory as n, vite as r, oxidejs as t };
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as OxidejsOptions } from "./types-BaZeMYcP.mjs";
|
|
2
2
|
import { UnpluginFactory } from "unplugin";
|
|
3
3
|
//#region src/plugin.d.ts
|
|
4
4
|
declare const unpluginFactory: UnpluginFactory<OxidejsOptions | undefined>;
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as unpluginFactory, r as vite, t as oxidejs } from "./plugin-
|
|
1
|
+
import { n as unpluginFactory, r as vite, t as oxidejs } from "./plugin-BIb3YnN7.mjs";
|
|
2
2
|
export { oxidejs as default, oxidejs, unpluginFactory, vite };
|