@vankyle/storage-cloudflare 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.
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # @vankyle-hub/storage-cloudflare
2
+
3
+ Cloudflare Workers adapters for `vankyle-storage`:
4
+
5
+ - **`R2BindingStorage`** — `IStorage` using the native Cloudflare R2 Worker binding API.
6
+ - **`D1Dialect`** — Kysely `Dialect` for Cloudflare D1, enabling `KyselyMetadataStore` to work on D1.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pnpm add @vankyle-hub/storage-cloudflare @vankyle-hub/storage-core @vankyle-hub/storage-shared
12
+ ```
13
+
14
+ For metadata on D1, also install:
15
+
16
+ ```bash
17
+ pnpm add @vankyle-hub/storage-kysely kysely
18
+ ```
19
+
20
+ ## R2 Binding Storage
21
+
22
+ Use `R2BindingStorage` when running inside a Cloudflare Worker and you have a direct `R2Bucket` binding (i.e. `env.BUCKET`).
23
+
24
+ > For Cloudflare R2 accessed via the S3-compatible HTTP API (outside of Workers), use [`@vankyle-hub/storage-s3`](../s3/README.md) instead.
25
+
26
+ ### Setup
27
+
28
+ ```typescript
29
+ import { R2BindingStorage } from "@vankyle-hub/storage-cloudflare";
30
+
31
+ interface Env {
32
+ BUCKET: R2Bucket;
33
+ }
34
+
35
+ export default {
36
+ async fetch(request: Request, env: Env): Promise<Response> {
37
+ const storage = new R2BindingStorage(env.BUCKET);
38
+ // ...
39
+ },
40
+ };
41
+ ```
42
+
43
+ ### Capabilities
44
+
45
+ | Capability | Supported |
46
+ |---|---|
47
+ | `multipartUpload` | ✓ |
48
+ | `signedReadUrl` | ✗ |
49
+ | `signedPutUrl` | ✗ |
50
+ | `signedPartUrl` | ✗ |
51
+
52
+ Workers with R2 bindings cannot issue presigned URLs — all data transfers go through the Worker itself. For single-mode uploads, the client must POST to your Worker which calls `service.uploadPart(...)`.
53
+
54
+ ### Multipart upload
55
+
56
+ `R2BindingStorage` uses the native R2 multipart API:
57
+
58
+ | `IStorage` method | R2 Binding API |
59
+ |---|---|
60
+ | `initUploadSession` | `bucket.createMultipartUpload()` |
61
+ | `uploadPart` | `multipart.uploadPart()` |
62
+ | `completeUploadSession` | `multipart.complete()` |
63
+ | `abortUploadSession` | `multipart.abort()` |
64
+
65
+ The `providerUploadId` returned by `initUploadSession` is the R2 `uploadId`.
66
+
67
+ ---
68
+
69
+ ## D1 Kysely Dialect
70
+
71
+ `D1Dialect` adapts Cloudflare D1 to the Kysely query builder interface. This allows `KyselyMetadataStore` from `@vankyle-hub/storage-kysely` to run on D1 without any additional code changes.
72
+
73
+ ### Setup
74
+
75
+ ```typescript
76
+ import { D1Dialect } from "@vankyle-hub/storage-cloudflare";
77
+ import { KyselyMetadataStore } from "@vankyle-hub/storage-kysely";
78
+ import { Kysely } from "kysely";
79
+ import type { StorageDatabase } from "@vankyle-hub/storage-kysely";
80
+
81
+ interface Env {
82
+ DB: D1Database;
83
+ }
84
+
85
+ // Inside your Worker handler:
86
+ const db = new Kysely<StorageDatabase>({
87
+ dialect: new D1Dialect({ database: env.DB }),
88
+ });
89
+
90
+ const metadata = new KyselyMetadataStore(db);
91
+ ```
92
+
93
+ ### Limitations
94
+
95
+ D1 does not support:
96
+ - Traditional transactions (`BEGIN` / `COMMIT` / `ROLLBACK`). Transaction calls in `D1Dialect` are stubbed.
97
+ - Query streaming. Calling `streamQuery` will throw.
98
+
99
+ ---
100
+
101
+ ## Complete Worker example
102
+
103
+ ```typescript
104
+ import { R2BindingStorage, D1Dialect } from "@vankyle-hub/storage-cloudflare";
105
+ import { KyselyMetadataStore } from "@vankyle-hub/storage-kysely";
106
+ import { DefaultStorageService, UploadMode } from "@vankyle-hub/storage-core";
107
+ import { Kysely } from "kysely";
108
+ import type { StorageDatabase } from "@vankyle-hub/storage-kysely";
109
+
110
+ interface Env {
111
+ BUCKET: R2Bucket;
112
+ DB: D1Database;
113
+ }
114
+
115
+ export default {
116
+ async fetch(request: Request, env: Env): Promise<Response> {
117
+ const storage = new R2BindingStorage(env.BUCKET);
118
+ const db = new Kysely<StorageDatabase>({ dialect: new D1Dialect({ database: env.DB }) });
119
+ const metadata = new KyselyMetadataStore(db);
120
+
121
+ const service = new DefaultStorageService({
122
+ storage,
123
+ metadata,
124
+ bucket: "my-bucket",
125
+ });
126
+
127
+ const url = new URL(request.url);
128
+
129
+ if (request.method === "POST" && url.pathname === "/upload/start") {
130
+ const body = await request.json<{ fileName: string }>();
131
+ const { session } = await service.createUploadSession({
132
+ fileName: body.fileName,
133
+ mode: UploadMode.Single,
134
+ });
135
+ return Response.json({ sessionId: session.id });
136
+ }
137
+
138
+ // ... additional routes
139
+ return new Response("Not Found", { status: 404 });
140
+ },
141
+ };
142
+ ```
143
+
144
+ ## D1 Migrations
145
+
146
+ Run migrations from `@vankyle-hub/storage-kysely` using the Kysely `Migrator` with `D1Dialect`. The SQL is compatible with SQLite, which D1 is based on.
147
+
148
+ ```typescript
149
+ import { Migrator } from "kysely";
150
+ import { migrations } from "@vankyle-hub/storage-kysely";
151
+
152
+ const migrator = new Migrator({
153
+ db,
154
+ provider: { getMigrations: async () => migrations },
155
+ });
156
+
157
+ await migrator.migrateToLatest();
158
+ ```
@@ -0,0 +1,10 @@
1
+ import { type DatabaseIntrospector, type Dialect, type DialectAdapter, type Driver, type Kysely, type QueryCompiler } from "kysely";
2
+ export declare class D1Dialect implements Dialect {
3
+ private readonly db;
4
+ constructor(db: D1Database);
5
+ createDriver(): Driver;
6
+ createQueryCompiler(): QueryCompiler;
7
+ createAdapter(): DialectAdapter;
8
+ createIntrospector(db: Kysely<unknown>): DatabaseIntrospector;
9
+ }
10
+ //# sourceMappingURL=d1-kysely-dialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1-kysely-dialect.d.ts","sourceRoot":"","sources":["../../src/d1/d1-kysely-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,aAAa,EAMnB,MAAM,QAAQ,CAAC;AAEhB,qBAAa,SAAU,YAAW,OAAO;IACvC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;gBAEpB,EAAE,EAAE,UAAU;IAI1B,YAAY,IAAI,MAAM;IAItB,mBAAmB,IAAI,aAAa;IAIpC,aAAa,IAAI,cAAc;IAI/B,kBAAkB,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,oBAAoB;CAG9D"}
@@ -0,0 +1,77 @@
1
+ import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, } from "kysely";
2
+ export class D1Dialect {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ createDriver() {
8
+ return new D1Driver(this.db);
9
+ }
10
+ createQueryCompiler() {
11
+ return new SqliteQueryCompiler();
12
+ }
13
+ createAdapter() {
14
+ return new SqliteAdapter();
15
+ }
16
+ createIntrospector(db) {
17
+ return new SqliteIntrospector(db);
18
+ }
19
+ }
20
+ class D1Driver {
21
+ db;
22
+ constructor(db) {
23
+ this.db = db;
24
+ }
25
+ async init() {
26
+ // No initialization needed
27
+ }
28
+ async acquireConnection() {
29
+ return new D1Connection(this.db);
30
+ }
31
+ async beginTransaction(connection) {
32
+ // D1 doesn't support transactions in the traditional sense
33
+ // Batch operations are used instead
34
+ }
35
+ async commitTransaction(connection) {
36
+ // D1 doesn't support transactions
37
+ }
38
+ async rollbackTransaction(connection) {
39
+ // D1 doesn't support transactions
40
+ }
41
+ async releaseConnection(connection) {
42
+ // No-op
43
+ }
44
+ async destroy() {
45
+ // No-op
46
+ }
47
+ }
48
+ class D1Connection {
49
+ db;
50
+ constructor(db) {
51
+ this.db = db;
52
+ }
53
+ async executeQuery(compiledQuery) {
54
+ const { sql, parameters } = compiledQuery;
55
+ const stmt = this.db.prepare(sql).bind(...parameters);
56
+ const result = await stmt.all();
57
+ if (result.error) {
58
+ throw new Error(result.error);
59
+ }
60
+ const rows = (result.results ?? []);
61
+ const numAffectedRows = result.meta.changes > 0
62
+ ? BigInt(result.meta.changes)
63
+ : undefined;
64
+ const insertId = result.meta.last_row_id === null || result.meta.last_row_id === undefined
65
+ ? undefined
66
+ : BigInt(result.meta.last_row_id);
67
+ return {
68
+ rows,
69
+ ...(numAffectedRows !== undefined ? { numAffectedRows } : {}),
70
+ ...(insertId !== undefined ? { insertId } : {}),
71
+ };
72
+ }
73
+ streamQuery() {
74
+ throw new Error("D1 does not support streaming queries");
75
+ }
76
+ }
77
+ //# sourceMappingURL=d1-kysely-dialect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1-kysely-dialect.js","sourceRoot":"","sources":["../../src/d1/d1-kysely-dialect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { D1Dialect } from "./d1-kysely-dialect.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/d1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { D1Dialect } from "./d1-kysely-dialect.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/d1/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { R2BindingStorage } from "./storage/index.js";
2
+ export { D1Dialect } from "./d1/index.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { R2BindingStorage } from "./storage/index.js";
2
+ export { D1Dialect } from "./d1/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { R2BindingStorage } from "./r2-binding-storage.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { R2BindingStorage } from "./r2-binding-storage.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type IStorage, type StorageCapabilities, type PutObjectInput, type PutObjectResult, type GetObjectInput, type GetObjectResult, type HeadObjectInput, type HeadObjectResult, type DeleteObjectInput, type InitUploadSessionInput, type InitUploadSessionResult, type UploadPartInput, type UploadPartResult, type CompleteUploadSessionInput, type CompleteUploadSessionResult, type AbortUploadSessionInput } from "@vankyle-hub/storage-core";
2
+ export declare class R2BindingStorage implements IStorage {
3
+ readonly provider: "r2-binding";
4
+ readonly capabilities: StorageCapabilities;
5
+ private readonly bucket;
6
+ constructor(bucket: R2Bucket);
7
+ putObject(input: PutObjectInput): Promise<PutObjectResult>;
8
+ getObject(input: GetObjectInput): Promise<GetObjectResult>;
9
+ headObject(input: HeadObjectInput): Promise<HeadObjectResult>;
10
+ deleteObject(input: DeleteObjectInput): Promise<void>;
11
+ initUploadSession(input: InitUploadSessionInput): Promise<InitUploadSessionResult>;
12
+ uploadPart(input: UploadPartInput): Promise<UploadPartResult>;
13
+ completeUploadSession(input: CompleteUploadSessionInput): Promise<CompleteUploadSessionResult>;
14
+ abortUploadSession(input: AbortUploadSessionInput): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=r2-binding-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-binding-storage.d.ts","sourceRoot":"","sources":["../../src/storage/r2-binding-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,uBAAuB,EAC7B,MAAM,2BAA2B,CAAC;AAMnC,qBAAa,gBAAiB,YAAW,QAAQ;IAC/C,QAAQ,CAAC,QAAQ,eAA6B;IAC9C,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAKxC;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;gBAEtB,MAAM,EAAE,QAAQ;IAItB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAwB1D,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAgC1D,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgB7D,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD,iBAAiB,CACrB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,uBAAuB,CAAC;IAkB7B,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoB7D,qBAAqB,CACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC;IAkBjC,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;CAQxE"}
@@ -0,0 +1,126 @@
1
+ import { StorageProvider, } from "@vankyle-hub/storage-core";
2
+ import { StorageObjectNotFoundError, } from "@vankyle-hub/storage-shared";
3
+ export class R2BindingStorage {
4
+ provider = StorageProvider.R2Binding;
5
+ capabilities = {
6
+ multipartUpload: true,
7
+ signedReadUrl: false,
8
+ signedPutUrl: false,
9
+ signedPartUrl: false,
10
+ };
11
+ bucket;
12
+ constructor(bucket) {
13
+ this.bucket = bucket;
14
+ }
15
+ async putObject(input) {
16
+ const body = input.body instanceof Uint8Array
17
+ ? input.body.buffer.slice(input.body.byteOffset, input.body.byteOffset + input.body.byteLength)
18
+ : input.body;
19
+ const options = {};
20
+ if (input.contentType) {
21
+ options.httpMetadata = { contentType: input.contentType };
22
+ }
23
+ if (input.sha256) {
24
+ options.sha256 = hexToArrayBuffer(input.sha256);
25
+ }
26
+ if (input.metadata) {
27
+ options.customMetadata = input.metadata;
28
+ }
29
+ const result = await this.bucket.put(input.objectKey, body, options);
30
+ return {
31
+ etag: result?.etag,
32
+ };
33
+ }
34
+ async getObject(input) {
35
+ const options = {};
36
+ if (input.range) {
37
+ if (input.range.end !== undefined) {
38
+ options.range = {
39
+ offset: input.range.start,
40
+ length: input.range.end - input.range.start + 1,
41
+ };
42
+ }
43
+ else {
44
+ options.range = {
45
+ offset: input.range.start,
46
+ };
47
+ }
48
+ }
49
+ const result = await this.bucket.get(input.objectKey, options);
50
+ if (!result) {
51
+ throw new StorageObjectNotFoundError(input.bucket, input.objectKey);
52
+ }
53
+ const r2ObjectBody = result;
54
+ return {
55
+ body: r2ObjectBody.body,
56
+ contentType: result.httpMetadata?.contentType,
57
+ contentLength: result.size,
58
+ etag: result.etag,
59
+ metadata: result.customMetadata,
60
+ };
61
+ }
62
+ async headObject(input) {
63
+ const result = await this.bucket.head(input.objectKey);
64
+ if (!result) {
65
+ throw new StorageObjectNotFoundError(input.bucket, input.objectKey);
66
+ }
67
+ return {
68
+ contentType: result.httpMetadata?.contentType,
69
+ contentLength: result.size,
70
+ etag: result.etag,
71
+ lastModified: result.uploaded,
72
+ metadata: result.customMetadata,
73
+ };
74
+ }
75
+ async deleteObject(input) {
76
+ await this.bucket.delete(input.objectKey);
77
+ }
78
+ async initUploadSession(input) {
79
+ const multipartOptions = {};
80
+ if (input.contentType) {
81
+ multipartOptions.httpMetadata = { contentType: input.contentType };
82
+ }
83
+ if (input.metadata) {
84
+ multipartOptions.customMetadata = input.metadata;
85
+ }
86
+ const multipart = await this.bucket.createMultipartUpload(input.objectKey, multipartOptions);
87
+ return {
88
+ providerUploadId: multipart.uploadId,
89
+ };
90
+ }
91
+ async uploadPart(input) {
92
+ const multipart = this.bucket.resumeMultipartUpload(input.objectKey, input.providerUploadId);
93
+ const body = input.body instanceof Uint8Array
94
+ ? input.body.buffer.slice(input.body.byteOffset, input.body.byteOffset + input.body.byteLength)
95
+ : input.body;
96
+ const part = await multipart.uploadPart(input.partNumber, body);
97
+ return {
98
+ etag: part.etag,
99
+ partNumber: part.partNumber,
100
+ size: input.contentLength,
101
+ };
102
+ }
103
+ async completeUploadSession(input) {
104
+ const multipart = this.bucket.resumeMultipartUpload(input.objectKey, input.providerUploadId);
105
+ const parts = input.parts.map((p) => ({
106
+ partNumber: p.partNumber,
107
+ etag: p.etag ?? "",
108
+ }));
109
+ const result = await multipart.complete(parts);
110
+ return {
111
+ etag: result.etag,
112
+ };
113
+ }
114
+ async abortUploadSession(input) {
115
+ const multipart = this.bucket.resumeMultipartUpload(input.objectKey, input.providerUploadId);
116
+ await multipart.abort();
117
+ }
118
+ }
119
+ function hexToArrayBuffer(hex) {
120
+ const bytes = new Uint8Array(hex.length / 2);
121
+ for (let i = 0; i < hex.length; i += 2) {
122
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
123
+ }
124
+ return bytes.buffer;
125
+ }
126
+ //# sourceMappingURL=r2-binding-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-binding-storage.js","sourceRoot":"","sources":["../../src/storage/r2-binding-storage.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@vankyle/storage-cloudflare",
3
+ "version": "0.1.0",
4
+ "description": "Cloudflare R2 Binding and D1 adapters for vankyle-storage.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc -b tsconfig.json --force",
19
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
20
+ "typecheck": "tsc -p tsconfig.json --noEmit"
21
+ },
22
+ "license": "MPL-2.0",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/Vankyle-Hub/storage-ts.git",
26
+ "directory": "packages/cloudflare"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/Vankyle-Hub/storage-ts/issues"
30
+ },
31
+ "homepage": "https://github.com/Vankyle-Hub/storage-ts",
32
+ "packageManager": "pnpm@10.28.2",
33
+ "dependencies": {
34
+ "@vankyle/storage-core": "0.1.0",
35
+ "@vankyle/storage-shared": "0.1.0",
36
+ "@cloudflare/workers-types": "^4.20260310.1",
37
+ "kysely": "^0.28.2"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }