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.
- package/README.md +3 -1
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/cli/d1.cjs +93 -0
- package/dist/cli/d1.mjs +92 -0
- package/dist/cli/index.cjs +93 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +94 -0
- package/dist/cli/init.cjs +134 -0
- package/dist/cli/init.mjs +133 -0
- package/dist/cli/kv.cjs +63 -0
- package/dist/cli/kv.mjs +60 -0
- package/dist/cli/r2.cjs +83 -0
- package/dist/cli/r2.mjs +82 -0
- package/dist/cli/wrangler.cjs +93 -0
- package/dist/cli/wrangler.mjs +89 -0
- package/dist/local/adapters/cloudflare.cjs +200 -0
- package/dist/local/adapters/cloudflare.d.cts +50 -0
- package/dist/local/adapters/cloudflare.d.mts +50 -0
- package/dist/local/adapters/cloudflare.mjs +200 -0
- package/dist/local/auth-context.cjs +14 -0
- package/dist/local/auth-context.d.cts +7 -0
- package/dist/local/auth-context.d.mts +7 -0
- package/dist/local/auth-context.mjs +12 -0
- package/dist/local/auth.cjs +109 -0
- package/dist/local/auth.d.cts +26 -0
- package/dist/local/auth.d.mts +26 -0
- package/dist/local/auth.mjs +99 -0
- package/dist/local/commit.cjs +350 -0
- package/dist/local/commit.d.cts +59 -0
- package/dist/local/commit.d.mts +59 -0
- package/dist/local/commit.mjs +349 -0
- package/dist/local/config.cjs +17 -0
- package/dist/local/config.mjs +15 -0
- package/dist/local/index.cjs +16 -0
- package/dist/local/index.d.cts +10 -0
- package/dist/local/index.d.mts +10 -0
- package/dist/local/index.mjs +9 -0
- package/dist/local/provider.cjs +204 -0
- package/dist/local/provider.d.cts +25 -0
- package/dist/local/provider.d.mts +25 -0
- package/dist/local/provider.mjs +203 -0
- package/dist/local/query-store.cjs +276 -0
- package/dist/local/query-store.mjs +274 -0
- package/dist/local/storage.cjs +71 -0
- package/dist/local/storage.d.cts +7 -0
- package/dist/local/storage.d.mts +7 -0
- package/dist/local/storage.mjs +68 -0
- package/dist/local/sync.cjs +124 -0
- package/dist/local/sync.d.cts +36 -0
- package/dist/local/sync.d.mts +36 -0
- package/dist/local/sync.mjs +122 -0
- package/dist/local/view.cjs +257 -0
- package/dist/local/view.d.cts +24 -0
- package/dist/local/view.d.mts +24 -0
- package/dist/local/view.mjs +254 -0
- package/dist/package.cjs +11 -0
- package/dist/package.mjs +5 -0
- package/dist/schema/index.cjs +276 -0
- package/dist/schema/index.d.cts +207 -0
- package/dist/schema/index.d.mts +207 -0
- package/dist/schema/index.mjs +265 -0
- package/dist/server/auth.cjs +132 -0
- package/dist/server/auth.d.cts +49 -0
- package/dist/server/auth.d.mts +49 -0
- package/dist/server/auth.mjs +122 -0
- package/dist/server/d1.cjs +120 -0
- package/dist/server/d1.mjs +116 -0
- package/dist/server/do.cjs +132 -0
- package/dist/server/do.d.cts +21 -0
- package/dist/server/do.d.mts +21 -0
- package/dist/server/do.mjs +131 -0
- package/dist/server/index.cjs +355 -0
- package/dist/server/index.d.cts +65 -0
- package/dist/server/index.d.mts +65 -0
- package/dist/server/index.mjs +348 -0
- package/dist/server/protect.cjs +34 -0
- package/dist/server/protect.d.cts +32 -0
- package/dist/server/protect.d.mts +32 -0
- package/dist/server/protect.mjs +33 -0
- package/dist/server/r2.cjs +58 -0
- package/dist/server/r2.d.cts +4 -0
- package/dist/server/r2.d.mts +4 -0
- package/dist/server/r2.mjs +53 -0
- package/package.json +55 -2
package/dist/cli/kv.cjs
ADDED
|
@@ -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;
|
package/dist/cli/kv.mjs
ADDED
|
@@ -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 };
|
package/dist/cli/r2.cjs
ADDED
|
@@ -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;
|
package/dist/cli/r2.mjs
ADDED
|
@@ -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;
|