silosdk 0.0.0 → 0.0.1

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 (85) hide show
  1. package/README.md +3 -1
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/cli/d1.cjs +93 -0
  4. package/dist/cli/d1.mjs +92 -0
  5. package/dist/cli/index.cjs +93 -0
  6. package/dist/cli/index.d.cts +1 -0
  7. package/dist/cli/index.d.mts +1 -0
  8. package/dist/cli/index.mjs +94 -0
  9. package/dist/cli/init.cjs +134 -0
  10. package/dist/cli/init.mjs +133 -0
  11. package/dist/cli/kv.cjs +63 -0
  12. package/dist/cli/kv.mjs +60 -0
  13. package/dist/cli/r2.cjs +83 -0
  14. package/dist/cli/r2.mjs +82 -0
  15. package/dist/cli/wrangler.cjs +93 -0
  16. package/dist/cli/wrangler.mjs +89 -0
  17. package/dist/local/adapters/cloudflare.cjs +200 -0
  18. package/dist/local/adapters/cloudflare.d.cts +50 -0
  19. package/dist/local/adapters/cloudflare.d.mts +50 -0
  20. package/dist/local/adapters/cloudflare.mjs +200 -0
  21. package/dist/local/auth-context.cjs +14 -0
  22. package/dist/local/auth-context.d.cts +7 -0
  23. package/dist/local/auth-context.d.mts +7 -0
  24. package/dist/local/auth-context.mjs +12 -0
  25. package/dist/local/auth.cjs +109 -0
  26. package/dist/local/auth.d.cts +26 -0
  27. package/dist/local/auth.d.mts +26 -0
  28. package/dist/local/auth.mjs +99 -0
  29. package/dist/local/commit.cjs +350 -0
  30. package/dist/local/commit.d.cts +59 -0
  31. package/dist/local/commit.d.mts +59 -0
  32. package/dist/local/commit.mjs +349 -0
  33. package/dist/local/config.cjs +17 -0
  34. package/dist/local/config.mjs +15 -0
  35. package/dist/local/index.cjs +16 -0
  36. package/dist/local/index.d.cts +10 -0
  37. package/dist/local/index.d.mts +10 -0
  38. package/dist/local/index.mjs +9 -0
  39. package/dist/local/provider.cjs +204 -0
  40. package/dist/local/provider.d.cts +25 -0
  41. package/dist/local/provider.d.mts +25 -0
  42. package/dist/local/provider.mjs +203 -0
  43. package/dist/local/query-store.cjs +276 -0
  44. package/dist/local/query-store.mjs +274 -0
  45. package/dist/local/storage.cjs +71 -0
  46. package/dist/local/storage.d.cts +7 -0
  47. package/dist/local/storage.d.mts +7 -0
  48. package/dist/local/storage.mjs +68 -0
  49. package/dist/local/sync.cjs +124 -0
  50. package/dist/local/sync.d.cts +36 -0
  51. package/dist/local/sync.d.mts +36 -0
  52. package/dist/local/sync.mjs +122 -0
  53. package/dist/local/view.cjs +257 -0
  54. package/dist/local/view.d.cts +24 -0
  55. package/dist/local/view.d.mts +24 -0
  56. package/dist/local/view.mjs +254 -0
  57. package/dist/package.cjs +11 -0
  58. package/dist/package.mjs +5 -0
  59. package/dist/schema/index.cjs +276 -0
  60. package/dist/schema/index.d.cts +207 -0
  61. package/dist/schema/index.d.mts +207 -0
  62. package/dist/schema/index.mjs +265 -0
  63. package/dist/server/auth.cjs +132 -0
  64. package/dist/server/auth.d.cts +49 -0
  65. package/dist/server/auth.d.mts +49 -0
  66. package/dist/server/auth.mjs +122 -0
  67. package/dist/server/d1.cjs +120 -0
  68. package/dist/server/d1.mjs +116 -0
  69. package/dist/server/do.cjs +132 -0
  70. package/dist/server/do.d.cts +21 -0
  71. package/dist/server/do.d.mts +21 -0
  72. package/dist/server/do.mjs +131 -0
  73. package/dist/server/index.cjs +355 -0
  74. package/dist/server/index.d.cts +65 -0
  75. package/dist/server/index.d.mts +65 -0
  76. package/dist/server/index.mjs +348 -0
  77. package/dist/server/protect.cjs +34 -0
  78. package/dist/server/protect.d.cts +32 -0
  79. package/dist/server/protect.d.mts +32 -0
  80. package/dist/server/protect.mjs +33 -0
  81. package/dist/server/r2.cjs +58 -0
  82. package/dist/server/r2.d.cts +4 -0
  83. package/dist/server/r2.d.mts +4 -0
  84. package/dist/server/r2.mjs +53 -0
  85. package/package.json +55 -2
@@ -0,0 +1,63 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_wrangler = require('./wrangler.cjs');
3
+ let node_fs = require("node:fs");
4
+ let node_path = require("node:path");
5
+
6
+ //#region src/cli/kv.ts
7
+ async function runKvCreate() {
8
+ const dir = require_wrangler.siloDir();
9
+ console.log("Creating KV namespace SILO_SESSIONS...");
10
+ const { stdout, code } = await require_wrangler.runWranglerCaptured([
11
+ "kv",
12
+ "namespace",
13
+ "create",
14
+ "SILO_SESSIONS"
15
+ ]);
16
+ if (code !== 0) {
17
+ console.error("Failed to create KV namespace. Check wrangler output above.");
18
+ process.exit(1);
19
+ }
20
+ const match = stdout.match(/"id"\s*:\s*"([a-f0-9]{32})"/) ?? stdout.match(/id\s*=\s*"([a-f0-9]{32})"/) ?? stdout.match(/id[^a-f0-9]*([a-f0-9]{32})/);
21
+ if (!match) {
22
+ console.error(`Could not parse KV namespace id from wrangler output.
23
+ Run: silo kv create --id <namespace-id> to set it manually.
24
+
25
+ Wrangler output:\n${stdout}`);
26
+ process.exit(1);
27
+ }
28
+ const id = match[1];
29
+ patchWrangler(dir, id);
30
+ console.log(`KV namespace created (id: ${id}) and patched into wrangler.jsonc`);
31
+ }
32
+ function runKvPatch(id) {
33
+ patchWrangler(require_wrangler.siloDir(), id);
34
+ console.log(`Patched wrangler.jsonc with KV namespace id: ${id}`);
35
+ }
36
+ function patchWrangler(dir, id) {
37
+ const wranglerPath = (0, node_path.resolve)(dir, "wrangler.jsonc");
38
+ let raw;
39
+ try {
40
+ raw = (0, node_fs.readFileSync)(wranglerPath, "utf8");
41
+ } catch {
42
+ console.error(`Could not read ${wranglerPath}`);
43
+ process.exit(1);
44
+ }
45
+ const patched = raw.replace(/("binding"\s*:\s*"SILO_SESSIONS"[^}]*"id"\s*:\s*)"[^"]*"/s, `$1"${id}"`);
46
+ if (patched === raw) {
47
+ console.warn("Could not find SILO_SESSIONS id placeholder in wrangler.jsonc — edit it manually.");
48
+ return;
49
+ }
50
+ (0, node_fs.writeFileSync)(wranglerPath, patched, "utf8");
51
+ }
52
+ async function runSecretPut(key) {
53
+ await require_wrangler.runWrangler([
54
+ "secret",
55
+ "put",
56
+ key
57
+ ]);
58
+ }
59
+
60
+ //#endregion
61
+ exports.runKvCreate = runKvCreate;
62
+ exports.runKvPatch = runKvPatch;
63
+ exports.runSecretPut = runSecretPut;
@@ -0,0 +1,60 @@
1
+ import { runWrangler, runWranglerCaptured, siloDir } from "./wrangler.mjs";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ //#region src/cli/kv.ts
6
+ async function runKvCreate() {
7
+ const dir = siloDir();
8
+ console.log("Creating KV namespace SILO_SESSIONS...");
9
+ const { stdout, code } = await runWranglerCaptured([
10
+ "kv",
11
+ "namespace",
12
+ "create",
13
+ "SILO_SESSIONS"
14
+ ]);
15
+ if (code !== 0) {
16
+ console.error("Failed to create KV namespace. Check wrangler output above.");
17
+ process.exit(1);
18
+ }
19
+ const match = stdout.match(/"id"\s*:\s*"([a-f0-9]{32})"/) ?? stdout.match(/id\s*=\s*"([a-f0-9]{32})"/) ?? stdout.match(/id[^a-f0-9]*([a-f0-9]{32})/);
20
+ if (!match) {
21
+ console.error(`Could not parse KV namespace id from wrangler output.
22
+ Run: silo kv create --id <namespace-id> to set it manually.
23
+
24
+ Wrangler output:\n${stdout}`);
25
+ process.exit(1);
26
+ }
27
+ const id = match[1];
28
+ patchWrangler(dir, id);
29
+ console.log(`KV namespace created (id: ${id}) and patched into wrangler.jsonc`);
30
+ }
31
+ function runKvPatch(id) {
32
+ patchWrangler(siloDir(), id);
33
+ console.log(`Patched wrangler.jsonc with KV namespace id: ${id}`);
34
+ }
35
+ function patchWrangler(dir, id) {
36
+ const wranglerPath = resolve(dir, "wrangler.jsonc");
37
+ let raw;
38
+ try {
39
+ raw = readFileSync(wranglerPath, "utf8");
40
+ } catch {
41
+ console.error(`Could not read ${wranglerPath}`);
42
+ process.exit(1);
43
+ }
44
+ const patched = raw.replace(/("binding"\s*:\s*"SILO_SESSIONS"[^}]*"id"\s*:\s*)"[^"]*"/s, `$1"${id}"`);
45
+ if (patched === raw) {
46
+ console.warn("Could not find SILO_SESSIONS id placeholder in wrangler.jsonc — edit it manually.");
47
+ return;
48
+ }
49
+ writeFileSync(wranglerPath, patched, "utf8");
50
+ }
51
+ async function runSecretPut(key) {
52
+ await runWrangler([
53
+ "secret",
54
+ "put",
55
+ key
56
+ ]);
57
+ }
58
+
59
+ //#endregion
60
+ export { runKvCreate, runKvPatch, runSecretPut };
@@ -0,0 +1,83 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_wrangler = require('./wrangler.cjs');
3
+ let node_fs = require("node:fs");
4
+ let node_path = require("node:path");
5
+
6
+ //#region src/cli/r2.ts
7
+ async function runR2(args) {
8
+ const nameFlag = args.indexOf("--name");
9
+ if (nameFlag !== -1) {
10
+ const bucketName = args[nameFlag + 1];
11
+ if (!bucketName) {
12
+ console.error("Error: --name requires a bucket name");
13
+ process.exit(1);
14
+ }
15
+ runR2Patch(bucketName);
16
+ return;
17
+ }
18
+ await runR2Create();
19
+ }
20
+ async function runR2Create() {
21
+ const wranglerPath = (0, node_path.resolve)(require_wrangler.siloDir(), "wrangler.jsonc");
22
+ let source;
23
+ try {
24
+ source = (0, node_fs.readFileSync)(wranglerPath, "utf8");
25
+ } catch {
26
+ console.error("Error: silo/wrangler.jsonc not found. Run `silo init` first.");
27
+ process.exit(1);
28
+ }
29
+ const bucketName = extractBucketName(source);
30
+ if (!bucketName) {
31
+ console.error("Error: Could not determine R2 bucket name from silo/wrangler.jsonc.");
32
+ console.error("Use: silo r2 create --name <bucket-name> to patch manually.");
33
+ process.exit(1);
34
+ }
35
+ console.log(`Creating R2 bucket: ${bucketName} ...`);
36
+ try {
37
+ await require_wrangler.runWrangler([
38
+ "r2",
39
+ "bucket",
40
+ "create",
41
+ bucketName
42
+ ]);
43
+ } catch (err) {
44
+ console.error("wrangler r2 bucket create failed.");
45
+ process.exit(1);
46
+ }
47
+ runR2Patch(bucketName);
48
+ console.log(`R2 bucket created and patched: ${bucketName}`);
49
+ }
50
+ function runR2Patch(bucketName) {
51
+ const wranglerPath = (0, node_path.resolve)(require_wrangler.siloDir(), "wrangler.jsonc");
52
+ let source;
53
+ try {
54
+ source = (0, node_fs.readFileSync)(wranglerPath, "utf8");
55
+ } catch {
56
+ console.error("Error: silo/wrangler.jsonc not found. Run `silo init` first.");
57
+ process.exit(1);
58
+ }
59
+ const replaced = source.replace(/("binding"\s*:\s*"SILO_STORAGE"[^}]*"bucket_name"\s*:\s*)"[^"]*"/s, `$1"${bucketName}"`);
60
+ if (replaced !== source) {
61
+ (0, node_fs.writeFileSync)(wranglerPath, replaced, "utf8");
62
+ console.log(`Patched silo/wrangler.jsonc with R2 bucket_name = "${bucketName}"`);
63
+ return;
64
+ }
65
+ const insertion = `,\n "r2_buckets": [\n {\n "binding": "SILO_STORAGE",\n "bucket_name": "${bucketName}"\n }\n ]\n`;
66
+ const lastBrace = source.lastIndexOf("}");
67
+ if (lastBrace === -1) {
68
+ console.error("Error: Invalid JSONC in silo/wrangler.jsonc");
69
+ process.exit(1);
70
+ }
71
+ (0, node_fs.writeFileSync)(wranglerPath, source.slice(0, lastBrace) + insertion + source.slice(lastBrace), "utf8");
72
+ console.log(`Added r2_buckets to silo/wrangler.jsonc with bucket_name = "${bucketName}"`);
73
+ }
74
+ function extractBucketName(source) {
75
+ const existing = source.match(/"binding"\s*:\s*"SILO_STORAGE"[^}]*"bucket_name"\s*:\s*"([^"]+)"/s)?.[1];
76
+ if (existing && !existing.startsWith("TODO:")) return existing;
77
+ const workerName = source.match(/"name"\s*:\s*"([^"]+)"/)?.[1];
78
+ if (!workerName) return null;
79
+ return `${workerName}-storage`;
80
+ }
81
+
82
+ //#endregion
83
+ exports.runR2 = runR2;
@@ -0,0 +1,82 @@
1
+ import { runWrangler, siloDir } from "./wrangler.mjs";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ //#region src/cli/r2.ts
6
+ async function runR2(args) {
7
+ const nameFlag = args.indexOf("--name");
8
+ if (nameFlag !== -1) {
9
+ const bucketName = args[nameFlag + 1];
10
+ if (!bucketName) {
11
+ console.error("Error: --name requires a bucket name");
12
+ process.exit(1);
13
+ }
14
+ runR2Patch(bucketName);
15
+ return;
16
+ }
17
+ await runR2Create();
18
+ }
19
+ async function runR2Create() {
20
+ const wranglerPath = resolve(siloDir(), "wrangler.jsonc");
21
+ let source;
22
+ try {
23
+ source = readFileSync(wranglerPath, "utf8");
24
+ } catch {
25
+ console.error("Error: silo/wrangler.jsonc not found. Run `silo init` first.");
26
+ process.exit(1);
27
+ }
28
+ const bucketName = extractBucketName(source);
29
+ if (!bucketName) {
30
+ console.error("Error: Could not determine R2 bucket name from silo/wrangler.jsonc.");
31
+ console.error("Use: silo r2 create --name <bucket-name> to patch manually.");
32
+ process.exit(1);
33
+ }
34
+ console.log(`Creating R2 bucket: ${bucketName} ...`);
35
+ try {
36
+ await runWrangler([
37
+ "r2",
38
+ "bucket",
39
+ "create",
40
+ bucketName
41
+ ]);
42
+ } catch (err) {
43
+ console.error("wrangler r2 bucket create failed.");
44
+ process.exit(1);
45
+ }
46
+ runR2Patch(bucketName);
47
+ console.log(`R2 bucket created and patched: ${bucketName}`);
48
+ }
49
+ function runR2Patch(bucketName) {
50
+ const wranglerPath = resolve(siloDir(), "wrangler.jsonc");
51
+ let source;
52
+ try {
53
+ source = readFileSync(wranglerPath, "utf8");
54
+ } catch {
55
+ console.error("Error: silo/wrangler.jsonc not found. Run `silo init` first.");
56
+ process.exit(1);
57
+ }
58
+ const replaced = source.replace(/("binding"\s*:\s*"SILO_STORAGE"[^}]*"bucket_name"\s*:\s*)"[^"]*"/s, `$1"${bucketName}"`);
59
+ if (replaced !== source) {
60
+ writeFileSync(wranglerPath, replaced, "utf8");
61
+ console.log(`Patched silo/wrangler.jsonc with R2 bucket_name = "${bucketName}"`);
62
+ return;
63
+ }
64
+ const insertion = `,\n "r2_buckets": [\n {\n "binding": "SILO_STORAGE",\n "bucket_name": "${bucketName}"\n }\n ]\n`;
65
+ const lastBrace = source.lastIndexOf("}");
66
+ if (lastBrace === -1) {
67
+ console.error("Error: Invalid JSONC in silo/wrangler.jsonc");
68
+ process.exit(1);
69
+ }
70
+ writeFileSync(wranglerPath, source.slice(0, lastBrace) + insertion + source.slice(lastBrace), "utf8");
71
+ console.log(`Added r2_buckets to silo/wrangler.jsonc with bucket_name = "${bucketName}"`);
72
+ }
73
+ function extractBucketName(source) {
74
+ const existing = source.match(/"binding"\s*:\s*"SILO_STORAGE"[^}]*"bucket_name"\s*:\s*"([^"]+)"/s)?.[1];
75
+ if (existing && !existing.startsWith("TODO:")) return existing;
76
+ const workerName = source.match(/"name"\s*:\s*"([^"]+)"/)?.[1];
77
+ if (!workerName) return null;
78
+ return `${workerName}-storage`;
79
+ }
80
+
81
+ //#endregion
82
+ export { runR2 };
@@ -0,0 +1,93 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let node_path = require("node:path");
3
+ let node_child_process = require("node:child_process");
4
+
5
+ //#region src/cli/wrangler.ts
6
+ /**
7
+ * Resolve the user's project root — the directory that contains the silo/
8
+ * directory. We assume the CLI is run from the project root (cwd).
9
+ */
10
+ function projectRoot() {
11
+ return process.cwd();
12
+ }
13
+ /**
14
+ * Absolute path to the user's silo/ directory.
15
+ */
16
+ function siloDir() {
17
+ return (0, node_path.resolve)(projectRoot(), "silo");
18
+ }
19
+ /**
20
+ * Absolute path to wrangler.jsonc config file.
21
+ */
22
+ function wranglerConfigPath() {
23
+ return (0, node_path.resolve)(siloDir(), "wrangler.jsonc");
24
+ }
25
+ /**
26
+ * Spawn wrangler with the given args, passing absolute config path.
27
+ * Resolves when the process exits 0, rejects on non-zero exit.
28
+ */
29
+ function runWrangler(args) {
30
+ return new Promise((resolve$1, reject) => {
31
+ const child = (0, node_child_process.spawn)("npx", [
32
+ "wrangler",
33
+ "--config",
34
+ wranglerConfigPath(),
35
+ ...args
36
+ ], {
37
+ cwd: siloDir(),
38
+ stdio: "inherit",
39
+ shell: process.platform === "win32"
40
+ });
41
+ child.on("error", (err) => {
42
+ reject(/* @__PURE__ */ new Error(`Failed to spawn wrangler: ${err.message}`));
43
+ });
44
+ child.on("close", (code) => {
45
+ if (code === 0) resolve$1();
46
+ else reject(/* @__PURE__ */ new Error(`wrangler exited with code ${code}`));
47
+ });
48
+ });
49
+ }
50
+ /**
51
+ * Spawn wrangler with stdout captured (for parsing output).
52
+ * stderr is inherited so the user still sees errors.
53
+ */
54
+ function runWranglerCaptured(args) {
55
+ return new Promise((resolve$1) => {
56
+ const chunks = [];
57
+ const child = (0, node_child_process.spawn)("npx", [
58
+ "wrangler",
59
+ "--config",
60
+ wranglerConfigPath(),
61
+ ...args
62
+ ], {
63
+ cwd: siloDir(),
64
+ stdio: [
65
+ "inherit",
66
+ "pipe",
67
+ "inherit"
68
+ ],
69
+ shell: process.platform === "win32"
70
+ });
71
+ child.stdout?.on("data", (chunk) => {
72
+ chunks.push(chunk);
73
+ });
74
+ child.on("error", (err) => {
75
+ resolve$1({
76
+ stdout: "",
77
+ code: 1
78
+ });
79
+ });
80
+ child.on("close", (code) => {
81
+ resolve$1({
82
+ stdout: Buffer.concat(chunks).toString("utf8"),
83
+ code: code ?? 1
84
+ });
85
+ });
86
+ });
87
+ }
88
+
89
+ //#endregion
90
+ exports.projectRoot = projectRoot;
91
+ exports.runWrangler = runWrangler;
92
+ exports.runWranglerCaptured = runWranglerCaptured;
93
+ exports.siloDir = siloDir;
@@ -0,0 +1,89 @@
1
+ import { resolve } from "node:path";
2
+ import { spawn } from "node:child_process";
3
+
4
+ //#region src/cli/wrangler.ts
5
+ /**
6
+ * Resolve the user's project root — the directory that contains the silo/
7
+ * directory. We assume the CLI is run from the project root (cwd).
8
+ */
9
+ function projectRoot() {
10
+ return process.cwd();
11
+ }
12
+ /**
13
+ * Absolute path to the user's silo/ directory.
14
+ */
15
+ function siloDir() {
16
+ return resolve(projectRoot(), "silo");
17
+ }
18
+ /**
19
+ * Absolute path to wrangler.jsonc config file.
20
+ */
21
+ function wranglerConfigPath() {
22
+ return resolve(siloDir(), "wrangler.jsonc");
23
+ }
24
+ /**
25
+ * Spawn wrangler with the given args, passing absolute config path.
26
+ * Resolves when the process exits 0, rejects on non-zero exit.
27
+ */
28
+ function runWrangler(args) {
29
+ return new Promise((resolve$1, reject) => {
30
+ const child = spawn("npx", [
31
+ "wrangler",
32
+ "--config",
33
+ wranglerConfigPath(),
34
+ ...args
35
+ ], {
36
+ cwd: siloDir(),
37
+ stdio: "inherit",
38
+ shell: process.platform === "win32"
39
+ });
40
+ child.on("error", (err) => {
41
+ reject(/* @__PURE__ */ new Error(`Failed to spawn wrangler: ${err.message}`));
42
+ });
43
+ child.on("close", (code) => {
44
+ if (code === 0) resolve$1();
45
+ else reject(/* @__PURE__ */ new Error(`wrangler exited with code ${code}`));
46
+ });
47
+ });
48
+ }
49
+ /**
50
+ * Spawn wrangler with stdout captured (for parsing output).
51
+ * stderr is inherited so the user still sees errors.
52
+ */
53
+ function runWranglerCaptured(args) {
54
+ return new Promise((resolve$1) => {
55
+ const chunks = [];
56
+ const child = spawn("npx", [
57
+ "wrangler",
58
+ "--config",
59
+ wranglerConfigPath(),
60
+ ...args
61
+ ], {
62
+ cwd: siloDir(),
63
+ stdio: [
64
+ "inherit",
65
+ "pipe",
66
+ "inherit"
67
+ ],
68
+ shell: process.platform === "win32"
69
+ });
70
+ child.stdout?.on("data", (chunk) => {
71
+ chunks.push(chunk);
72
+ });
73
+ child.on("error", (err) => {
74
+ resolve$1({
75
+ stdout: "",
76
+ code: 1
77
+ });
78
+ });
79
+ child.on("close", (code) => {
80
+ resolve$1({
81
+ stdout: Buffer.concat(chunks).toString("utf8"),
82
+ code: code ?? 1
83
+ });
84
+ });
85
+ });
86
+ }
87
+
88
+ //#endregion
89
+ export { projectRoot, runWrangler, runWranglerCaptured, siloDir };
@@ -0,0 +1,200 @@
1
+ const require_sync = require('../sync.cjs');
2
+
3
+ //#region src/local/adapters/cloudflare.ts
4
+ const INITIAL_BACKOFF = 1e3;
5
+ const MAX_BACKOFF = 3e4;
6
+ const SYNC_RETRY_WINDOW_MS = 3e4;
7
+ const SYNC_MAX_RETRIES = 5;
8
+ var CloudflareSyncAdapter = class extends require_sync.SyncAdapter {
9
+ constructor(options) {
10
+ super({ merge: options.merge });
11
+ this.ws = null;
12
+ this.reconnectTimer = null;
13
+ this.backoff = INITIAL_BACKOFF;
14
+ this.alive = true;
15
+ this.registeredViews = [];
16
+ this.failureCount = 0;
17
+ this.firstFailureAt = 0;
18
+ this.blocked = false;
19
+ this.warned = false;
20
+ this.ignoreClose = false;
21
+ this.pendingCommits = /* @__PURE__ */ new Map();
22
+ this.url = options.url;
23
+ this.getToken = options.getToken;
24
+ this.db = options.db;
25
+ this.subscribers = options.subscribers;
26
+ }
27
+ /** Register the view names this adapter should subscribe to on connect. */
28
+ registerViews(names) {
29
+ this.registeredViews = names;
30
+ }
31
+ async connect() {
32
+ if (!this.alive) return;
33
+ if (this.blocked) return;
34
+ try {
35
+ const wsUrl = this.url.replace(/^https?/, (m) => m === "https" ? "wss" : "ws");
36
+ const token = this.getToken ? await this.getToken() : null;
37
+ if (this.getToken && !token) return;
38
+ const url = token ? `${wsUrl}/sync?token=${encodeURIComponent(token)}` : `${wsUrl}/sync`;
39
+ this.ws = new WebSocket(url);
40
+ this.ws.onopen = () => {
41
+ this.backoff = INITIAL_BACKOFF;
42
+ if (this.registeredViews.length > 0) this.ws.send(JSON.stringify({
43
+ type: "subscribe",
44
+ views: this.registeredViews
45
+ }));
46
+ };
47
+ this.ws.onmessage = async (event) => {
48
+ const msg = JSON.parse(event.data);
49
+ await this.handleMessage(msg);
50
+ };
51
+ this.ws.onclose = () => {
52
+ if (this.ignoreClose) {
53
+ this.ignoreClose = false;
54
+ return;
55
+ }
56
+ if (this.alive) this.scheduleReconnect();
57
+ };
58
+ this.ws.onerror = () => {
59
+ this.ws?.close();
60
+ };
61
+ } catch {
62
+ if (this.alive) this.scheduleReconnect();
63
+ }
64
+ }
65
+ async handleMessage(msg) {
66
+ switch (msg.type) {
67
+ case "hydrate": {
68
+ const ops = (msg.rows ?? []).map((row) => ({
69
+ kind: "add",
70
+ view: { name: msg.view },
71
+ id: row.id,
72
+ value: row.data,
73
+ version: row.version ?? 1,
74
+ timestamp: row.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
75
+ }));
76
+ if (ops.length > 0) await require_sync.applyRemoteOps(this.db, ops, this.subscribers, this.merge);
77
+ break;
78
+ }
79
+ case "push": {
80
+ const ops = msg.ops ?? [];
81
+ if (ops.length > 0) await require_sync.applyRemoteOps(this.db, ops, this.subscribers, this.merge);
82
+ break;
83
+ }
84
+ case "committed": {
85
+ const pending = this.pendingCommits.get(msg.requestId);
86
+ if (pending) {
87
+ pending.resolve();
88
+ this.pendingCommits.delete(msg.requestId);
89
+ }
90
+ break;
91
+ }
92
+ case "rejected": {
93
+ const pending = this.pendingCommits.get(msg.requestId);
94
+ if (pending) {
95
+ pending.reject(msg.reason ?? "Rejected by server");
96
+ this.pendingCommits.delete(msg.requestId);
97
+ }
98
+ break;
99
+ }
100
+ case "error":
101
+ console.error("[CloudflareSyncAdapter] server error:", msg.message);
102
+ break;
103
+ }
104
+ if (msg.type === "push" && this.receiveHandler) await this.receiveHandler({ ops: msg.ops ?? [] });
105
+ }
106
+ scheduleReconnect() {
107
+ this.recordFailure();
108
+ if (this.blocked) return;
109
+ if (this.reconnectTimer) return;
110
+ this.reconnectTimer = setTimeout(() => {
111
+ this.reconnectTimer = null;
112
+ this.backoff = Math.min(this.backoff * 2, MAX_BACKOFF);
113
+ this.connect();
114
+ }, this.backoff);
115
+ }
116
+ recordFailure() {
117
+ const now = Date.now();
118
+ if (!this.firstFailureAt || now - this.firstFailureAt > SYNC_RETRY_WINDOW_MS) {
119
+ this.firstFailureAt = now;
120
+ this.failureCount = 0;
121
+ }
122
+ this.failureCount += 1;
123
+ if (this.failureCount > SYNC_MAX_RETRIES) {
124
+ this.blocked = true;
125
+ if (!this.warned) {
126
+ this.warned = true;
127
+ console.warn("[CloudflareSyncAdapter] Retry budget exceeded, blocking reconnects");
128
+ }
129
+ }
130
+ }
131
+ resetConnection() {
132
+ this.blocked = false;
133
+ this.warned = false;
134
+ this.failureCount = 0;
135
+ this.firstFailureAt = 0;
136
+ this.backoff = INITIAL_BACKOFF;
137
+ if (this.reconnectTimer) {
138
+ clearTimeout(this.reconnectTimer);
139
+ this.reconnectTimer = null;
140
+ }
141
+ if (this.ws) {
142
+ this.ignoreClose = true;
143
+ this.ws.close();
144
+ this.ws = null;
145
+ }
146
+ this.connect();
147
+ }
148
+ async send(payload) {
149
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
150
+ const requestId = Math.random().toString(36).slice(2);
151
+ return new Promise((resolve, reject) => {
152
+ this.pendingCommits.set(requestId, {
153
+ resolve,
154
+ reject
155
+ });
156
+ this.ws.send(JSON.stringify({
157
+ type: "commit",
158
+ ops: payload.ops,
159
+ requestId
160
+ }));
161
+ setTimeout(() => {
162
+ if (this.pendingCommits.has(requestId)) {
163
+ this.pendingCommits.delete(requestId);
164
+ resolve();
165
+ }
166
+ }, 1e4);
167
+ });
168
+ }
169
+ onReceive(handler) {
170
+ this.receiveHandler = handler;
171
+ }
172
+ async fetchInitialState() {
173
+ return null;
174
+ }
175
+ disconnect() {
176
+ this.alive = false;
177
+ if (this.reconnectTimer) {
178
+ clearTimeout(this.reconnectTimer);
179
+ this.reconnectTimer = null;
180
+ }
181
+ this.ws?.close();
182
+ this.ws = null;
183
+ }
184
+ /**
185
+ * Reset backoff and force an immediate reconnect.
186
+ * Used after token changes (e.g. elevation from anonymous to authenticated).
187
+ */
188
+ forceReconnect() {
189
+ if (this.reconnectTimer) {
190
+ clearTimeout(this.reconnectTimer);
191
+ this.reconnectTimer = null;
192
+ }
193
+ this.backoff = INITIAL_BACKOFF;
194
+ if (this.ws) this.ws.close();
195
+ else this.connect();
196
+ }
197
+ };
198
+
199
+ //#endregion
200
+ exports.CloudflareSyncAdapter = CloudflareSyncAdapter;