rwsdk 0.1.19 → 0.1.21

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.
Files changed (37) hide show
  1. package/dist/runtime/entries/auth.d.ts +1 -0
  2. package/dist/runtime/entries/auth.js +1 -0
  3. package/dist/runtime/entries/client.d.ts +1 -0
  4. package/dist/runtime/entries/client.js +1 -0
  5. package/dist/runtime/entries/clientSSR.d.ts +1 -0
  6. package/dist/runtime/entries/clientSSR.js +1 -0
  7. package/dist/runtime/entries/router.d.ts +1 -0
  8. package/dist/runtime/entries/router.js +1 -0
  9. package/dist/runtime/entries/ssr.d.ts +1 -0
  10. package/dist/runtime/entries/ssr.js +1 -0
  11. package/dist/runtime/entries/types/client.d.ts +1 -0
  12. package/dist/runtime/entries/types/client.js +1 -0
  13. package/dist/runtime/entries/types/shared.d.ts +7 -0
  14. package/dist/runtime/entries/types/shared.js +1 -0
  15. package/dist/runtime/entries/types/ssr.d.ts +1 -0
  16. package/dist/runtime/entries/types/ssr.js +1 -0
  17. package/dist/runtime/entries/types/worker.d.ts +1 -0
  18. package/dist/runtime/entries/types/worker.js +1 -0
  19. package/dist/runtime/entries/worker.d.ts +1 -0
  20. package/dist/runtime/entries/worker.js +1 -0
  21. package/dist/runtime/imports/client.js +1 -1
  22. package/dist/runtime/lib/auth/session.js +3 -2
  23. package/dist/runtime/lib/db/SqliteDurableObject.js +9 -2
  24. package/dist/runtime/lib/db/createDb.js +9 -2
  25. package/dist/runtime/lib/turnstile/useTurnstile.js +3 -2
  26. package/dist/runtime/register/worker.js +1 -2
  27. package/dist/runtime/requestInfo/worker.d.ts +6 -3
  28. package/dist/runtime/requestInfo/worker.js +11 -3
  29. package/dist/runtime/script.d.ts +2 -2
  30. package/dist/runtime/script.js +6 -3
  31. package/dist/runtime/worker.js +3 -3
  32. package/dist/scripts/debug-sync.mjs +3 -5
  33. package/dist/scripts/worker-run.mjs +57 -11
  34. package/dist/vite/devServerConstant.d.mts +9 -0
  35. package/dist/vite/devServerConstant.mjs +11 -0
  36. package/dist/vite/redwoodPlugin.mjs +2 -0
  37. package/package.json +1 -1
@@ -1 +1,2 @@
1
+ import "./types/worker";
1
2
  export * from "../lib/auth";
@@ -1 +1,2 @@
1
+ import "./types/worker";
1
2
  export * from "../lib/auth";
@@ -1,3 +1,4 @@
1
+ import "./types/client";
1
2
  export * from "../client";
2
3
  export * from "../register/client";
3
4
  export * from "../lib/streams/consumeEventStream";
@@ -1,3 +1,4 @@
1
+ import "./types/client";
1
2
  export * from "../client";
2
3
  export * from "../register/client";
3
4
  export * from "../lib/streams/consumeEventStream";
@@ -1 +1,2 @@
1
+ import "./types/ssr";
1
2
  export * from "../lib/streams/consumeEventStream";
@@ -1 +1,2 @@
1
+ import "./types/ssr";
1
2
  export * from "../lib/streams/consumeEventStream";
@@ -1,2 +1,3 @@
1
+ import "./types/shared";
1
2
  export * from "../lib/router";
2
3
  export * from "../lib/links";
@@ -1,2 +1,3 @@
1
+ import "./types/shared";
1
2
  export * from "../lib/router";
2
3
  export * from "../lib/links";
@@ -1 +1,2 @@
1
+ import "./types/ssr";
1
2
  export * from "../register/ssr";
@@ -1 +1,2 @@
1
+ import "./types/ssr";
1
2
  export * from "../register/ssr";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -0,0 +1,7 @@
