@lika85456/s3qlite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/cjs/effect.js +18 -0
  2. package/cjs/effect.js.map +1 -0
  3. package/cjs/index.js +40 -0
  4. package/cjs/index.js.map +1 -0
  5. package/cjs/package.json +3 -0
  6. package/cjs/src/batches.js +39 -0
  7. package/cjs/src/batches.js.map +1 -0
  8. package/cjs/src/cdc/apply.js +404 -0
  9. package/cjs/src/cdc/apply.js.map +1 -0
  10. package/cjs/src/cdc/extract.js +38 -0
  11. package/cjs/src/cdc/extract.js.map +1 -0
  12. package/cjs/src/cdc/protobuf.js +135 -0
  13. package/cjs/src/cdc/protobuf.js.map +1 -0
  14. package/cjs/src/cdc/testUtils.js +49 -0
  15. package/cjs/src/cdc/testUtils.js.map +1 -0
  16. package/cjs/src/cdc/truncate.js +7 -0
  17. package/cjs/src/cdc/truncate.js.map +1 -0
  18. package/cjs/src/cdc/types.js +10 -0
  19. package/cjs/src/cdc/types.js.map +1 -0
  20. package/cjs/src/cdc/withoutCDC.js +7 -0
  21. package/cjs/src/cdc/withoutCDC.js.map +1 -0
  22. package/cjs/src/connection.js +128 -0
  23. package/cjs/src/connection.js.map +1 -0
  24. package/cjs/src/contexts.js +11 -0
  25. package/cjs/src/contexts.js.map +1 -0
  26. package/cjs/src/index.js +30 -0
  27. package/cjs/src/index.js.map +1 -0
  28. package/cjs/src/kv/fileKV.js +131 -0
  29. package/cjs/src/kv/fileKV.js.map +1 -0
  30. package/cjs/src/kv/kv.js +8 -0
  31. package/cjs/src/kv/kv.js.map +1 -0
  32. package/cjs/src/kv/memoryKV.js +16 -0
  33. package/cjs/src/kv/memoryKV.js.map +1 -0
  34. package/cjs/src/kv/s3KV.js +283 -0
  35. package/cjs/src/kv/s3KV.js.map +1 -0
  36. package/cjs/src/kv/syncFiles.js +32 -0
  37. package/cjs/src/kv/syncFiles.js.map +1 -0
  38. package/cjs/src/pull.js +101 -0
  39. package/cjs/src/pull.js.map +1 -0
  40. package/cjs/src/push.js +58 -0
  41. package/cjs/src/push.js.map +1 -0
  42. package/cjs/src/storage.js +41 -0
  43. package/cjs/src/storage.js.map +1 -0
  44. package/cjs/src/types.js +3 -0
  45. package/cjs/src/types.js.map +1 -0
  46. package/cjs/src/wrapDatabase.js +80 -0
  47. package/cjs/src/wrapDatabase.js.map +1 -0
  48. package/dts/effect.d.ts +1 -0
  49. package/dts/index.d.ts +16 -0
  50. package/dts/src/batches.d.ts +10 -0
  51. package/dts/src/cdc/apply.d.ts +14 -0
  52. package/dts/src/cdc/extract.d.ts +8 -0
  53. package/dts/src/cdc/protobuf.d.ts +3 -0
  54. package/dts/src/cdc/testUtils.d.ts +19 -0
  55. package/dts/src/cdc/truncate.d.ts +6 -0
  56. package/dts/src/cdc/types.d.ts +35 -0
  57. package/dts/src/cdc/withoutCDC.d.ts +3 -0
  58. package/dts/src/connection.d.ts +5 -0
  59. package/dts/src/contexts.d.ts +22 -0
  60. package/dts/src/index.d.ts +12 -0
  61. package/dts/src/kv/fileKV.d.ts +5 -0
  62. package/dts/src/kv/kv.d.ts +42 -0
  63. package/dts/src/kv/memoryKV.d.ts +5 -0
  64. package/dts/src/kv/s3KV.d.ts +4 -0
  65. package/dts/src/kv/syncFiles.d.ts +4 -0
  66. package/dts/src/pull.d.ts +8 -0
  67. package/dts/src/push.d.ts +4 -0
  68. package/dts/src/storage.d.ts +22 -0
  69. package/dts/src/types.d.ts +38 -0
  70. package/dts/src/wrapDatabase.d.ts +1 -0
  71. package/esm/effect.js +2 -0
  72. package/esm/effect.js.map +1 -0
  73. package/esm/index.js +22 -0
  74. package/esm/index.js.map +1 -0
  75. package/esm/src/batches.js +34 -0
  76. package/esm/src/batches.js.map +1 -0
  77. package/esm/src/cdc/apply.js +398 -0
  78. package/esm/src/cdc/apply.js.map +1 -0
  79. package/esm/src/cdc/extract.js +33 -0
  80. package/esm/src/cdc/extract.js.map +1 -0
  81. package/esm/src/cdc/protobuf.js +127 -0
  82. package/esm/src/cdc/protobuf.js.map +1 -0
  83. package/esm/src/cdc/testUtils.js +42 -0
  84. package/esm/src/cdc/testUtils.js.map +1 -0
  85. package/esm/src/cdc/truncate.js +3 -0
  86. package/esm/src/cdc/truncate.js.map +1 -0
  87. package/esm/src/cdc/types.js +7 -0
  88. package/esm/src/cdc/types.js.map +1 -0
  89. package/esm/src/cdc/withoutCDC.js +3 -0
  90. package/esm/src/cdc/withoutCDC.js.map +1 -0
  91. package/esm/src/connection.js +123 -0
  92. package/esm/src/connection.js.map +1 -0
  93. package/esm/src/contexts.js +6 -0
  94. package/esm/src/contexts.js.map +1 -0
  95. package/esm/src/index.js +12 -0
  96. package/esm/src/index.js.map +1 -0
  97. package/esm/src/kv/fileKV.js +127 -0
  98. package/esm/src/kv/fileKV.js.map +1 -0
  99. package/esm/src/kv/kv.js +4 -0
  100. package/esm/src/kv/kv.js.map +1 -0
  101. package/esm/src/kv/memoryKV.js +12 -0
  102. package/esm/src/kv/memoryKV.js.map +1 -0
  103. package/esm/src/kv/s3KV.js +279 -0
  104. package/esm/src/kv/s3KV.js.map +1 -0
  105. package/esm/src/kv/syncFiles.js +27 -0
  106. package/esm/src/kv/syncFiles.js.map +1 -0
  107. package/esm/src/pull.js +97 -0
  108. package/esm/src/pull.js.map +1 -0
  109. package/esm/src/push.js +54 -0
  110. package/esm/src/push.js.map +1 -0
  111. package/esm/src/storage.js +26 -0
  112. package/esm/src/storage.js.map +1 -0
  113. package/esm/src/types.js +2 -0
  114. package/esm/src/types.js.map +1 -0
  115. package/esm/src/wrapDatabase.js +76 -0
  116. package/esm/src/wrapDatabase.js.map +1 -0
  117. package/package.json +35 -0
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeFileKV = void 0;
4
+ const FileSystem_1 = require("@effect/platform/FileSystem");
5
+ const database_1 = require("@tursodatabase/database");
6
+ const effect_1 = require("effect");
7
+ const node_fs_1 = require("node:fs");
8
+ const promises_1 = require("node:fs/promises");
9
+ const kv_1 = require("./kv");
10
+ // TODO: in the future the etag should be probably cached
11
+ const getEtag = (value) => effect_1.Effect.promise(async () => {
12
+ const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(value).buffer);
13
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
14
+ }).pipe(effect_1.Effect.orDie);
15
+ const makeFileKV = (rootDirectory) => effect_1.Effect.gen(function* () {
16
+ const fs = yield* FileSystem_1.FileSystem;
17
+ const writeBytes = (key, value) => effect_1.Effect.gen(function* () {
18
+ const path = `${rootDirectory}/${key}`;
19
+ const directory = path.split("/").slice(0, -1).join("/");
20
+ yield* fs.makeDirectory(directory, { recursive: true }).pipe(effect_1.Effect.orDie);
21
+ const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
22
+ yield* fs.writeFile(tempPath, value).pipe(effect_1.Effect.orDie);
23
+ yield* fs.rename(tempPath, path).pipe(effect_1.Effect.orDie);
24
+ return { etag: yield* getEtag(value) };
25
+ });
26
+ return {
27
+ get: (key) => effect_1.Effect.gen(function* () {
28
+ yield* effect_1.Effect.logDebug(`fileKV.get(${key})`);
29
+ const path = `${rootDirectory}/${key}`;
30
+ if (!(yield* fs.exists(path).pipe(effect_1.Effect.orDie))) {
31
+ yield* effect_1.Effect.logDebug(`fileKV.get(${key}) => None`);
32
+ return effect_1.Option.none();
33
+ }
34
+ const value = yield* fs.readFile(path).pipe(effect_1.Effect.orDie);
35
+ const etag = yield* getEtag(value);
36
+ yield* effect_1.Effect.logDebug(`fileKV.get(${key}) => Some(${etag})`);
37
+ return effect_1.Option.some({ etag, value });
38
+ }),
39
+ getIfChanged: (key, etag) => effect_1.Effect.gen(function* () {
40
+ yield* effect_1.Effect.logDebug(`fileKV.getIfChanged(${key}, ${etag})`);
41
+ const path = `${rootDirectory}/${key}`;
42
+ if (!(yield* fs.exists(path).pipe(effect_1.Effect.orDie))) {
43
+ yield* effect_1.Effect.logDebug(`fileKV.getIfChanged(${key}) => None`);
44
+ return effect_1.Option.none();
45
+ }
46
+ const value = yield* fs.readFile(path).pipe(effect_1.Effect.orDie);
47
+ const currentEtag = yield* getEtag(value);
48
+ if (currentEtag === etag) {
49
+ yield* effect_1.Effect.logDebug(`fileKV.getIfChanged(${key}) => None (not changed)`);
50
+ return effect_1.Option.none();
51
+ }
52
+ yield* effect_1.Effect.logDebug(`fileKV.getIfChanged(${key}) => Some(${currentEtag})`);
53
+ return effect_1.Option.some({ etag: currentEtag, value });
54
+ }),
55
+ exists: (key) => effect_1.Effect.logDebug(`fileKV.exists(${key})`).pipe(effect_1.Effect.flatMap(() => fs.exists(`${rootDirectory}/${key}`).pipe(effect_1.Effect.orDie)), effect_1.Effect.tap((result) => effect_1.Effect.logDebug(`fileKV.exists(${key}) => ${result}`))),
56
+ set: (key, value) => effect_1.Effect.logDebug(`fileKV.set(${key}, ${value.length} bytes)`).pipe(effect_1.Effect.flatMap(() => writeBytes(key, value)), effect_1.Effect.tap((result) => effect_1.Effect.logDebug(`fileKV.set(${key}) => ${result.etag}`))),
57
+ cas: (key, value, etag) => effect_1.Effect.gen(function* () {
58
+ yield* effect_1.Effect.logDebug(`fileKV.cas(${key}, ${value.length} bytes, ${etag})`);
59
+ const path = `${rootDirectory}/${key}`;
60
+ if (!(yield* fs.exists(path).pipe(effect_1.Effect.orDie))) {
61
+ yield* effect_1.Effect.logDebug(`fileKV.cas(${key}) => ConflictError (missing)`);
62
+ return yield* effect_1.Effect.fail(new kv_1.ConflictError({ key }));
63
+ }
64
+ const existingValue = yield* fs.readFile(path).pipe(effect_1.Effect.orDie);
65
+ if ((yield* getEtag(existingValue)) !== etag) {
66
+ yield* effect_1.Effect.logDebug(`fileKV.cas(${key}) => ConflictError (etag mismatch)`);
67
+ return yield* effect_1.Effect.fail(new kv_1.ConflictError({ key }));
68
+ }
69
+ const result = yield* writeBytes(key, value);
70
+ yield* effect_1.Effect.logDebug(`fileKV.cas(${key}) => ${result.etag}`);
71
+ return result;
72
+ }),
73
+ delete: (key) => effect_1.Effect.logDebug(`fileKV.delete(${key})`).pipe(effect_1.Effect.flatMap(() => fs.remove(`${rootDirectory}/${key}`, { force: true }).pipe(effect_1.Effect.orDie)), effect_1.Effect.tap(() => effect_1.Effect.logDebug(`fileKV.delete(${key}) => void`))),
74
+ readStream: (key) => effect_1.Effect.gen(function* () {
75
+ yield* effect_1.Effect.logDebug(`fileKV.readStream(${key})`);
76
+ const path = `${rootDirectory}/${key}`;
77
+ if (!(yield* fs.exists(path).pipe(effect_1.Effect.orDie))) {
78
+ yield* effect_1.Effect.logDebug(`fileKV.readStream(${key}) => None`);
79
+ return effect_1.Option.none();
80
+ }
81
+ yield* effect_1.Effect.logDebug(`fileKV.readStream(${key}) => Some(stream)`);
82
+ return effect_1.Option.some(fs.stream(path).pipe(effect_1.Stream.orDie));
83
+ }),
84
+ writeStream: (key, content) => effect_1.Effect.gen(function* () {
85
+ yield* effect_1.Effect.logDebug(`fileKV.writeStream(${key}, stream)`);
86
+ const path = `${rootDirectory}/${key}`;
87
+ const directory = path.split("/").slice(0, -1).join("/");
88
+ yield* fs.makeDirectory(directory, { recursive: true }).pipe(effect_1.Effect.orDie);
89
+ const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
90
+ yield* effect_1.Stream.run(content, fs.sink(tempPath)).pipe(effect_1.Effect.orDie);
91
+ yield* fs.rename(tempPath, path).pipe(effect_1.Effect.orDie);
92
+ const value = yield* fs.readFile(path).pipe(effect_1.Effect.orDie);
93
+ const etag = yield* getEtag(value);
94
+ yield* effect_1.Effect.logDebug(`fileKV.writeStream(${key}) => ${etag}`);
95
+ return { etag };
96
+ }),
97
+ clone: (from, to) => effect_1.Effect.gen(function* () {
98
+ yield* effect_1.Effect.logDebug(`fileKV.clone(${from}, ${to})`);
99
+ const fromPath = `${rootDirectory}/${from}`;
100
+ const toPath = `${rootDirectory}/${to}`;
101
+ const directory = toPath.split("/").slice(0, -1).join("/");
102
+ yield* fs.makeDirectory(directory, { recursive: true }).pipe(effect_1.Effect.orDie);
103
+ const cloneAttempt = yield* effect_1.Effect.either(effect_1.Effect.promise(() => (0, promises_1.copyFile)(fromPath, toPath, node_fs_1.constants.COPYFILE_FICLONE)));
104
+ if (cloneAttempt._tag === "Right") {
105
+ yield* effect_1.Effect.logDebug(`fileKV.clone(${from}, ${to}) => void (copied)`);
106
+ return;
107
+ }
108
+ const errorOption = typeof cloneAttempt.left === "object"
109
+ ? effect_1.Option.fromNullable(cloneAttempt.left)
110
+ : effect_1.Option.none();
111
+ const currentError = effect_1.Option.map(errorOption, (error) => error);
112
+ if (effect_1.Option.isNone(currentError) ||
113
+ !("code" in currentError.value) ||
114
+ !["ENOTSUP", "EINVAL", "ENOSYS", "EXDEV", "EOPNOTSUPP"].includes(String(currentError.value.code))) {
115
+ return yield* effect_1.Effect.die(cloneAttempt.left);
116
+ }
117
+ yield* fs.copyFile(fromPath, toPath).pipe(effect_1.Effect.orDie);
118
+ yield* effect_1.Effect.logDebug(`fileKV.clone(${from}, ${to}) => void (copied)`);
119
+ }),
120
+ connect: (key) => effect_1.Effect.logDebug(`fileKV.connect(${key})`).pipe(effect_1.Effect.flatMap(() => effect_1.Effect.acquireRelease(effect_1.Effect.tryPromise({
121
+ try: async () => {
122
+ const connection = await (0, database_1.connect)(`${rootDirectory}/${key}`);
123
+ await connection.exec("PRAGMA capture_data_changes_conn('full')");
124
+ return connection;
125
+ },
126
+ catch: (e) => new Error(`Failed to connect to database at key ${key}: ${String(e)}`),
127
+ }), (db) => effect_1.Effect.promise(() => db.close()))), effect_1.Effect.tap(() => effect_1.Effect.logDebug(`fileKV.connect(${key}) => database`))),
128
+ };
129
+ });
130
+ exports.makeFileKV = makeFileKV;
131
+ //# sourceMappingURL=fileKV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileKV.js","sourceRoot":"","sources":["../../../../src/kv/fileKV.ts"],"names":[],"mappings":"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","sourcesContent":["import { FileSystem } from \"@effect/platform/FileSystem\";\nimport { connect as connectTurso } from \"@tursodatabase/database\";\nimport type { Scope } from \"effect\";\nimport { Effect, Option, Stream } from \"effect\";\nimport { constants } from \"node:fs\";\nimport { copyFile as copyFilePromise } from \"node:fs/promises\";\n\nimport { ConflictError } from \"./kv\";\nimport type { CloneTrait, ConnectDatabaseTrait, KV } from \"./kv\";\n\n// TODO: in the future the etag should be probably cached\nconst getEtag = (value: Uint8Array): Effect.Effect<string> =>\n\tEffect.promise(async () => {\n\t\tconst digest = await crypto.subtle.digest(\"SHA-256\", Uint8Array.from(value).buffer);\n\t\treturn Array.from(new Uint8Array(digest), (byte) =>\n\t\t\tbyte.toString(16).padStart(2, \"0\"),\n\t\t).join(\"\");\n\t}).pipe(Effect.orDie);\n\nexport const makeFileKV = (\n\trootDirectory: string,\n): Effect.Effect<KV & CloneTrait & ConnectDatabaseTrait, never, FileSystem | Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\n\t\tconst writeBytes = (key: string, value: Uint8Array): Effect.Effect<{ etag: string }> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\tconst directory = path.split(\"/\").slice(0, -1).join(\"/\");\n\t\t\t\tyield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.orDie);\n\t\t\t\tconst tempPath = `${path}.${crypto.randomUUID()}.tmp`;\n\t\t\t\tyield* fs.writeFile(tempPath, value).pipe(Effect.orDie);\n\t\t\t\tyield* fs.rename(tempPath, path).pipe(Effect.orDie);\n\t\t\t\treturn { etag: yield* getEtag(value) };\n\t\t\t});\n\n\t\treturn {\n\t\t\tget: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.get(${key})`);\n\n\t\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\t\tif (!(yield* fs.exists(path).pipe(Effect.orDie))) {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.get(${key}) => None`);\n\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = yield* fs.readFile(path).pipe(Effect.orDie);\n\t\t\t\t\tconst etag = yield* getEtag(value);\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.get(${key}) => Some(${etag})`);\n\t\t\t\t\treturn Option.some({ etag, value });\n\t\t\t\t}),\n\t\t\tgetIfChanged: (key, etag) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.getIfChanged(${key}, ${etag})`);\n\n\t\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\t\tif (!(yield* fs.exists(path).pipe(Effect.orDie))) {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.getIfChanged(${key}) => None`);\n\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t}\n\n\t\t\t\t\tconst value = yield* fs.readFile(path).pipe(Effect.orDie);\n\t\t\t\t\tconst currentEtag = yield* getEtag(value);\n\t\t\t\t\tif (currentEtag === etag) {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.getIfChanged(${key}) => None (not changed)`);\n\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.getIfChanged(${key}) => Some(${currentEtag})`);\n\t\t\t\t\treturn Option.some({ etag: currentEtag, value });\n\t\t\t\t}),\n\t\t\texists: (key) =>\n\t\t\t\tEffect.logDebug(`fileKV.exists(${key})`).pipe(\n\t\t\t\t\tEffect.flatMap(() => fs.exists(`${rootDirectory}/${key}`).pipe(Effect.orDie)),\n\t\t\t\t\tEffect.tap((result) => Effect.logDebug(`fileKV.exists(${key}) => ${result}`)),\n\t\t\t\t),\n\t\t\tset: (key, value) =>\n\t\t\t\tEffect.logDebug(`fileKV.set(${key}, ${value.length} bytes)`).pipe(\n\t\t\t\t\tEffect.flatMap(() => writeBytes(key, value)),\n\t\t\t\t\tEffect.tap((result) => Effect.logDebug(`fileKV.set(${key}) => ${result.etag}`)),\n\t\t\t\t),\n\t\t\tcas: (key, value, etag) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.cas(${key}, ${value.length} bytes, ${etag})`);\n\n\t\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\t\tif (!(yield* fs.exists(path).pipe(Effect.orDie))) {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.cas(${key}) => ConflictError (missing)`);\n\t\t\t\t\t\treturn yield* Effect.fail(new ConflictError({ key }));\n\t\t\t\t\t}\n\n\t\t\t\t\tconst existingValue = yield* fs.readFile(path).pipe(Effect.orDie);\n\t\t\t\t\tif ((yield* getEtag(existingValue)) !== etag) {\n\t\t\t\t\t\tyield* Effect.logDebug(\n\t\t\t\t\t\t\t`fileKV.cas(${key}) => ConflictError (etag mismatch)`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn yield* Effect.fail(new ConflictError({ key }));\n\t\t\t\t\t}\n\n\t\t\t\t\tconst result = yield* writeBytes(key, value);\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.cas(${key}) => ${result.etag}`);\n\t\t\t\t\treturn result;\n\t\t\t\t}),\n\t\t\tdelete: (key) =>\n\t\t\t\tEffect.logDebug(`fileKV.delete(${key})`).pipe(\n\t\t\t\t\tEffect.flatMap(() =>\n\t\t\t\t\t\tfs.remove(`${rootDirectory}/${key}`, { force: true }).pipe(Effect.orDie),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.tap(() => Effect.logDebug(`fileKV.delete(${key}) => void`)),\n\t\t\t\t),\n\t\t\treadStream: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.readStream(${key})`);\n\n\t\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\t\tif (!(yield* fs.exists(path).pipe(Effect.orDie))) {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.readStream(${key}) => None`);\n\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.readStream(${key}) => Some(stream)`);\n\t\t\t\t\treturn Option.some(fs.stream(path).pipe(Stream.orDie));\n\t\t\t\t}),\n\t\t\twriteStream: (key, content) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.writeStream(${key}, stream)`);\n\n\t\t\t\t\tconst path = `${rootDirectory}/${key}`;\n\t\t\t\t\tconst directory = path.split(\"/\").slice(0, -1).join(\"/\");\n\t\t\t\t\tyield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.orDie);\n\t\t\t\t\tconst tempPath = `${path}.${crypto.randomUUID()}.tmp`;\n\t\t\t\t\tyield* Stream.run(content, fs.sink(tempPath)).pipe(Effect.orDie);\n\t\t\t\t\tyield* fs.rename(tempPath, path).pipe(Effect.orDie);\n\t\t\t\t\tconst value = yield* fs.readFile(path).pipe(Effect.orDie);\n\t\t\t\t\tconst etag = yield* getEtag(value);\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.writeStream(${key}) => ${etag}`);\n\t\t\t\t\treturn { etag };\n\t\t\t\t}),\n\t\t\tclone: (from, to) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.clone(${from}, ${to})`);\n\n\t\t\t\t\tconst fromPath = `${rootDirectory}/${from}`;\n\t\t\t\t\tconst toPath = `${rootDirectory}/${to}`;\n\t\t\t\t\tconst directory = toPath.split(\"/\").slice(0, -1).join(\"/\");\n\t\t\t\t\tyield* fs.makeDirectory(directory, { recursive: true }).pipe(Effect.orDie);\n\t\t\t\t\tconst cloneAttempt = yield* Effect.either(\n\t\t\t\t\t\tEffect.promise(() =>\n\t\t\t\t\t\t\tcopyFilePromise(fromPath, toPath, constants.COPYFILE_FICLONE),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\tif (cloneAttempt._tag === \"Right\") {\n\t\t\t\t\t\tyield* Effect.logDebug(`fileKV.clone(${from}, ${to}) => void (copied)`);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst errorOption =\n\t\t\t\t\t\ttypeof cloneAttempt.left === \"object\"\n\t\t\t\t\t\t\t? Option.fromNullable(cloneAttempt.left)\n\t\t\t\t\t\t\t: Option.none();\n\t\t\t\t\tconst currentError = Option.map(\n\t\t\t\t\t\terrorOption,\n\t\t\t\t\t\t(error) => error as { code?: unknown },\n\t\t\t\t\t);\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tOption.isNone(currentError) ||\n\t\t\t\t\t\t!(\"code\" in currentError.value) ||\n\t\t\t\t\t\t![\"ENOTSUP\", \"EINVAL\", \"ENOSYS\", \"EXDEV\", \"EOPNOTSUPP\"].includes(\n\t\t\t\t\t\t\tString(currentError.value.code),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn yield* Effect.die(cloneAttempt.left);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* fs.copyFile(fromPath, toPath).pipe(Effect.orDie);\n\t\t\t\t\tyield* Effect.logDebug(`fileKV.clone(${from}, ${to}) => void (copied)`);\n\t\t\t\t}),\n\t\t\tconnect: (key) =>\n\t\t\t\tEffect.logDebug(`fileKV.connect(${key})`).pipe(\n\t\t\t\t\tEffect.flatMap(() =>\n\t\t\t\t\t\tEffect.acquireRelease(\n\t\t\t\t\t\t\tEffect.tryPromise({\n\t\t\t\t\t\t\t\ttry: async () => {\n\t\t\t\t\t\t\t\t\tconst connection = await connectTurso(\n\t\t\t\t\t\t\t\t\t\t`${rootDirectory}/${key}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tawait connection.exec(\n\t\t\t\t\t\t\t\t\t\t\"PRAGMA capture_data_changes_conn('full')\",\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\treturn connection;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tcatch: (e) =>\n\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t`Failed to connect to database at key ${key}: ${String(e)}`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t(db) => Effect.promise(() => db.close()),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.tap(() => Effect.logDebug(`fileKV.connect(${key}) => database`)),\n\t\t\t\t),\n\t\t};\n\t});"]}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConflictError = void 0;
4
+ const effect_1 = require("effect");
5
+ class ConflictError extends effect_1.Data.TaggedError("ConflictError") {
6
+ }
7
+ exports.ConflictError = ConflictError;
8
+ //# sourceMappingURL=kv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv.js","sourceRoot":"","sources":["../../../../src/kv/kv.ts"],"names":[],"mappings":";;;AACA,mCAA8B;AAG9B,MAAa,aAAc,SAAQ,aAAI,CAAC,WAAW,CAAC,eAAe,CAEjE;CAAG;AAFL,sCAEK","sourcesContent":["import type { Database } from \"@tursodatabase/database\";\nimport { Data } from \"effect\";\nimport type { Effect, Option, Scope, Stream } from \"effect\";\n\nexport class ConflictError extends Data.TaggedError(\"ConflictError\")<{\n\treadonly key: string;\n}> {}\n\nexport type KV = {\n\tget(key: string): Effect.Effect<Option.Option<{ value: Uint8Array; etag: string }>>;\n\t/** Optimization for S3 calls, so there isn't head+get, but only one request*/\n\tgetIfChanged(\n\t\tkey: string,\n\t\tetag: string,\n\t): Effect.Effect<Option.Option<{ value: Uint8Array; etag: string }>>;\n\texists(key: string): Effect.Effect<boolean>;\n\tset(key: string, value: Uint8Array): Effect.Effect<{ etag: string }>;\n\tcas(\n\t\tkey: string,\n\t\tvalue: Uint8Array,\n\t\tetag: string,\n\t): Effect.Effect<{ etag: string }, ConflictError>;\n\tdelete(key: string): Effect.Effect<void>;\n\n\treadStream(key: string): Effect.Effect<Option.Option<Stream.Stream<Uint8Array>>>;\n\twriteStream(key: string, content: Stream.Stream<Uint8Array>): Effect.Effect<{ etag: string }>;\n};\n\nexport type CloneTrait = {\n\tclone(from: string, to: string): Effect.Effect<void>;\n};\n\nexport type ConnectDatabaseTrait = {\n\t/**\n\t * Creates connection with CDC full enabled\n\t */\n\tconnect(key: string): Effect.Effect<Database, Error, Scope.Scope>;\n};"]}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeMemoryKV = void 0;
4
+ const FileSystem_1 = require("@effect/platform/FileSystem");
5
+ const effect_1 = require("effect");
6
+ const fileKV_1 = require("./fileKV");
7
+ const makeMemoryKV = () => effect_1.Effect.gen(function* () {
8
+ const fs = yield* FileSystem_1.FileSystem;
9
+ const rootDirectory = `/dev/shm/s3qlite-${crypto.randomUUID()}`;
10
+ yield* effect_1.Effect.logDebug(`memoryKV.makeMemoryKV(${rootDirectory})`);
11
+ yield* fs.makeDirectory(rootDirectory, { recursive: true }).pipe(effect_1.Effect.orDie);
12
+ yield* effect_1.Effect.addFinalizer(() => fs.remove(rootDirectory, { recursive: true, force: true }).pipe(effect_1.Effect.orDie));
13
+ return yield* (0, fileKV_1.makeFileKV)(rootDirectory);
14
+ });
15
+ exports.makeMemoryKV = makeMemoryKV;
16
+ //# sourceMappingURL=memoryKV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryKV.js","sourceRoot":"","sources":["../../../../src/kv/memoryKV.ts"],"names":[],"mappings":";;;AAAA,4DAAyD;AACzD,mCAAgC;AAGhC,qCAAsC;AAG/B,MAAM,YAAY,GAAG,GAI1B,EAAE,CACH,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,uBAAU,CAAC;IAC7B,MAAM,aAAa,GAAG,oBAAoB,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;IAEhE,KAAK,CAAC,CAAC,eAAM,CAAC,QAAQ,CAAC,yBAAyB,aAAa,GAAG,CAAC,CAAC;IAClE,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC;IAC/E,KAAK,CAAC,CAAC,eAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC/B,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,eAAM,CAAC,KAAK,CAAC,CAC7E,CAAC;IAEF,OAAO,KAAK,CAAC,CAAC,IAAA,mBAAU,EAAC,aAAa,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAhBS,QAAA,YAAY,gBAgBrB","sourcesContent":["import { FileSystem } from \"@effect/platform/FileSystem\";\nimport { Effect } from \"effect\";\nimport type { Scope } from \"effect\";\n\nimport { makeFileKV } from \"./fileKV\";\nimport type { CloneTrait, ConnectDatabaseTrait, KV } from \"./kv\";\n\nexport const makeMemoryKV = (): Effect.Effect<\n\tKV & CloneTrait & ConnectDatabaseTrait,\n\tnever,\n\tFileSystem | Scope.Scope\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tconst rootDirectory = `/dev/shm/s3qlite-${crypto.randomUUID()}`;\n\n\t\tyield* Effect.logDebug(`memoryKV.makeMemoryKV(${rootDirectory})`);\n\t\tyield* fs.makeDirectory(rootDirectory, { recursive: true }).pipe(Effect.orDie);\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tfs.remove(rootDirectory, { recursive: true, force: true }).pipe(Effect.orDie),\n\t\t);\n\n\t\treturn yield* makeFileKV(rootDirectory);\n\t});"]}
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeS3KV = void 0;
4
+ const client_s3_1 = require("@effect-aws/client-s3");
5
+ const effect_1 = require("effect");
6
+ const kv_1 = require("./kv");
7
+ const isMissingObjectError = (error) => effect_1.Effect.runSync(effect_1.Effect.sync(() => {
8
+ if (typeof error !== "object") {
9
+ return false;
10
+ }
11
+ const errorOption = effect_1.Option.fromNullable(error);
12
+ if (effect_1.Option.isNone(errorOption)) {
13
+ return false;
14
+ }
15
+ const currentError = errorOption.value;
16
+ return ((typeof currentError.Code === "string" && currentError.Code === "NoSuchKey") ||
17
+ (typeof currentError.name === "string" &&
18
+ (currentError.name === "NoSuchKey" || currentError.name === "NotFound")));
19
+ }));
20
+ const isNotModifiedError = (error) => effect_1.Effect.runSync(effect_1.Effect.sync(() => {
21
+ if (typeof error !== "object") {
22
+ return false;
23
+ }
24
+ const errorOption = effect_1.Option.fromNullable(error);
25
+ if (effect_1.Option.isNone(errorOption)) {
26
+ return false;
27
+ }
28
+ const currentError = errorOption.value;
29
+ const metadata = typeof currentError.$metadata === "object" &&
30
+ effect_1.Option.isSome(effect_1.Option.fromNullable(currentError.$metadata))
31
+ ? effect_1.Option.some(currentError.$metadata)
32
+ : effect_1.Option.none();
33
+ return ((typeof currentError.Code === "string" && currentError.Code === "NotModified") ||
34
+ (typeof currentError.name === "string" && currentError.name === "NotModified") ||
35
+ effect_1.Option.exists(metadata, (currentMetadata) => currentMetadata.httpStatusCode === 304));
36
+ }));
37
+ const getEtag = (result, key) => effect_1.Option.fromNullable(result.ETag).pipe(effect_1.Option.match({
38
+ onNone: () => effect_1.Effect.die(new Error(`Expected ETag for ${key}`)),
39
+ onSome: (etag) => effect_1.Effect.succeed({ etag }),
40
+ }));
41
+ const readBodyBytes = (body, key) => {
42
+ if (body instanceof Uint8Array) {
43
+ return effect_1.Effect.succeed(body);
44
+ }
45
+ if (typeof body === "object") {
46
+ const bodyOption = effect_1.Option.fromNullable(body);
47
+ if (effect_1.Option.isSome(bodyOption) &&
48
+ "transformToByteArray" in bodyOption.value &&
49
+ typeof bodyOption.value.transformToByteArray === "function") {
50
+ const currentBody = bodyOption.value;
51
+ return effect_1.Effect.promise(() => currentBody.transformToByteArray()).pipe(effect_1.Effect.orDie);
52
+ }
53
+ }
54
+ return effect_1.Effect.die(new Error(`Expected readable body for ${key}`));
55
+ };
56
+ const readBodyStream = (body, key) => {
57
+ if (body instanceof Uint8Array) {
58
+ return effect_1.Effect.succeed(effect_1.Stream.fromIterable([body]));
59
+ }
60
+ if (body instanceof ReadableStream) {
61
+ return effect_1.Effect.succeed(effect_1.Stream.fromReadableStream(() => body, () => new Error(`Failed to stream ${key}`)).pipe(effect_1.Stream.orDie));
62
+ }
63
+ if (typeof body === "object") {
64
+ const bodyOption = effect_1.Option.fromNullable(body);
65
+ if (effect_1.Option.isSome(bodyOption) &&
66
+ "transformToWebStream" in bodyOption.value &&
67
+ typeof bodyOption.value.transformToWebStream === "function") {
68
+ const currentBody = bodyOption.value;
69
+ return effect_1.Effect.succeed(effect_1.Stream.fromReadableStream(() => currentBody.transformToWebStream(), () => new Error(`Failed to stream ${key}`)).pipe(effect_1.Stream.orDie));
70
+ }
71
+ }
72
+ if (typeof body === "object") {
73
+ const bodyOption = effect_1.Option.fromNullable(body);
74
+ if (effect_1.Option.isSome(bodyOption) &&
75
+ "transformToByteArray" in bodyOption.value &&
76
+ typeof bodyOption.value.transformToByteArray === "function") {
77
+ return readBodyBytes(bodyOption.value, key).pipe(effect_1.Effect.map((bytes) => effect_1.Stream.fromIterable([bytes])));
78
+ }
79
+ }
80
+ return effect_1.Effect.die(new Error(`Expected streamable body for ${key}`));
81
+ };
82
+ const minimumMultipartPartSize = 5 * 1024 * 1024;
83
+ const combineChunks = (chunks, totalLength) => {
84
+ const combined = new Uint8Array(totalLength);
85
+ let offset = 0;
86
+ for (const chunk of chunks) {
87
+ combined.set(chunk, offset);
88
+ offset += chunk.length;
89
+ }
90
+ return combined;
91
+ };
92
+ const makeS3KV = (bucket) => effect_1.Effect.gen(function* () {
93
+ const s3 = yield* client_s3_1.S3;
94
+ return {
95
+ get: (key) => effect_1.Effect.gen(function* () {
96
+ yield* effect_1.Effect.logDebug(`s3KV.get(${key})`);
97
+ const response = yield* effect_1.Effect.either(s3.getObject({ Bucket: bucket, Key: key }));
98
+ if (response._tag === "Left") {
99
+ if (isMissingObjectError(response.left)) {
100
+ yield* effect_1.Effect.logDebug(`s3KV.get(${key}) => None`);
101
+ return effect_1.Option.none();
102
+ }
103
+ return yield* effect_1.Effect.die(response.left instanceof Error
104
+ ? response.left
105
+ : new Error(`Failed to read ${key}: ${String(response.left)}`));
106
+ }
107
+ const { etag } = yield* getEtag(response.right, key);
108
+ const value = yield* readBodyBytes(response.right.Body, key);
109
+ yield* effect_1.Effect.logDebug(`s3KV.get(${key}) => Some(${etag})`);
110
+ return effect_1.Option.some({ etag, value });
111
+ }),
112
+ getIfChanged: (key, etag) => effect_1.Effect.gen(function* () {
113
+ yield* effect_1.Effect.logDebug(`s3KV.getIfChanged(${key}, ${etag})`);
114
+ const response = yield* effect_1.Effect.either(s3.getObject({ Bucket: bucket, IfNoneMatch: etag, Key: key }));
115
+ if (response._tag === "Left") {
116
+ if (isMissingObjectError(response.left) ||
117
+ isNotModifiedError(response.left)) {
118
+ yield* effect_1.Effect.logDebug(`s3KV.getIfChanged(${key}) => None`);
119
+ return effect_1.Option.none();
120
+ }
121
+ return yield* effect_1.Effect.die(response.left instanceof Error
122
+ ? response.left
123
+ : new Error(`Failed to read ${key}: ${String(response.left)}`));
124
+ }
125
+ const { etag: currentEtag } = yield* getEtag(response.right, key);
126
+ const value = yield* readBodyBytes(response.right.Body, key);
127
+ yield* effect_1.Effect.logDebug(`s3KV.getIfChanged(${key}) => Some(${currentEtag})`);
128
+ return effect_1.Option.some({ etag: currentEtag, value });
129
+ }),
130
+ exists: (key) => effect_1.Effect.gen(function* () {
131
+ yield* effect_1.Effect.logDebug(`s3KV.exists(${key})`);
132
+ const response = yield* effect_1.Effect.either(s3.headObject({ Bucket: bucket, Key: key }));
133
+ if (response._tag === "Left") {
134
+ if (isMissingObjectError(response.left)) {
135
+ yield* effect_1.Effect.logDebug(`s3KV.exists(${key}) => false`);
136
+ return false;
137
+ }
138
+ return yield* effect_1.Effect.die(response.left instanceof Error
139
+ ? response.left
140
+ : new Error(`Failed to stat ${key}: ${String(response.left)}`));
141
+ }
142
+ yield* effect_1.Effect.logDebug(`s3KV.exists(${key}) => true`);
143
+ return true;
144
+ }),
145
+ set: (key, value) => effect_1.Effect.logDebug(`s3KV.set(${key}, ${value.length} bytes)`).pipe(effect_1.Effect.flatMap(() => s3.putObject({ Body: value, Bucket: bucket, Key: key })), effect_1.Effect.orDieWith((error) => error instanceof Error
146
+ ? error
147
+ : new Error(`Failed to write ${key}: ${String(error)}`)), effect_1.Effect.flatMap((result) => getEtag(result, key)), effect_1.Effect.tap((result) => effect_1.Effect.logDebug(`s3KV.set(${key}) => ${result.etag}`))),
148
+ cas: (key, value, etag) => effect_1.Effect.logDebug(`s3KV.cas(${key}, ${value.length} bytes, ${etag})`).pipe(effect_1.Effect.flatMap(() => s3.putObject({ Body: value, Bucket: bucket, IfMatch: etag, Key: key })), effect_1.Effect.catchAll((error) => {
149
+ const currentError = typeof error === "object" && effect_1.Option.isSome(effect_1.Option.fromNullable(error))
150
+ ? effect_1.Option.some(error)
151
+ : effect_1.Option.none();
152
+ const metadata = effect_1.Option.isSome(currentError) &&
153
+ typeof currentError.value.$metadata === "object" &&
154
+ effect_1.Option.isSome(effect_1.Option.fromNullable(currentError.value.$metadata))
155
+ ? effect_1.Option.some(currentError.value.$metadata)
156
+ : effect_1.Option.none();
157
+ return effect_1.Option.isSome(currentError) &&
158
+ ((typeof currentError.value.Code === "string" &&
159
+ currentError.value.Code === "PreconditionFailed") ||
160
+ (typeof currentError.value.name === "string" &&
161
+ currentError.value.name === "PreconditionFailed")) &&
162
+ effect_1.Option.exists(metadata, (currentMetadata) => currentMetadata.httpStatusCode === 412)
163
+ ? effect_1.Effect.fail(new kv_1.ConflictError({ key }))
164
+ : effect_1.Effect.die(error instanceof Error
165
+ ? error
166
+ : new Error(`Failed to write ${key}: ${String(error)}`));
167
+ }), effect_1.Effect.flatMap((result) => getEtag(result, key)), effect_1.Effect.tap((result) => effect_1.Effect.logDebug(`s3KV.cas(${key}) => ${result.etag}`))),
168
+ delete: (key) => effect_1.Effect.logDebug(`s3KV.delete(${key})`).pipe(effect_1.Effect.flatMap(() => s3.deleteObject({ Bucket: bucket, Key: key })), effect_1.Effect.asVoid, effect_1.Effect.orDieWith((error) => error instanceof Error
169
+ ? error
170
+ : new Error(`Failed to delete ${key}: ${String(error)}`)), effect_1.Effect.tap(() => effect_1.Effect.logDebug(`s3KV.delete(${key}) => void`))),
171
+ readStream: (key) => effect_1.Effect.gen(function* () {
172
+ yield* effect_1.Effect.logDebug(`s3KV.readStream(${key})`);
173
+ const response = yield* effect_1.Effect.either(s3.getObject({ Bucket: bucket, Key: key }));
174
+ if (response._tag === "Left") {
175
+ if (isMissingObjectError(response.left)) {
176
+ yield* effect_1.Effect.logDebug(`s3KV.readStream(${key}) => None`);
177
+ return effect_1.Option.none();
178
+ }
179
+ return yield* effect_1.Effect.die(response.left instanceof Error
180
+ ? response.left
181
+ : new Error(`Failed to stream ${key}: ${String(response.left)}`));
182
+ }
183
+ const stream = yield* readBodyStream(response.right.Body, key);
184
+ yield* effect_1.Effect.logDebug(`s3KV.readStream(${key}) => Some(stream)`);
185
+ return effect_1.Option.some(stream);
186
+ }),
187
+ writeStream: (key, content) => effect_1.Effect.gen(function* () {
188
+ yield* effect_1.Effect.logDebug(`s3KV.writeStream(${key}, stream)`);
189
+ const multipartUpload = yield* s3
190
+ .createMultipartUpload({ Bucket: bucket, Key: key })
191
+ .pipe(effect_1.Effect.orDieWith((error) => error instanceof Error
192
+ ? error
193
+ : new Error(`Failed to start upload ${key}: ${String(error)}`)));
194
+ const uploadIdOption = effect_1.Option.fromNullable(multipartUpload.UploadId);
195
+ if (effect_1.Option.isNone(uploadIdOption)) {
196
+ return yield* effect_1.Effect.die(new Error(`Expected UploadId for ${key}`));
197
+ }
198
+ const uploadId = uploadIdOption.value;
199
+ const uploadParts = effect_1.Effect.scoped(effect_1.Effect.gen(function* () {
200
+ const pull = yield* effect_1.Stream.toPull(content);
201
+ const completedParts = [];
202
+ let bufferedChunks = [];
203
+ let bufferedLength = 0;
204
+ let partNumber = 1;
205
+ const uploadBufferedPart = (force) => {
206
+ if (bufferedLength === 0 ||
207
+ (!force && bufferedLength < minimumMultipartPartSize)) {
208
+ return effect_1.Effect.void;
209
+ }
210
+ const body = combineChunks(bufferedChunks, bufferedLength);
211
+ bufferedChunks = [];
212
+ bufferedLength = 0;
213
+ return s3
214
+ .uploadPart({
215
+ Body: body,
216
+ Bucket: bucket,
217
+ ContentLength: body.length,
218
+ Key: key,
219
+ PartNumber: partNumber,
220
+ UploadId: uploadId,
221
+ })
222
+ .pipe(effect_1.Effect.orDieWith((error) => error instanceof Error
223
+ ? error
224
+ : new Error(`Failed to upload ${key} part ${partNumber}: ${String(error)}`)), effect_1.Effect.flatMap((result) => effect_1.Option.fromNullable(result.ETag).pipe(effect_1.Option.match({
225
+ onNone: () => effect_1.Effect.die(new Error(`Expected ETag for ${key} part ${partNumber}`)),
226
+ onSome: (etag) => effect_1.Effect.sync(() => {
227
+ completedParts.push({
228
+ ETag: etag,
229
+ PartNumber: partNumber,
230
+ });
231
+ partNumber += 1;
232
+ }),
233
+ }))));
234
+ };
235
+ while (true) {
236
+ const nextChunk = yield* effect_1.Effect.either(pull);
237
+ if (nextChunk._tag === "Left") {
238
+ return yield* uploadBufferedPart(true).pipe(effect_1.Effect.as(completedParts));
239
+ }
240
+ for (const chunk of effect_1.Chunk.toReadonlyArray(nextChunk.right)) {
241
+ bufferedChunks.push(chunk);
242
+ bufferedLength += chunk.length;
243
+ if (bufferedLength >= minimumMultipartPartSize) {
244
+ yield* uploadBufferedPart(false);
245
+ }
246
+ }
247
+ }
248
+ })).pipe(effect_1.Effect.catchAllCause((cause) => s3
249
+ .abortMultipartUpload({
250
+ Bucket: bucket,
251
+ Key: key,
252
+ UploadId: uploadId,
253
+ })
254
+ .pipe(effect_1.Effect.orDie, effect_1.Effect.catchAll(() => effect_1.Effect.void), effect_1.Effect.zipRight(effect_1.Effect.failCause(cause)))));
255
+ const completedParts = yield* uploadParts;
256
+ if (completedParts.length === 0) {
257
+ const emptyResult = yield* s3
258
+ .putObject({ Body: new Uint8Array(), Bucket: bucket, Key: key })
259
+ .pipe(effect_1.Effect.orDieWith((error) => error instanceof Error
260
+ ? error
261
+ : new Error(`Failed to upload ${key}: ${String(error)}`)));
262
+ const emptyEtagResult = yield* getEtag(emptyResult, key);
263
+ yield* effect_1.Effect.logDebug(`s3KV.writeStream(${key}) => ${emptyEtagResult.etag}`);
264
+ return emptyEtagResult;
265
+ }
266
+ const result = yield* s3
267
+ .completeMultipartUpload({
268
+ Bucket: bucket,
269
+ Key: key,
270
+ MultipartUpload: { Parts: completedParts },
271
+ UploadId: uploadId,
272
+ })
273
+ .pipe(effect_1.Effect.orDieWith((error) => error instanceof Error
274
+ ? error
275
+ : new Error(`Failed to upload ${key}: ${String(error)}`)));
276
+ const finalResult = yield* getEtag(result, key);
277
+ yield* effect_1.Effect.logDebug(`s3KV.writeStream(${key}) => ${finalResult.etag}`);
278
+ return finalResult;
279
+ }),
280
+ };
281
+ });
282
+ exports.makeS3KV = makeS3KV;
283
+ //# sourceMappingURL=s3KV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3KV.js","sourceRoot":"","sources":["../../../../src/kv/s3KV.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3 } from \"@effect-aws/client-s3\";\nimport type { S3Service } from \"@effect-aws/client-s3\";\nimport { Chunk, Effect, Option, Stream } from \"effect\";\n\nimport { ConflictError } from \"./kv\";\nimport type { KV } from \"./kv\";\n\nconst isMissingObjectError = (error: unknown): boolean =>\n\tEffect.runSync(\n\t\tEffect.sync(() => {\n\t\t\tif (typeof error !== \"object\") {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst errorOption = Option.fromNullable(error);\n\t\t\tif (Option.isNone(errorOption)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst currentError = errorOption.value as { Code?: unknown; name?: unknown };\n\t\t\treturn (\n\t\t\t\t(typeof currentError.Code === \"string\" && currentError.Code === \"NoSuchKey\") ||\n\t\t\t\t(typeof currentError.name === \"string\" &&\n\t\t\t\t\t(currentError.name === \"NoSuchKey\" || currentError.name === \"NotFound\"))\n\t\t\t);\n\t\t}),\n\t);\n\nconst isNotModifiedError = (error: unknown): boolean =>\n\tEffect.runSync(\n\t\tEffect.sync(() => {\n\t\t\tif (typeof error !== \"object\") {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst errorOption = Option.fromNullable(error);\n\t\t\tif (Option.isNone(errorOption)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst currentError = errorOption.value as {\n\t\t\t\tCode?: unknown;\n\t\t\t\tname?: unknown;\n\t\t\t\t$metadata?: unknown;\n\t\t\t};\n\t\t\tconst metadata =\n\t\t\t\ttypeof currentError.$metadata === \"object\" &&\n\t\t\t\tOption.isSome(Option.fromNullable(currentError.$metadata))\n\t\t\t\t\t? Option.some(currentError.$metadata as { httpStatusCode?: number })\n\t\t\t\t\t: Option.none<{ httpStatusCode?: number }>();\n\n\t\t\treturn (\n\t\t\t\t(typeof currentError.Code === \"string\" && currentError.Code === \"NotModified\") ||\n\t\t\t\t(typeof currentError.name === \"string\" && currentError.name === \"NotModified\") ||\n\t\t\t\tOption.exists(metadata, (currentMetadata) => currentMetadata.httpStatusCode === 304)\n\t\t\t);\n\t\t}),\n\t);\n\nconst getEtag = (result: { ETag?: string }, key: string): Effect.Effect<{ etag: string }> =>\n\tOption.fromNullable(result.ETag).pipe(\n\t\tOption.match({\n\t\t\tonNone: () => Effect.die(new Error(`Expected ETag for ${key}`)),\n\t\t\tonSome: (etag) => Effect.succeed({ etag }),\n\t\t}),\n\t);\n\nconst readBodyBytes = (body: unknown, key: string): Effect.Effect<Uint8Array> => {\n\tif (body instanceof Uint8Array) {\n\t\treturn Effect.succeed(body);\n\t}\n\n\tif (typeof body === \"object\") {\n\t\tconst bodyOption = Option.fromNullable(body);\n\t\tif (\n\t\t\tOption.isSome(bodyOption) &&\n\t\t\t\"transformToByteArray\" in bodyOption.value &&\n\t\t\ttypeof bodyOption.value.transformToByteArray === \"function\"\n\t\t) {\n\t\t\tconst currentBody = bodyOption.value as {\n\t\t\t\ttransformToByteArray: () => Promise<Uint8Array>;\n\t\t\t};\n\t\t\treturn Effect.promise(() => currentBody.transformToByteArray()).pipe(Effect.orDie);\n\t\t}\n\t}\n\n\treturn Effect.die(new Error(`Expected readable body for ${key}`));\n};\n\nconst readBodyStream = (body: unknown, key: string): Effect.Effect<Stream.Stream<Uint8Array>> => {\n\tif (body instanceof Uint8Array) {\n\t\treturn Effect.succeed(Stream.fromIterable([body]));\n\t}\n\n\tif (body instanceof ReadableStream) {\n\t\treturn Effect.succeed(\n\t\t\tStream.fromReadableStream<Uint8Array, Error>(\n\t\t\t\t() => body as ReadableStream<Uint8Array>,\n\t\t\t\t() => new Error(`Failed to stream ${key}`),\n\t\t\t).pipe(Stream.orDie),\n\t\t);\n\t}\n\n\tif (typeof body === \"object\") {\n\t\tconst bodyOption = Option.fromNullable(body);\n\t\tif (\n\t\t\tOption.isSome(bodyOption) &&\n\t\t\t\"transformToWebStream\" in bodyOption.value &&\n\t\t\ttypeof bodyOption.value.transformToWebStream === \"function\"\n\t\t) {\n\t\t\tconst currentBody = bodyOption.value as {\n\t\t\t\ttransformToWebStream: () => ReadableStream<Uint8Array>;\n\t\t\t};\n\t\t\treturn Effect.succeed(\n\t\t\t\tStream.fromReadableStream<Uint8Array, Error>(\n\t\t\t\t\t() => currentBody.transformToWebStream(),\n\t\t\t\t\t() => new Error(`Failed to stream ${key}`),\n\t\t\t\t).pipe(Stream.orDie),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (typeof body === \"object\") {\n\t\tconst bodyOption = Option.fromNullable(body);\n\t\tif (\n\t\t\tOption.isSome(bodyOption) &&\n\t\t\t\"transformToByteArray\" in bodyOption.value &&\n\t\t\ttypeof bodyOption.value.transformToByteArray === \"function\"\n\t\t) {\n\t\t\treturn readBodyBytes(bodyOption.value, key).pipe(\n\t\t\t\tEffect.map((bytes) => Stream.fromIterable([bytes])),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn Effect.die(new Error(`Expected streamable body for ${key}`));\n};\n\nconst minimumMultipartPartSize = 5 * 1024 * 1024;\n\nconst combineChunks = (chunks: readonly Uint8Array[], totalLength: number): Uint8Array => {\n\tconst combined = new Uint8Array(totalLength);\n\tlet offset = 0;\n\n\tfor (const chunk of chunks) {\n\t\tcombined.set(chunk, offset);\n\t\toffset += chunk.length;\n\t}\n\n\treturn combined;\n};\n\nexport const makeS3KV = (bucket: string): Effect.Effect<KV, never, S3Service> =>\n\tEffect.gen(function* () {\n\t\tconst s3 = yield* S3;\n\n\t\treturn {\n\t\t\tget: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.get(${key})`);\n\n\t\t\t\t\tconst response = yield* Effect.either(\n\t\t\t\t\t\ts3.getObject({ Bucket: bucket, Key: key }),\n\t\t\t\t\t);\n\t\t\t\t\tif (response._tag === \"Left\") {\n\t\t\t\t\t\tif (isMissingObjectError(response.left)) {\n\t\t\t\t\t\t\tyield* Effect.logDebug(`s3KV.get(${key}) => None`);\n\t\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\t\tresponse.left instanceof Error\n\t\t\t\t\t\t\t\t? response.left\n\t\t\t\t\t\t\t\t: new Error(`Failed to read ${key}: ${String(response.left)}`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { etag } = yield* getEtag(response.right, key);\n\t\t\t\t\tconst value = yield* readBodyBytes(response.right.Body, key);\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.get(${key}) => Some(${etag})`);\n\t\t\t\t\treturn Option.some({ etag, value });\n\t\t\t\t}),\n\t\t\tgetIfChanged: (key, etag) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.getIfChanged(${key}, ${etag})`);\n\n\t\t\t\t\tconst response = yield* Effect.either(\n\t\t\t\t\t\ts3.getObject({ Bucket: bucket, IfNoneMatch: etag, Key: key }),\n\t\t\t\t\t);\n\t\t\t\t\tif (response._tag === \"Left\") {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tisMissingObjectError(response.left) ||\n\t\t\t\t\t\t\tisNotModifiedError(response.left)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tyield* Effect.logDebug(`s3KV.getIfChanged(${key}) => None`);\n\t\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\t\tresponse.left instanceof Error\n\t\t\t\t\t\t\t\t? response.left\n\t\t\t\t\t\t\t\t: new Error(`Failed to read ${key}: ${String(response.left)}`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { etag: currentEtag } = yield* getEtag(response.right, key);\n\t\t\t\t\tconst value = yield* readBodyBytes(response.right.Body, key);\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.getIfChanged(${key}) => Some(${currentEtag})`);\n\t\t\t\t\treturn Option.some({ etag: currentEtag, value });\n\t\t\t\t}),\n\t\t\texists: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.exists(${key})`);\n\n\t\t\t\t\tconst response = yield* Effect.either(\n\t\t\t\t\t\ts3.headObject({ Bucket: bucket, Key: key }),\n\t\t\t\t\t);\n\t\t\t\t\tif (response._tag === \"Left\") {\n\t\t\t\t\t\tif (isMissingObjectError(response.left)) {\n\t\t\t\t\t\t\tyield* Effect.logDebug(`s3KV.exists(${key}) => false`);\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\t\tresponse.left instanceof Error\n\t\t\t\t\t\t\t\t? response.left\n\t\t\t\t\t\t\t\t: new Error(`Failed to stat ${key}: ${String(response.left)}`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.exists(${key}) => true`);\n\t\t\t\t\treturn true;\n\t\t\t\t}),\n\t\t\tset: (key, value) =>\n\t\t\t\tEffect.logDebug(`s3KV.set(${key}, ${value.length} bytes)`).pipe(\n\t\t\t\t\tEffect.flatMap(() => s3.putObject({ Body: value, Bucket: bucket, Key: key })),\n\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t: new Error(`Failed to write ${key}: ${String(error)}`),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.flatMap((result) => getEtag(result, key)),\n\t\t\t\t\tEffect.tap((result) => Effect.logDebug(`s3KV.set(${key}) => ${result.etag}`)),\n\t\t\t\t),\n\t\t\tcas: (key, value, etag) =>\n\t\t\t\tEffect.logDebug(`s3KV.cas(${key}, ${value.length} bytes, ${etag})`).pipe(\n\t\t\t\t\tEffect.flatMap(() =>\n\t\t\t\t\t\ts3.putObject({ Body: value, Bucket: bucket, IfMatch: etag, Key: key }),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catchAll((error) => {\n\t\t\t\t\t\tconst currentError =\n\t\t\t\t\t\t\ttypeof error === \"object\" && Option.isSome(Option.fromNullable(error))\n\t\t\t\t\t\t\t\t? Option.some(\n\t\t\t\t\t\t\t\t\t\terror as {\n\t\t\t\t\t\t\t\t\t\t\tCode?: unknown;\n\t\t\t\t\t\t\t\t\t\t\tname?: unknown;\n\t\t\t\t\t\t\t\t\t\t\t$metadata?: unknown;\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: Option.none<{\n\t\t\t\t\t\t\t\t\t\tCode?: unknown;\n\t\t\t\t\t\t\t\t\t\tname?: unknown;\n\t\t\t\t\t\t\t\t\t\t$metadata?: unknown;\n\t\t\t\t\t\t\t\t\t}>();\n\t\t\t\t\t\tconst metadata =\n\t\t\t\t\t\t\tOption.isSome(currentError) &&\n\t\t\t\t\t\t\ttypeof currentError.value.$metadata === \"object\" &&\n\t\t\t\t\t\t\tOption.isSome(Option.fromNullable(currentError.value.$metadata))\n\t\t\t\t\t\t\t\t? Option.some(\n\t\t\t\t\t\t\t\t\t\tcurrentError.value.$metadata as { httpStatusCode?: number },\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: Option.none<{ httpStatusCode?: number }>();\n\n\t\t\t\t\t\treturn Option.isSome(currentError) &&\n\t\t\t\t\t\t\t((typeof currentError.value.Code === \"string\" &&\n\t\t\t\t\t\t\t\tcurrentError.value.Code === \"PreconditionFailed\") ||\n\t\t\t\t\t\t\t\t(typeof currentError.value.name === \"string\" &&\n\t\t\t\t\t\t\t\t\tcurrentError.value.name === \"PreconditionFailed\")) &&\n\t\t\t\t\t\t\tOption.exists(\n\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\t(currentMetadata) => currentMetadata.httpStatusCode === 412,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t? Effect.fail(new ConflictError({ key }))\n\t\t\t\t\t\t\t: Effect.die(\n\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t\t\t: new Error(`Failed to write ${key}: ${String(error)}`),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t\tEffect.flatMap((result) => getEtag(result, key)),\n\t\t\t\t\tEffect.tap((result) => Effect.logDebug(`s3KV.cas(${key}) => ${result.etag}`)),\n\t\t\t\t),\n\t\t\tdelete: (key) =>\n\t\t\t\tEffect.logDebug(`s3KV.delete(${key})`).pipe(\n\t\t\t\t\tEffect.flatMap(() => s3.deleteObject({ Bucket: bucket, Key: key })),\n\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t: new Error(`Failed to delete ${key}: ${String(error)}`),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.tap(() => Effect.logDebug(`s3KV.delete(${key}) => void`)),\n\t\t\t\t),\n\t\t\treadStream: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.readStream(${key})`);\n\n\t\t\t\t\tconst response = yield* Effect.either(\n\t\t\t\t\t\ts3.getObject({ Bucket: bucket, Key: key }),\n\t\t\t\t\t);\n\t\t\t\t\tif (response._tag === \"Left\") {\n\t\t\t\t\t\tif (isMissingObjectError(response.left)) {\n\t\t\t\t\t\t\tyield* Effect.logDebug(`s3KV.readStream(${key}) => None`);\n\t\t\t\t\t\t\treturn Option.none();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn yield* Effect.die(\n\t\t\t\t\t\t\tresponse.left instanceof Error\n\t\t\t\t\t\t\t\t? response.left\n\t\t\t\t\t\t\t\t: new Error(`Failed to stream ${key}: ${String(response.left)}`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst stream = yield* readBodyStream(response.right.Body, key);\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.readStream(${key}) => Some(stream)`);\n\t\t\t\t\treturn Option.some(stream);\n\t\t\t\t}),\n\t\t\twriteStream: (key, content) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.writeStream(${key}, stream)`);\n\n\t\t\t\t\tconst multipartUpload = yield* s3\n\t\t\t\t\t\t.createMultipartUpload({ Bucket: bucket, Key: key })\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t\t: new Error(`Failed to start upload ${key}: ${String(error)}`),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\tconst uploadIdOption = Option.fromNullable(multipartUpload.UploadId);\n\t\t\t\t\tif (Option.isNone(uploadIdOption)) {\n\t\t\t\t\t\treturn yield* Effect.die(new Error(`Expected UploadId for ${key}`));\n\t\t\t\t\t}\n\n\t\t\t\t\tconst uploadId = uploadIdOption.value;\n\t\t\t\t\tconst uploadParts = Effect.scoped(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst pull = yield* Stream.toPull(content);\n\t\t\t\t\t\t\tconst completedParts: { ETag: string; PartNumber: number }[] = [];\n\t\t\t\t\t\t\tlet bufferedChunks: Uint8Array[] = [];\n\t\t\t\t\t\t\tlet bufferedLength = 0;\n\t\t\t\t\t\t\tlet partNumber = 1;\n\n\t\t\t\t\t\t\tconst uploadBufferedPart = (force: boolean): Effect.Effect<void> => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tbufferedLength === 0 ||\n\t\t\t\t\t\t\t\t\t(!force && bufferedLength < minimumMultipartPartSize)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn Effect.void;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst body = combineChunks(bufferedChunks, bufferedLength);\n\t\t\t\t\t\t\t\tbufferedChunks = [];\n\t\t\t\t\t\t\t\tbufferedLength = 0;\n\n\t\t\t\t\t\t\t\treturn s3\n\t\t\t\t\t\t\t\t\t.uploadPart({\n\t\t\t\t\t\t\t\t\t\tBody: body,\n\t\t\t\t\t\t\t\t\t\tBucket: bucket,\n\t\t\t\t\t\t\t\t\t\tContentLength: body.length,\n\t\t\t\t\t\t\t\t\t\tKey: key,\n\t\t\t\t\t\t\t\t\t\tPartNumber: partNumber,\n\t\t\t\t\t\t\t\t\t\tUploadId: uploadId,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t\t\t\t\t: new Error(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t`Failed to upload ${key} part ${partNumber}: ${String(error)}`,\n\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\tEffect.flatMap((result) =>\n\t\t\t\t\t\t\t\t\t\t\tOption.fromNullable(result.ETag).pipe(\n\t\t\t\t\t\t\t\t\t\t\t\tOption.match({\n\t\t\t\t\t\t\t\t\t\t\t\t\tonNone: () =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.die(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t`Expected ETag for ${key} part ${partNumber}`,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t\tonSome: (etag) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcompletedParts.push({\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tETag: etag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPartNumber: partNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartNumber += 1;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\t\tconst nextChunk = yield* Effect.either(pull);\n\n\t\t\t\t\t\t\t\tif (nextChunk._tag === \"Left\") {\n\t\t\t\t\t\t\t\t\treturn yield* uploadBufferedPart(true).pipe(\n\t\t\t\t\t\t\t\t\t\tEffect.as(completedParts),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tfor (const chunk of Chunk.toReadonlyArray(nextChunk.right)) {\n\t\t\t\t\t\t\t\t\tbufferedChunks.push(chunk);\n\t\t\t\t\t\t\t\t\tbufferedLength += chunk.length;\n\t\t\t\t\t\t\t\t\tif (bufferedLength >= minimumMultipartPartSize) {\n\t\t\t\t\t\t\t\t\t\tyield* uploadBufferedPart(false);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t).pipe(\n\t\t\t\t\t\tEffect.catchAllCause((cause) =>\n\t\t\t\t\t\t\ts3\n\t\t\t\t\t\t\t\t.abortMultipartUpload({\n\t\t\t\t\t\t\t\t\tBucket: bucket,\n\t\t\t\t\t\t\t\t\tKey: key,\n\t\t\t\t\t\t\t\t\tUploadId: uploadId,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\tEffect.orDie,\n\t\t\t\t\t\t\t\t\tEffect.catchAll(() => Effect.void),\n\t\t\t\t\t\t\t\t\tEffect.zipRight(Effect.failCause(cause)),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\tconst completedParts = yield* uploadParts;\n\n\t\t\t\t\tif (completedParts.length === 0) {\n\t\t\t\t\t\tconst emptyResult = yield* s3\n\t\t\t\t\t\t\t.putObject({ Body: new Uint8Array(), Bucket: bucket, Key: key })\n\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t\t\t: new Error(`Failed to upload ${key}: ${String(error)}`),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst emptyEtagResult = yield* getEtag(emptyResult, key);\n\t\t\t\t\t\tyield* Effect.logDebug(\n\t\t\t\t\t\t\t`s3KV.writeStream(${key}) => ${emptyEtagResult.etag}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn emptyEtagResult;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst result = yield* s3\n\t\t\t\t\t\t.completeMultipartUpload({\n\t\t\t\t\t\t\tBucket: bucket,\n\t\t\t\t\t\t\tKey: key,\n\t\t\t\t\t\t\tMultipartUpload: { Parts: completedParts },\n\t\t\t\t\t\t\tUploadId: uploadId,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.orDieWith((error) =>\n\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t\t: new Error(`Failed to upload ${key}: ${String(error)}`),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\n\t\t\t\t\tconst finalResult = yield* getEtag(result, key);\n\t\t\t\t\tyield* Effect.logDebug(`s3KV.writeStream(${key}) => ${finalResult.etag}`);\n\t\t\t\t\treturn finalResult;\n\t\t\t\t}),\n\t\t} satisfies KV;\n\t});"]}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pushFiles = exports.pullFiles = void 0;
4
+ const effect_1 = require("effect");
5
+ const storage_1 = require("../storage");
6
+ const pullFiles = (keys) => effect_1.Effect.forEach(keys, (key) => effect_1.Effect.gen(function* () {
7
+ const localKV = yield* storage_1.LocalKV;
8
+ const remoteKV = yield* storage_1.RemoteKV;
9
+ if (yield* localKV.exists(key)) {
10
+ return;
11
+ }
12
+ const readStream = yield* remoteKV.readStream(key);
13
+ if (effect_1.Option.isNone(readStream)) {
14
+ return yield* effect_1.Effect.fail(new Error(`File ${key} does not exist remotely or cannot be read`));
15
+ }
16
+ yield* localKV.writeStream(key, readStream.value);
17
+ }));
18
+ exports.pullFiles = pullFiles;
19
+ const pushFiles = (keys) => effect_1.Effect.forEach(keys, (key) => effect_1.Effect.gen(function* () {
20
+ const localKV = yield* storage_1.LocalKV;
21
+ const remoteKV = yield* storage_1.RemoteKV;
22
+ if (yield* remoteKV.exists(key)) {
23
+ return;
24
+ }
25
+ const readStream = yield* localKV.readStream(key);
26
+ if (effect_1.Option.isNone(readStream)) {
27
+ return yield* effect_1.Effect.fail(new Error(`File ${key} does not exist locally or cannot be read`));
28
+ }
29
+ yield* remoteKV.writeStream(key, readStream.value);
30
+ }));
31
+ exports.pushFiles = pushFiles;
32
+ //# sourceMappingURL=syncFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncFiles.js","sourceRoot":"","sources":["../../../../src/kv/syncFiles.ts"],"names":[],"mappings":";;;AAAA,mCAAwC;AAExC,wCAA+C;AAExC,MAAM,SAAS,GAAG,CAAC,IAAc,EAAE,EAAE,CAC3C,eAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAC5B,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAO,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,kBAAQ,CAAC;IAEjC,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO;IACR,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAEnD,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,IAAI,CACxB,IAAI,KAAK,CAAC,QAAQ,GAAG,4CAA4C,CAAC,CAClE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC,CAAC,CACF,CAAC;AApBU,QAAA,SAAS,aAoBnB;AAEI,MAAM,SAAS,GAAG,CAAC,IAAc,EAAE,EAAE,CAC3C,eAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAC5B,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAO,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,kBAAQ,CAAC;IAEjC,IAAI,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO;IACR,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAElD,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,eAAM,CAAC,IAAI,CACxB,IAAI,KAAK,CAAC,QAAQ,GAAG,2CAA2C,CAAC,CACjE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC,CAAC,CACF,CAAC;AApBU,QAAA,SAAS,aAoBnB","sourcesContent":["import { Effect, Option } from \"effect\";\n\nimport { LocalKV, RemoteKV } from \"../storage\";\n\nexport const pullFiles = (keys: string[]) =>\n\tEffect.forEach(keys, (key) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst localKV = yield* LocalKV;\n\t\t\tconst remoteKV = yield* RemoteKV;\n\n\t\t\tif (yield* localKV.exists(key)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst readStream = yield* remoteKV.readStream(key);\n\n\t\t\tif (Option.isNone(readStream)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew Error(`File ${key} does not exist remotely or cannot be read`),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tyield* localKV.writeStream(key, readStream.value);\n\t\t}),\n\t);\n\nexport const pushFiles = (keys: string[]) =>\n\tEffect.forEach(keys, (key) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst localKV = yield* LocalKV;\n\t\t\tconst remoteKV = yield* RemoteKV;\n\n\t\t\tif (yield* remoteKV.exists(key)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst readStream = yield* localKV.readStream(key);\n\n\t\t\tif (Option.isNone(readStream)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew Error(`File ${key} does not exist locally or cannot be read`),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tyield* remoteKV.writeStream(key, readStream.value);\n\t\t}),\n\t);"]}