@hot-updater/cloudflare 1.0.0-rc.5 → 1.0.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.
- package/dist/index.cjs +90 -924
- package/dist/index.d.cts +16 -6
- package/dist/index.d.mts +16 -6
- package/dist/index.mjs +91 -926
- package/dist/init/index.cjs +11777 -0
- package/dist/init/index.d.cts +5 -1
- package/dist/init/index.d.mts +5 -1
- package/dist/init/index.mjs +11771 -1
- package/dist/worker/index.cjs +117 -916
- package/dist/worker/index.d.cts +45 -11
- package/dist/worker/index.d.mts +45 -11
- package/dist/worker/index.mjs +118 -917
- package/package.json +9 -14
- package/sql/bundles.sql +121 -212
- package/src/d1Database.ts +58 -43
- package/src/d1Executor.ts +84 -0
- package/src/d1Schema.ts +26 -0
- package/src/presignR2Url.ts +103 -0
- package/src/r2S3Storage.ts +20 -19
- package/src/r2Storage.ts +5 -3
- package/src/r2WorkerStorage.ts +55 -17
- package/src/worker/d1Database.ts +21 -39
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +9859 -5696
- package/worker/dist/index.js.map +4 -4
- package/worker/migrations/0001_hot-updater_1.0.0.sql +121 -222
- package/worker/src/index.ts +15 -9
- package/worker/wrangler.json +1 -0
- package/dist/iac/index.cjs +0 -12687
- package/dist/iac/index.d.cts +0 -5
- package/dist/iac/index.d.mts +0 -5
- package/dist/iac/index.mjs +0 -12679
- package/src/d1Database.spec.ts +0 -277
- package/src/d1Implementation.spec.ts +0 -342
- package/src/d1Implementation.ts +0 -781
- package/src/d1Query.spec.ts +0 -46
- package/src/d1Query.ts +0 -92
- package/src/d1Rows.spec.ts +0 -143
- package/src/d1Rows.ts +0 -357
- package/src/d1Sql.spec.ts +0 -30
- package/src/d1Sql.ts +0 -175
- package/src/r2Storage.spec.ts +0 -346
- package/src/r2WorkerStorage.spec.ts +0 -184
- package/src/utils/createWrangler.spec.ts +0 -75
- package/src/worker/d1Database.spec.ts +0 -69
- package/worker/src/databaseMigration.integration.spec.ts +0 -191
- package/worker/src/databasePlugins.integration.spec.ts +0 -326
- package/worker/src/index.integration.spec.ts +0 -190
- package/worker/src/insightsMovement.integration.spec.ts +0 -73
- package/worker/src/storagePlugin.integration.spec.ts +0 -59
- package/worker/src/testWorker.ts +0 -5
package/src/d1Schema.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
coreTarget,
|
|
3
|
+
createTableStatements,
|
|
4
|
+
WRITE_GUARD_TABLE,
|
|
5
|
+
generateEngineSql,
|
|
6
|
+
type ToolingTarget,
|
|
7
|
+
} from "@hot-updater/plugin-core";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* D1's schema: the guard table batch writes need, then the shared SQL
|
|
11
|
+
* schema of core and the target's plugins, settings last. Every statement can
|
|
12
|
+
* run again, so a migration for a server's plugin tables repeats core's.
|
|
13
|
+
*/
|
|
14
|
+
export const d1SchemaStatements = ({
|
|
15
|
+
schema,
|
|
16
|
+
settings,
|
|
17
|
+
}: ToolingTarget = coreTarget): string[] => [
|
|
18
|
+
...createTableStatements("sqlite", [WRITE_GUARD_TABLE]),
|
|
19
|
+
...generateEngineSql("sqlite", schema, settings),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/** The schema as a D1 migration, which `wrangler d1 migrations apply` runs. */
|
|
23
|
+
export const d1SchemaSql = (target?: ToolingTarget): string =>
|
|
24
|
+
`-- HotUpdater.schema\n\n${d1SchemaStatements(target)
|
|
25
|
+
.map((statement) => `${statement};`)
|
|
26
|
+
.join("\n\n")}\n`;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
|
|
3
|
+
const hex = (bytes: ArrayBuffer) =>
|
|
4
|
+
Array.from(new Uint8Array(bytes), (byte) =>
|
|
5
|
+
byte.toString(16).padStart(2, "0"),
|
|
6
|
+
).join("");
|
|
7
|
+
|
|
8
|
+
const hmac = async (key: BufferSource, value: string) =>
|
|
9
|
+
crypto.subtle.sign(
|
|
10
|
+
"HMAC",
|
|
11
|
+
await crypto.subtle.importKey(
|
|
12
|
+
"raw",
|
|
13
|
+
key,
|
|
14
|
+
{ hash: "SHA-256", name: "HMAC" },
|
|
15
|
+
false,
|
|
16
|
+
["sign"],
|
|
17
|
+
),
|
|
18
|
+
encoder.encode(value),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/** RFC 3986 encoding, which S3 signs keys and query values in. */
|
|
22
|
+
const encodeRfc3986 = (value: string) =>
|
|
23
|
+
encodeURIComponent(value).replace(
|
|
24
|
+
/[!'()*]/g,
|
|
25
|
+
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
/** R2's S3-compatible credentials. */
|
|
29
|
+
export interface R2Credentials {
|
|
30
|
+
readonly accessKeyId: string;
|
|
31
|
+
readonly secretAccessKey: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A presigned GET URL for an object on R2's S3-compatible API: AWS Signature
|
|
36
|
+
* Version 4 on Web Crypto, in the form `getSignedUrl` from
|
|
37
|
+
* `@aws-sdk/s3-request-presigner` signs, so a Worker presigns without the
|
|
38
|
+
* AWS SDK.
|
|
39
|
+
*/
|
|
40
|
+
export const presignR2GetUrl = async ({
|
|
41
|
+
accountId,
|
|
42
|
+
bucketName,
|
|
43
|
+
credentials,
|
|
44
|
+
expiresInSeconds,
|
|
45
|
+
key,
|
|
46
|
+
signingDate = new Date(),
|
|
47
|
+
}: {
|
|
48
|
+
readonly accountId: string;
|
|
49
|
+
readonly bucketName: string;
|
|
50
|
+
readonly credentials: R2Credentials;
|
|
51
|
+
readonly expiresInSeconds: number;
|
|
52
|
+
/** The object's key, unencoded. */
|
|
53
|
+
readonly key: string;
|
|
54
|
+
readonly signingDate?: Date;
|
|
55
|
+
}): Promise<string> => {
|
|
56
|
+
const host = `${accountId}.r2.cloudflarestorage.com`;
|
|
57
|
+
const path = `/${bucketName}/${key.split("/").map(encodeRfc3986).join("/")}`;
|
|
58
|
+
const amzDate = signingDate.toISOString().replace(/[-:]|\.\d{3}/g, "");
|
|
59
|
+
const scope = `${amzDate.slice(0, 8)}/auto/s3/aws4_request`;
|
|
60
|
+
// Sorted by name, as the canonical request orders them.
|
|
61
|
+
const query: [string, string][] = [
|
|
62
|
+
["X-Amz-Algorithm", "AWS4-HMAC-SHA256"],
|
|
63
|
+
["X-Amz-Content-Sha256", "UNSIGNED-PAYLOAD"],
|
|
64
|
+
["X-Amz-Credential", `${credentials.accessKeyId}/${scope}`],
|
|
65
|
+
["X-Amz-Date", amzDate],
|
|
66
|
+
["X-Amz-Expires", String(expiresInSeconds)],
|
|
67
|
+
["X-Amz-SignedHeaders", "host"],
|
|
68
|
+
["x-id", "GetObject"],
|
|
69
|
+
];
|
|
70
|
+
const queryString = (entries: [string, string][]) =>
|
|
71
|
+
entries
|
|
72
|
+
.map(([name, value]) => `${encodeRfc3986(name)}=${encodeRfc3986(value)}`)
|
|
73
|
+
.join("&");
|
|
74
|
+
const canonicalRequest = [
|
|
75
|
+
"GET",
|
|
76
|
+
path,
|
|
77
|
+
queryString(query),
|
|
78
|
+
`host:${host}\n`,
|
|
79
|
+
"host",
|
|
80
|
+
"UNSIGNED-PAYLOAD",
|
|
81
|
+
].join("\n");
|
|
82
|
+
const stringToSign = [
|
|
83
|
+
"AWS4-HMAC-SHA256",
|
|
84
|
+
amzDate,
|
|
85
|
+
scope,
|
|
86
|
+
hex(
|
|
87
|
+
await crypto.subtle.digest("SHA-256", encoder.encode(canonicalRequest)),
|
|
88
|
+
),
|
|
89
|
+
].join("\n");
|
|
90
|
+
let signingKey: ArrayBuffer = encoder.encode(
|
|
91
|
+
`AWS4${credentials.secretAccessKey}`,
|
|
92
|
+
).buffer as ArrayBuffer;
|
|
93
|
+
for (const part of [amzDate.slice(0, 8), "auto", "s3", "aws4_request"]) {
|
|
94
|
+
signingKey = await hmac(signingKey, part);
|
|
95
|
+
}
|
|
96
|
+
const signature = hex(await hmac(signingKey, stringToSign));
|
|
97
|
+
const signed: [string, string][] = [
|
|
98
|
+
...query.slice(0, 5),
|
|
99
|
+
["X-Amz-Signature", signature],
|
|
100
|
+
...query.slice(5),
|
|
101
|
+
];
|
|
102
|
+
return `https://${host}${path}?${queryString(signed)}`;
|
|
103
|
+
};
|
package/src/r2S3Storage.ts
CHANGED
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
type S3ClientConfig,
|
|
7
7
|
} from "@aws-sdk/client-s3";
|
|
8
8
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
9
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
9
10
|
import {
|
|
10
11
|
createStorageKeyBuilder,
|
|
11
|
-
|
|
12
|
-
createStoragePlugin,
|
|
12
|
+
createStorageAdapter,
|
|
13
13
|
createStorageUri,
|
|
14
14
|
parseStorageUri,
|
|
15
|
-
type
|
|
15
|
+
type StorageAdapterWith,
|
|
16
16
|
} from "@hot-updater/plugin-core";
|
|
17
17
|
|
|
18
18
|
export interface R2S3StorageConfig extends S3ClientConfig {
|
|
@@ -21,10 +21,11 @@ export interface R2S3StorageConfig extends S3ClientConfig {
|
|
|
21
21
|
credentials: NonNullable<S3ClientConfig["credentials"]>;
|
|
22
22
|
/** Base path where bundles will be stored in the bucket. */
|
|
23
23
|
basePath?: string;
|
|
24
|
-
/** Required when this storage serves downloads through createHotUpdater. */
|
|
25
|
-
downloadUrlSigningKey?: string;
|
|
26
24
|
}
|
|
27
25
|
|
|
26
|
+
/** Devices download right after the update check that returns the URL. */
|
|
27
|
+
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
28
|
+
|
|
28
29
|
const isObjectNotFoundError = (error: unknown) => {
|
|
29
30
|
if (
|
|
30
31
|
error instanceof Error &&
|
|
@@ -43,12 +44,13 @@ const isObjectNotFoundError = (error: unknown) => {
|
|
|
43
44
|
|
|
44
45
|
export const createR2S3Storage = (
|
|
45
46
|
config: R2S3StorageConfig,
|
|
46
|
-
):
|
|
47
|
+
): StorageAdapterWith<
|
|
48
|
+
"put" | "get" | "getDownloadUrl" | "exists" | "delete"
|
|
49
|
+
> => {
|
|
47
50
|
const {
|
|
48
51
|
accountId,
|
|
49
52
|
basePath,
|
|
50
53
|
bucketName,
|
|
51
|
-
downloadUrlSigningKey,
|
|
52
54
|
endpoint,
|
|
53
55
|
forcePathStyle,
|
|
54
56
|
region,
|
|
@@ -61,9 +63,6 @@ export const createR2S3Storage = (
|
|
|
61
63
|
region: region ?? "auto",
|
|
62
64
|
});
|
|
63
65
|
const getStorageKey = createStorageKeyBuilder(basePath);
|
|
64
|
-
const getDownloadUrl = downloadUrlSigningKey
|
|
65
|
-
? createStorageDownloadUrl(downloadUrlSigningKey)
|
|
66
|
-
: undefined;
|
|
67
66
|
|
|
68
67
|
const parseAndValidate = (storageUri: string) => {
|
|
69
68
|
const parsed = parseStorageUri(storageUri, "r2");
|
|
@@ -75,7 +74,7 @@ export const createR2S3Storage = (
|
|
|
75
74
|
return parsed;
|
|
76
75
|
};
|
|
77
76
|
|
|
78
|
-
return
|
|
77
|
+
return createStorageAdapter({
|
|
79
78
|
name: "r2Storage",
|
|
80
79
|
protocol: "r2",
|
|
81
80
|
async put({ key, body, contentLength, contentType }) {
|
|
@@ -122,14 +121,16 @@ export const createR2S3Storage = (
|
|
|
122
121
|
throw error;
|
|
123
122
|
}
|
|
124
123
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
async getDownloadUrl({ storageUri }) {
|
|
125
|
+
const { key } = parseAndValidate(storageUri);
|
|
126
|
+
return {
|
|
127
|
+
url: await getSignedUrl(
|
|
128
|
+
client,
|
|
129
|
+
new GetObjectCommand({ Bucket: bucketName, Key: key }),
|
|
130
|
+
{ expiresIn: PRESIGNED_URL_EXPIRES_IN_SECONDS },
|
|
131
|
+
),
|
|
132
|
+
};
|
|
133
|
+
},
|
|
133
134
|
async exists({ storageUri }) {
|
|
134
135
|
const { key } = parseAndValidate(storageUri);
|
|
135
136
|
try {
|
package/src/r2Storage.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StorageAdapterWith } from "@hot-updater/plugin-core";
|
|
2
2
|
|
|
3
3
|
import { createR2S3Storage, type R2S3StorageConfig } from "./r2S3Storage";
|
|
4
4
|
|
|
@@ -7,10 +7,12 @@ export type { R2S3StorageConfig };
|
|
|
7
7
|
|
|
8
8
|
export const r2Storage = (
|
|
9
9
|
config: R2S3StorageConfig,
|
|
10
|
-
):
|
|
10
|
+
): StorageAdapterWith<
|
|
11
|
+
"put" | "get" | "getDownloadUrl" | "exists" | "delete"
|
|
12
|
+
> => {
|
|
11
13
|
if (!config.credentials) {
|
|
12
14
|
throw new Error(
|
|
13
|
-
"r2Storage requires S3-compatible credentials
|
|
15
|
+
"r2Storage requires S3-compatible credentials: pass credentials with an R2 access key ID and secret access key.",
|
|
14
16
|
);
|
|
15
17
|
}
|
|
16
18
|
|
package/src/r2WorkerStorage.ts
CHANGED
|
@@ -1,26 +1,52 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createStorageKeyBuilder,
|
|
3
|
-
|
|
4
|
-
createStoragePlugin,
|
|
3
|
+
createStorageAdapter,
|
|
5
4
|
createStorageUri,
|
|
6
5
|
parseStorageUri,
|
|
7
|
-
type
|
|
6
|
+
type StorageAdapterWith,
|
|
8
7
|
} from "@hot-updater/plugin-core";
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
import { presignR2GetUrl, type R2Credentials } from "./presignR2Url";
|
|
10
|
+
|
|
11
|
+
export type CloudflareWorkerStorageConfig = {
|
|
11
12
|
readonly bucket: R2Bucket;
|
|
12
13
|
readonly bucketName: string;
|
|
13
14
|
readonly basePath?: string;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
} & (
|
|
16
|
+
| {
|
|
17
|
+
/** The account whose R2 endpoint download URLs name. */
|
|
18
|
+
readonly accountId: string;
|
|
19
|
+
/**
|
|
20
|
+
* R2's S3-compatible credentials, which presign download URLs, as
|
|
21
|
+
* `r2Storage` from `@hot-updater/cloudflare` does: what a server needs
|
|
22
|
+
* to serve downloads.
|
|
23
|
+
*/
|
|
24
|
+
readonly credentials: R2Credentials;
|
|
25
|
+
}
|
|
26
|
+
| { readonly accountId?: undefined; readonly credentials?: undefined }
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/** Devices download right after the update check that returns the URL. */
|
|
30
|
+
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Storage on the Worker's R2 binding. With R2's S3-compatible credentials,
|
|
34
|
+
* its download URLs are presigned and devices download from the bucket;
|
|
35
|
+
* without them, it has no `getDownloadUrl`, as a Console that only reads
|
|
36
|
+
* needs.
|
|
37
|
+
*/
|
|
38
|
+
export function r2WorkerStorage(
|
|
39
|
+
config: CloudflareWorkerStorageConfig & {
|
|
40
|
+
readonly credentials: R2Credentials;
|
|
41
|
+
},
|
|
42
|
+
): StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
|
|
43
|
+
export function r2WorkerStorage(
|
|
18
44
|
config: CloudflareWorkerStorageConfig,
|
|
19
|
-
):
|
|
20
|
-
|
|
21
|
-
|
|
45
|
+
): StorageAdapterWith<"put" | "get" | "exists" | "delete">;
|
|
46
|
+
export function r2WorkerStorage(
|
|
47
|
+
config: CloudflareWorkerStorageConfig,
|
|
48
|
+
): StorageAdapterWith<"put" | "get" | "exists" | "delete"> {
|
|
22
49
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
23
|
-
const getDownloadUrl = createStorageDownloadUrl(config.downloadUrlSigningKey);
|
|
24
50
|
|
|
25
51
|
const parseAndValidate = (storageUri: string) => {
|
|
26
52
|
const parsed = parseStorageUri(storageUri, "r2");
|
|
@@ -32,7 +58,7 @@ export const r2WorkerStorage = (
|
|
|
32
58
|
return parsed;
|
|
33
59
|
};
|
|
34
60
|
|
|
35
|
-
return
|
|
61
|
+
return createStorageAdapter({
|
|
36
62
|
name: "r2Storage",
|
|
37
63
|
protocol: "r2",
|
|
38
64
|
async put({ key, body, contentLength, contentType }) {
|
|
@@ -77,10 +103,22 @@ export const r2WorkerStorage = (
|
|
|
77
103
|
headers.set("content-length", String(object.size));
|
|
78
104
|
return { response: new Response(object.body, { headers }) };
|
|
79
105
|
},
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
106
|
+
...(config.credentials === undefined
|
|
107
|
+
? {}
|
|
108
|
+
: {
|
|
109
|
+
async getDownloadUrl({ storageUri }: { storageUri: string }) {
|
|
110
|
+
const { key } = parseAndValidate(storageUri);
|
|
111
|
+
return {
|
|
112
|
+
url: await presignR2GetUrl({
|
|
113
|
+
accountId: config.accountId,
|
|
114
|
+
bucketName: config.bucketName,
|
|
115
|
+
credentials: config.credentials,
|
|
116
|
+
expiresInSeconds: PRESIGNED_URL_EXPIRES_IN_SECONDS,
|
|
117
|
+
key,
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
84
122
|
async exists({ storageUri }) {
|
|
85
123
|
const { key } = parseAndValidate(storageUri);
|
|
86
124
|
return { exists: (await config.bucket.head(key)) !== null };
|
|
@@ -91,4 +129,4 @@ export const r2WorkerStorage = (
|
|
|
91
129
|
return { deleted: true };
|
|
92
130
|
},
|
|
93
131
|
});
|
|
94
|
-
}
|
|
132
|
+
}
|
package/src/worker/d1Database.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createDatabasePluginAdapter } from "@hot-updater/plugin-core/internal";
|
|
1
|
+
import type { EngineDatabase } from "@hot-updater/plugin-core";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
3
|
+
import {
|
|
4
|
+
createD1Database,
|
|
5
|
+
D1ExecutionError,
|
|
6
|
+
type D1ResultLike,
|
|
7
|
+
toSqlResult,
|
|
8
|
+
} from "../d1Executor";
|
|
10
9
|
|
|
11
10
|
type D1BoundStatement = {
|
|
12
|
-
all: () => Promise<
|
|
11
|
+
all: () => Promise<D1ResultLike>;
|
|
13
12
|
};
|
|
14
13
|
|
|
15
14
|
type D1PreparedStatement = {
|
|
@@ -18,43 +17,26 @@ type D1PreparedStatement = {
|
|
|
18
17
|
|
|
19
18
|
export type D1Like = {
|
|
20
19
|
prepare(sql: string): D1PreparedStatement;
|
|
21
|
-
batch(statements: D1BoundStatement[]): Promise<readonly
|
|
20
|
+
batch(statements: D1BoundStatement[]): Promise<readonly D1ResultLike[]>;
|
|
22
21
|
};
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.all();
|
|
35
|
-
if (result.success === false) throw new D1ExecutionError();
|
|
36
|
-
return result.results ?? [];
|
|
37
|
-
},
|
|
23
|
+
/** Hot Updater's database on a D1 binding, inside a Worker. */
|
|
24
|
+
export const d1Database = (database: D1Like): EngineDatabase =>
|
|
25
|
+
createD1Database({
|
|
26
|
+
query: async ({ sql, params }) =>
|
|
27
|
+
toSqlResult(
|
|
28
|
+
await database
|
|
29
|
+
.prepare(sql)
|
|
30
|
+
.bind(...params)
|
|
31
|
+
.all(),
|
|
32
|
+
),
|
|
38
33
|
async batch(statements) {
|
|
39
34
|
const results = await database.batch(
|
|
40
35
|
statements.map(({ sql, params }) =>
|
|
41
36
|
database.prepare(sql).bind(...params),
|
|
42
37
|
),
|
|
43
38
|
);
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
results.some(({ success }) => success === false)
|
|
47
|
-
) {
|
|
48
|
-
throw new D1ExecutionError();
|
|
49
|
-
}
|
|
50
|
-
return results.map(({ results }) => results ?? []);
|
|
39
|
+
if (results.length !== statements.length) throw new D1ExecutionError();
|
|
40
|
+
return results.map(toSqlResult);
|
|
51
41
|
},
|
|
52
42
|
});
|
|
53
|
-
const adapter = createDatabasePluginAdapter("d1Database", implementation);
|
|
54
|
-
return createDatabasePlugin({
|
|
55
|
-
name: "d1Database",
|
|
56
|
-
models: adapter.models,
|
|
57
|
-
commit: adapter.commit,
|
|
58
|
-
...(adapter.dispose ? { dispose: adapter.dispose } : {}),
|
|
59
|
-
});
|
|
60
|
-
};
|
package/worker/dist/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "hot-updater" generated at 2026-
|
|
1
|
+
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-11T01:49:38.431Z.
|