1
+ interface ImportMeta {
2
+ readonly env: ImportMetaEnv;
3
+ }
4
+ interface ImportMetaEnv {
5
+ readonly DEV: boolean;
6
+ readonly VITE_IS_DEV_SERVER: string;
7
+ }
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -0,0 +1 @@
1
+ import "./shared";
@@ -1,3 +1,4 @@
1
+ import "./types/worker";
1
2
  export * from "../register/worker";
2
3
  export * from "../worker";
3
4
  export * from "../error";
@@ -1,3 +1,4 @@
1
+ import "./types/worker";
1
2
  export * from "../register/worker";
2
3
  export * from "../worker";
3
4
  export * from "../error";
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import memoize from "lodash/memoize";
3
3
  export const loadModule = memoize(async (id) => {
4
- if (import.meta.env.DEV && !process.env.PREVIEW) {
4
+ if (import.meta.env.VITE_IS_DEV_SERVER) {
5
5
  return await import(/* @vite-ignore */ id);
6
6
  }
7
7
  else {
@@ -1,8 +1,9 @@
1
1
  import { ErrorResponse } from "../../error";
2
- import { IS_DEV } from "../../constants";
3
2
  import { env } from "cloudflare:workers";
4
3
  const AUTH_SECRET_KEY = env.AUTH_SECRET_KEY ??
5
- (IS_DEV ? "development-secret-key-do-not-use-in-production" : undefined);
4
+ (import.meta.env.VITE_IS_DEV_SERVER
5
+ ? "development-secret-key-do-not-use-in-production"
6
+ : undefined);
6
7
  if (AUTH_SECRET_KEY === "") {
7
8
  console.warn("AUTH_SECRET_KEY is set but empty. Please provide a non-empty secret key for session store security.");
8
9
  }
@@ -1,6 +1,6 @@
1
1
  import { DODialect } from "kysely-do";
2
2
  import { DurableObject } from "cloudflare:workers";
3
- import { Kysely } from "kysely";
3
+ import { Kysely, ParseJSONResultsPlugin, } from "kysely";
4
4
  import { createMigrator } from "./index.js";
5
5
  import debug from "../debug.js";
6
6
  const log = debug("sdk:do-db");
@@ -13,6 +13,7 @@ export class SqliteDurableObject extends DurableObject {
13
13
  this.migrationTableName = migrationTableName;
14
14
  this.kysely = new Kysely({
15
15
  dialect: new DODialect({ ctx }),
16
+ plugins: [new ParseJSONResultsPlugin()],
16
17
  });
17
18
  }
18
19
  async initialize() {
@@ -22,7 +23,13 @@ export class SqliteDurableObject extends DurableObject {
22
23
  }
23
24
  log("Initializing Durable Object database");
24
25
  const migrator = createMigrator(this.kysely, this.migrations, this.migrationTableName);
25
- await migrator.migrateToLatest();
26
+ const result = await migrator.migrateToLatest();
27
+ if (result.error) {
28
+ console.log("rwsdk/db: Migrations failed, rolling back and throwing with the migration error: %O", result.results);
29
+ await migrator.migrateDown();
30
+ throw result.error;
31
+ }
32
+ log("Migrations results", result.results);
26
33
  this.initialized = true;
27
34
  log("Database initialization complete");
28
35
  }
@@ -1,5 +1,5 @@
1
1
  import { Kysely } from "kysely";
2
- import { requestInfo } from "../../requestInfo/worker.js";
2
+ import { requestInfo, waitForRequestInfo } from "../../requestInfo/worker.js";
3
3
  import { DOWorkerDialect } from "./DOWorkerDialect.js";
4
4
  const createDurableObjectDb = (durableObjectBinding, name = "main") => {
5
5
  const durableObjectId = durableObjectBinding.idFromName(name);
@@ -12,6 +12,13 @@ const createDurableObjectDb = (durableObjectBinding, name = "main") => {
12
12
  export function createDb(durableObjectBinding, name = "main") {
13
13
  const cacheKey = `${durableObjectBinding}_${name}`;
14
14
  const doCreateDb = () => {
15
+ if (!requestInfo.rw) {
16
+ throw new Error(`
17
+ rwsdk: A database created using createDb() was accessed before requestInfo was available.
18
+
19
+ Please make sure database access is happening in a request handler or action handler.
20
+ `);
21
+ }
15
22
  let db = requestInfo.rw.databases.get(cacheKey);
16
23
  if (!db) {
17
24
  db = createDurableObjectDb(durableObjectBinding, name);
@@ -19,7 +26,7 @@ export function createDb(durableObjectBinding, name = "main") {
19
26
  }
20
27
  return db;
21
28
  };
22
- doCreateDb();
29
+ waitForRequestInfo().then(() => doCreateDb());
23
30
  return new Proxy({}, {
24
31
  get(target, prop, receiver) {
25
32
  const db = doCreateDb();
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import { useRef, useCallback } from "react";
3
- import { IS_DEV } from "../../constants";
4
3
  export function useTurnstile(siteKey) {
5
4
  const containerRef = useRef(null);
6
5
  const resolverRef = useRef(Promise.withResolvers());
@@ -10,7 +9,9 @@ export function useTurnstile(siteKey) {
10
9
  containerRef.current &&
11
10
  window.turnstile) {
12
11
  widgetIdRef.current = window.turnstile.render(containerRef.current, {
13
- sitekey: IS_DEV ? "1x00000000000000000000AA" : siteKey,
12
+ sitekey: import.meta.env.VITE_IS_DEV_SERVER
13
+ ? "1x00000000000000000000AA"
14
+ : siteKey,
14
15
  callback: (token) => resolverRef.current.resolve(token),
15
16
  });
16
17
  }
@@ -1,6 +1,5 @@
1
1
  import { registerServerReference as baseRegisterServerReference, registerClientReference as baseRegisterClientReference, decodeReply, } from "react-server-dom-webpack/server.edge";
2
2
  import { getServerModuleExport } from "../imports/worker.js";
3
- import { IS_DEV } from "../constants";
4
3
  export function registerServerReference(action, id, name) {
5
4
  if (typeof action !== "function") {
6
5
  return action;
@@ -31,7 +30,7 @@ export async function rscActionHandler(req) {
31
30
  : await req.text();
32
31
  const args = (await decodeReply(data, null));
33
32
  const actionId = url.searchParams.get("__rsc_action_id");
34
- if (IS_DEV && actionId === "__rsc_hot_update") {
33
+ if (import.meta.env.VITE_IS_DEV_SERVER && actionId === "__rsc_hot_update") {
35
34
  return null;
36
35
  }
37
36
  const action = await getServerModuleExport(actionId);
@@ -1,5 +1,8 @@
1
1
  import { RequestInfo, DefaultAppContext } from "./types";
2
- export declare const requestInfo: RequestInfo<DefaultAppContext>;
2
+ type DefaultRequestInfo = RequestInfo<DefaultAppContext>;
3
+ export declare const requestInfo: DefaultRequestInfo;
3
4
  export declare function getRequestInfo(): RequestInfo;
4
- export declare function runWithRequestInfo<Result>(context: Record<string, any>, fn: () => Result): Result;
5
- export declare function runWithRequestInfoOverrides<Result>(overrides: Record<string, any>, fn: () => Result): Result;
5
+ export declare function waitForRequestInfo(): Promise<DefaultRequestInfo>;
6
+ export declare function runWithRequestInfo<Result>(nextRequestInfo: DefaultRequestInfo, fn: () => Result): Result;
7
+ export declare function runWithRequestInfoOverrides<Result>(overrides: Partial<DefaultRequestInfo>, fn: () => Result): Result;
8
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from "async_hooks";
2
+ const requestInfoDeferred = Promise.withResolvers();
2
3
  const requestInfoStore = new AsyncLocalStorage();
3
4
  const requestInfoBase = {};
4
5
  const REQUEST_INFO_KEYS = ["request", "params", "ctx", "headers", "rw", "cf"];
@@ -20,8 +21,15 @@ export function getRequestInfo() {
20
21
  }
21
22
  return store;
22
23
  }
23
- export function runWithRequestInfo(context, fn) {
24
- return requestInfoStore.run(context, fn);
24
+ export function waitForRequestInfo() {
25
+ return requestInfoDeferred.promise;
26
+ }
27
+ export function runWithRequestInfo(nextRequestInfo, fn) {
28
+ const runWithRequestInfoFn = () => {
29
+ requestInfoDeferred.resolve(nextRequestInfo);
30
+ return fn();
31
+ };
32
+ return requestInfoStore.run(nextRequestInfo, runWithRequestInfoFn);
25
33
  }
26
34
  export function runWithRequestInfoOverrides(overrides, fn) {
27
35
  const requestInfo = requestInfoStore.getStore();
@@ -29,5 +37,5 @@ export function runWithRequestInfoOverrides(overrides, fn) {
29
37
  ...requestInfo,
30
38
  ...overrides,
31
39
  };
32
- return requestInfoStore.run(newRequestInfo, fn);
40
+ return runWithRequestInfo(newRequestInfo, fn);
33
41
  }
@@ -1,5 +1,5 @@
1
1
  export declare const defineScript: (fn: ({ env }: {
2
- env: Env;
2
+ env: Cloudflare.Env;
3
3
  }) => Promise<unknown>) => {
4
- fetch(request: Request, env: Env): Promise<Response>;
4
+ fetch: (request: Request, env: Env, cf: ExecutionContext) => Promise<Response>;
5
5
  };
@@ -1,8 +1,11 @@
1
+ import { defineApp } from "./worker";
2
+ import { env } from "cloudflare:workers";
1
3
  export const defineScript = (fn) => {
2
- return {
3
- async fetch(request, env) {
4
+ const app = defineApp([
5
+ async () => {
4
6
  await fn({ env });
5
7
  return new Response("Done!");
6
8
  },
7
- };
9
+ ]);
10
+ return app;
8
11
  };
@@ -7,7 +7,6 @@ import { ErrorResponse } from "./error";
7
7
  import { getRequestInfo, runWithRequestInfo, runWithRequestInfoOverrides, } from "./requestInfo/worker";
8
8
  import { defineRoutes } from "./lib/router";
9
9
  import { generateNonce } from "./lib/utils";
10
- import { IS_DEV } from "./constants";
11
10
  import { ssrWebpackRequire } from "./imports/worker";
12
11
  export const defineApp = (routes) => {
13
12
  return {
@@ -22,7 +21,8 @@ export const defineApp = (routes) => {
22
21
  url.pathname = url.pathname.slice("/assets/".length);
23
22
  return env.ASSETS.fetch(new Request(url.toString(), request));
24
23
  }
25
- else if (IS_DEV && request.url.includes("/__vite_preamble__")) {
24
+ else if (import.meta.env.VITE_IS_DEV_SERVER &&
25
+ request.url.includes("/__vite_preamble__")) {
26
26
  return new Response('import RefreshRuntime from "/@react-refresh"; RefreshRuntime.injectIntoGlobalHook(window); window.$RefreshReg$ = () => {}; window.$RefreshSig$ = () => (type) => type; window.__vite_plugin_react_preamble_installed__ = true;', {
27
27
  headers: {
28
28
  "content-type": "text/javascript",
@@ -76,7 +76,7 @@ export const defineApp = (routes) => {
76
76
  };
77
77
  const renderPage = async (requestInfo, Page, onError) => {
78
78
  if (isClientReference(requestInfo.rw.Document)) {
79
- if (IS_DEV) {
79
+ if (import.meta.env.DEV) {
80
80
  console.error("Document cannot be a client component");
81
81
  }
82
82
  return new Response(null, {
@@ -136,10 +136,8 @@ export const debugSync = async (opts) => {
136
136
  throw e;
137
137
  }
138
138
  // Initial sync for watch mode. We do it *after* acquiring the lock.
139
- let initialSyncOk = false;
140
139
  try {
141
140
  await performSync(sdkDir, targetDir);
142
- initialSyncOk = true;
143
141
  }
144
142
  catch (error) {
145
143
  console.error("❌ Initial sync failed:", error);
@@ -196,9 +194,9 @@ export const debugSync = async (opts) => {
196
194
  };
197
195
  process.on("SIGINT", cleanup);
198
196
  process.on("SIGTERM", cleanup);
199
- if (initialSyncOk) {
200
- runWatchedCommand();
201
- }
197
+ // Run the watched command even if the initial sync fails. This allows the
198
+ // user to see application errors and iterate more quickly.
199
+ runWatchedCommand();
202
200
  };
203
201
  if (import.meta.url === new URL(process.argv[1], import.meta.url).href) {
204
202
  const args = process.argv.slice(2);
@@ -1,9 +1,13 @@
1
+ import path from "path";
1
2
  import { resolve } from "path";
2
3
  import { writeFile } from "fs/promises";
3
4
  import { unstable_readConfig } from "wrangler";
4
5
  import { createServer as createViteServer } from "vite";
5
6
  import tmp from "tmp-promise";
6
7
  import baseDebug from "debug";
8
+ import enhancedResolve from "enhanced-resolve";
9
+ import { readFile } from "fs/promises";
10
+ import { Lang, parse } from "@ast-grep/napi";
7
11
  import { redwood } from "../vite/index.mjs";
8
12
  import { findWranglerConfig } from "../lib/findWranglerConfig.mjs";
9
13
  const debug = baseDebug("rwsdk:worker-run");
@@ -22,28 +26,71 @@ export const runWorkerScript = async (relativeScriptPath) => {
22
26
  debug("Using wrangler config: %s", workerConfigPath);
23
27
  const workerConfig = unstable_readConfig({
24
28
  config: workerConfigPath,
29
+ env: "dev",
25
30
  });
26
- const tmpWorkerPath = await tmp.file({
27
- postfix: ".json",
31
+ const durableObjectsToExport = workerConfig.durable_objects?.bindings
32
+ .filter((binding) => !binding.script_name)
33
+ .map((binding) => binding.class_name) ?? [];
34
+ const workerEntryRelativePath = workerConfig.main;
35
+ const workerEntryPath = workerEntryRelativePath ?? path.join(process.cwd(), "src/worker.tsx");
36
+ const durableObjectExports = [];
37
+ if (durableObjectsToExport.length > 0) {
38
+ const resolver = enhancedResolve.create.sync({
39
+ extensions: [".mts", ".ts", ".tsx", ".mjs", ".js", ".jsx", ".json"],
40
+ });
41
+ const workerEntryContents = await readFile(workerEntryPath, "utf-8");
42
+ const workerEntryAst = parse(Lang.Tsx, workerEntryContents);
43
+ const exportDeclarations = [
44
+ ...workerEntryAst.root().findAll('export { $$$EXPORTS } from "$MODULE"'),
45
+ ...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
46
+ ...workerEntryAst.root().findAll("export { $$$EXPORTS } from '$MODULE'"),
47
+ ];
48
+ for (const exportDeclaration of exportDeclarations) {
49
+ const moduleMatch = exportDeclaration.getMatch("MODULE");
50
+ const exportsMatch = exportDeclaration.getMultipleMatches("EXPORTS");
51
+ if (!moduleMatch || exportsMatch.length === 0) {
52
+ continue;
53
+ }
54
+ const modulePath = moduleMatch.text();
55
+ const specifiers = exportsMatch.map((m) => m.text().trim());
56
+ for (const specifier of specifiers) {
57
+ if (durableObjectsToExport.includes(specifier)) {
58
+ const resolvedPath = resolver(path.dirname(workerEntryPath), modulePath);
59
+ durableObjectExports.push(`export { ${specifier} } from "${resolvedPath}";`);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ const tmpDir = await tmp.dir({
65
+ prefix: "rw-worker-run-",
66
+ unsafeCleanup: true,
28
67
  });
68
+ const relativeTmpWorkerEntryPath = "worker.tsx";
69
+ const tmpWorkerPath = path.join(tmpDir.path, "wrangler.json");
70
+ const tmpWorkerEntryPath = path.join(tmpDir.path, relativeTmpWorkerEntryPath);
29
71
  const scriptWorkerConfig = {
30
72
  ...workerConfig,
31
- configPath: tmpWorkerPath.path,
32
- userConfigPath: tmpWorkerPath.path,
33
- main: scriptPath,
73
+ configPath: tmpWorkerPath,
74
+ userConfigPath: tmpWorkerPath,
75
+ main: relativeTmpWorkerEntryPath,
34
76
  };
35
77
  try {
36
- await writeFile(tmpWorkerPath.path, JSON.stringify(scriptWorkerConfig, null, 2));
37
- debug("Worker config written to: %s", tmpWorkerPath.path);
78
+ await writeFile(tmpWorkerPath, JSON.stringify(scriptWorkerConfig, null, 2));
79
+ await writeFile(tmpWorkerEntryPath, `
80
+ ${durableObjectExports.join("\n")}
81
+ export { default } from "${scriptPath}";
82
+ `);
83
+ debug("Worker config written to: %s", tmpWorkerPath);
84
+ debug("Worker entry written to: %s", tmpWorkerEntryPath);
38
85
  process.env.RWSDK_WORKER_RUN = "1";
39
86
  const server = await createViteServer({
40
87
  configFile: false,
41
88
  plugins: [
42
89
  redwood({
43
- configPath: tmpWorkerPath.path,
90
+ configPath: tmpWorkerPath,
44
91
  includeCloudflarePlugin: true,
45
92
  entry: {
46
- worker: scriptPath,
93
+ worker: tmpWorkerEntryPath,
47
94
  },
48
95
  }),
49
96
  ],
@@ -65,13 +112,12 @@ export const runWorkerScript = async (relativeScriptPath) => {
65
112
  }
66
113
  finally {
67
114
  debug("Closing server...");
68
- await server.close();
115
+ server.close();
69
116
  debug("Server closed");
70
117
  }
71
118
  }
72
119
  finally {
73
120
  debug("Closing inspector servers...");
74
- await tmpWorkerPath.cleanup();
75
121
  debug("Temporary files cleaned up");
76
122
  }
77
123
  // todo(justinvdm, 01 Apr 2025): Investigate what handles are remaining open
@@ -0,0 +1,9 @@
1
+ import { Plugin } from "vite";
2
+ declare global {
3
+ namespace NodeJS {
4
+ interface ProcessEnv {
5
+ VITE_IS_DEV_SERVER: string;
6
+ }
7
+ }
8
+ }
9
+ export declare const devServerConstantPlugin: () => Plugin;
@@ -0,0 +1,11 @@
1
+ export const devServerConstantPlugin = () => {
2
+ return {
3
+ name: "rwsdk:dev-server-constant",
4
+ config(_, { command, isPreview }) {
5
+ if (command === "serve" && isPreview) {
6
+ // context(justinvdm, 21 Jul 2025): Vite forwards this as `import.meta.env.DEV_IS_DEV_SERVER`
7
+ process.env.VITE_IS_DEV_SERVER = "1";
8
+ }
9
+ },
10
+ };
11
+ };
@@ -1,6 +1,7 @@
1
1
  import { resolve } from "node:path";
2
2
  import { unstable_readConfig } from "wrangler";
3
3
  import { cloudflare } from "@cloudflare/vite-plugin";
4
+ import { devServerConstantPlugin } from "./devServerConstant.mjs";
4
5
  import { hasOwnCloudflareVitePlugin } from "./hasOwnCloudflareVitePlugin.mjs";
5
6
  import reactPlugin from "@vitejs/plugin-react";
6
7
  import tsconfigPaths from "vite-tsconfig-paths";
@@ -54,6 +55,7 @@ export const redwoodPlugin = async (options = {}) => {
54
55
  }
55
56
  return [
56
57
  devServerTimingPlugin(),
58
+ devServerConstantPlugin(),
57
59
  configPlugin({
58
60
  silent: options.silent ?? false,
59
61
  projectRootDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {