@nexcraft-dev/r2d1 0.0.0-stage → 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/LICENSE +201 -0
- package/README.md +169 -2
- package/dist/core/codec.d.ts +7 -0
- package/dist/core/codec.js +16 -0
- package/dist/core/codec.js.map +1 -0
- package/dist/core/collection.d.ts +32 -0
- package/dist/core/collection.js +193 -0
- package/dist/core/collection.js.map +1 -0
- package/dist/core/cursor.d.ts +5 -0
- package/dist/core/cursor.js +139 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/errors.d.ts +27 -0
- package/dist/core/errors.js +42 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/model.d.ts +91 -0
- package/dist/core/model.js +54 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/query.d.ts +29 -0
- package/dist/core/query.js +65 -0
- package/dist/core/query.js.map +1 -0
- package/dist/core/schema.d.ts +21 -0
- package/dist/core/schema.js +61 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/node/d1.d.ts +10 -0
- package/dist/node/d1.js +57 -0
- package/dist/node/d1.js.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.js +3 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/r2.d.ts +23 -0
- package/dist/node/r2.js +115 -0
- package/dist/node/r2.js.map +1 -0
- package/dist/storage/d1.d.ts +28 -0
- package/dist/storage/d1.js +301 -0
- package/dist/storage/d1.js.map +1 -0
- package/dist/storage/r2-key.d.ts +4 -0
- package/dist/storage/r2-key.js +36 -0
- package/dist/storage/r2-key.js.map +1 -0
- package/dist/workers/index.d.ts +47 -0
- package/dist/workers/index.js +93 -0
- package/dist/workers/index.js.map +1 -0
- package/examples/common.ts +54 -0
- package/examples/node.ts +36 -0
- package/examples/workers.ts +22 -0
- package/package.json +61 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1.js","sourceRoot":"","sources":["../../src/storage/d1.ts"],"names":[],"mappings":"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","sourcesContent":["import { StorageOperationError } from '../core/errors.js';\nimport { indexValue, key, positive } from '../core/model.js';\nimport type {\n CollectionMetadata,\n DocumentKey,\n IndexEntry,\n IndexQuery,\n IndexStore,\n IndexValue,\n IndexPage,\n IndexType,\n} from '../core/model.js';\nimport { identifier, validateMetadata } from '../core/schema.js';\n\nexport interface SqlStatement {\n readonly sql: string;\n readonly params: readonly (string | number)[];\n}\nexport interface SqlResult {\n readonly rows: readonly Readonly<Record<string, unknown>>[];\n}\nexport interface D1Transport {\n execute(statement: SqlStatement): Promise<SqlResult>;\n}\nconst quote = (name: string): string => `\"${identifier(name)}\"`;\nconst sqlTypes: Record<IndexType, string> = {\n string: 'TEXT',\n long: 'INTEGER',\n double: 'REAL',\n boolean: 'INTEGER',\n};\nconst defaults: Record<IndexType, string> = {\n string: \"''\",\n long: '0',\n double: '0.0',\n boolean: '0',\n};\nfunction fieldType(metadata: CollectionMetadata, name: string): IndexType {\n const field = metadata.fields.find((f) => f.name === name);\n if (!field) throw new TypeError(`field is not indexed: ${name}`);\n return field.type;\n}\nfunction compatible(type: IndexType, value: IndexValue): IndexValue {\n if (!value || value.type !== type) throw new TypeError(`index value type does not match ${type}`);\n return indexValue(type, value.value);\n}\nfunction parameter(params: (string | number)[], value: IndexValue): string {\n if (value.type === 'timestamp') throw new TypeError('D1 does not support timestamp indexes');\n params.push(\n value.type === 'long'\n ? value.value.toString()\n : value.type === 'boolean'\n ? Number(value.value)\n : value.value,\n );\n const placeholder = `?${params.length}`;\n return value.type === 'long' ? `CAST(${placeholder} AS INTEGER)` : placeholder;\n}\nfunction sameCollection(metadata: CollectionMetadata, collection: string): void {\n if (metadata.collection !== collection)\n throw new TypeError('collection does not match initialized metadata');\n}\nexport function compileUpsert(metadata: CollectionMetadata, entry: IndexEntry): SqlStatement {\n sameCollection(metadata, entry.documentKey.collection);\n key(entry.documentKey.collection, entry.documentKey.id);\n for (const field of Object.keys(entry.values)) fieldType(metadata, field);\n const params: (string | number)[] = [entry.documentKey.id];\n const columns = ['\"document_id\"', ...metadata.fields.map((f) => quote(f.name))];\n const placeholders = [\n '?1',\n ...metadata.fields.map((f) => parameter(params, compatible(f.type, entry.values[f.name]!))),\n ];\n const conflict = metadata.fields.length\n ? 'DO UPDATE SET ' +\n metadata.fields.map((f) => `${quote(f.name)} = excluded.${quote(f.name)}`).join(', ')\n : 'DO NOTHING';\n return {\n sql: `INSERT INTO ${quote(metadata.collection)} (${columns.join(', ')}) VALUES (${placeholders.join(', ')}) ON CONFLICT(\"document_id\") ${conflict}`,\n params,\n };\n}\nexport function compileQuery(metadata: CollectionMetadata, query: IndexQuery): SqlStatement {\n sameCollection(metadata, query.collection);\n positive(query.limit, 'limit');\n const params: (string | number)[] = [];\n const conditions = query.filters.map((filter) => {\n const type = fieldType(metadata, filter.field);\n if (!['=', '<>', '>', '>=', '<', '<='].includes(filter.operator))\n throw new TypeError('unsupported comparison');\n if (type === 'boolean' && filter.operator !== '=' && filter.operator !== '<>')\n throw new TypeError('boolean supports only equality comparisons');\n return `${quote(filter.field)} ${filter.operator} ${parameter(params, compatible(type, filter.value))}`;\n });\n const sort = query.sort;\n let sortType: IndexType | undefined;\n if (sort) {\n sortType = fieldType(metadata, sort.field);\n if (sort.direction !== 'ASC' && sort.direction !== 'DESC')\n throw new TypeError('invalid sort direction');\n }\n const direction = sort?.direction ?? 'ASC';\n if (query.cursor) {\n const cursor = query.cursor;\n sameCollection(metadata, cursor.lastDocumentKey.collection);\n key(cursor.lastDocumentKey.collection, cursor.lastDocumentKey.id);\n if (!sort) {\n if (cursor.sortValue !== undefined)\n throw new TypeError('unsorted cursor must not include sort value');\n conditions.push(\n `\"document_id\" > ${parameter(params, { type: 'string', value: cursor.lastDocumentKey.id })}`,\n );\n } else {\n const value = parameter(params, compatible(sortType!, cursor.sortValue!));\n const id = parameter(params, { type: 'string', value: cursor.lastDocumentKey.id });\n const op = direction === 'ASC' ? '>' : '<';\n conditions.push(\n `(${quote(sort.field)} ${op} ${value} OR (${quote(sort.field)} = ${value} AND \"document_id\" ${op} ${id}))`,\n );\n }\n }\n const projection = sort\n ? ', ' +\n (sortType === 'long'\n ? `CAST(${quote(sort.field)} AS TEXT) AS ${quote(sort.field)}`\n : quote(sort.field))\n : '';\n const order = sort\n ? `${quote(metadata.collection)}.${quote(sort.field)} ${direction}, \"document_id\" ${direction}`\n : '\"document_id\" ASC';\n params.push(query.limit + 1);\n return {\n sql: `SELECT \"document_id\"${projection} FROM ${quote(metadata.collection)}${conditions.length ? ' WHERE ' + conditions.join(' AND ') : ''} ORDER BY ${order} LIMIT ?${params.length}`,\n params,\n };\n}\nfunction readValue(type: IndexType, value: unknown): IndexValue {\n try {\n if (type === 'long') {\n if (typeof value !== 'string' || !/^-?\\d+$/.test(value)) throw new TypeError();\n return indexValue(type, BigInt(value));\n }\n if (type === 'boolean') {\n if (value !== 0 && value !== 1) throw new TypeError();\n return indexValue(type, value === 1);\n }\n return indexValue(type, value);\n } catch {\n throw new StorageOperationError('D1 returned an invalid indexed value');\n }\n}\nexport function queryPage(\n metadata: CollectionMetadata,\n query: IndexQuery,\n result: SqlResult,\n): IndexPage {\n const rows = result.rows.slice(0, query.limit);\n const keys = rows.map((row) => {\n try {\n return key(metadata.collection, row['document_id'] as string);\n } catch {\n throw new StorageOperationError('D1 returned an invalid document_id');\n }\n });\n // Validate each returned sort value, including final pages.\n const sortValues = query.sort\n ? rows.map((row) => readValue(fieldType(metadata, query.sort!.field), row[query.sort!.field]))\n : [];\n const hasMore = result.rows.length > query.limit && keys.length > 0;\n return {\n documentKeys: keys,\n ...(hasMore\n ? {\n nextCursor: {\n lastDocumentKey: keys.at(-1)!,\n ...(query.sort ? { sortValue: sortValues.at(-1)! } : {}),\n },\n }\n : {}),\n };\n}\nfunction stringCell(row: Readonly<Record<string, unknown>>, name: string): string {\n const value = row[name];\n if (typeof value !== 'string')\n throw new StorageOperationError(`D1 schema returned invalid ${name}`);\n return value;\n}\nfunction integerCell(row: Readonly<Record<string, unknown>>, name: string): number {\n const value = row[name];\n if (typeof value !== 'number' || !Number.isSafeInteger(value))\n throw new StorageOperationError(`D1 schema returned invalid ${name}`);\n return value;\n}\nasync function initializeSchema(\n transport: D1Transport,\n metadata: CollectionMetadata,\n): Promise<void> {\n const table = quote(metadata.collection);\n const execute = (sql: string): Promise<SqlResult> => transport.execute({ sql, params: [] });\n const inspect = async (): Promise<Map<string, Readonly<Record<string, unknown>>>> => {\n const result = await execute(`PRAGMA table_info(${table})`);\n const columns = new Map<string, Readonly<Record<string, unknown>>>();\n for (const row of result.rows) {\n const name = stringCell(row, 'name');\n if (columns.has(name)) throw new StorageOperationError('D1 returned duplicate columns');\n columns.set(name, row);\n }\n return columns;\n };\n const validate = (\n columns: Map<string, Readonly<Record<string, unknown>>>,\n requireAll: boolean,\n ): void => {\n const id = columns.get('document_id');\n if (\n !id ||\n stringCell(id, 'type').toUpperCase() !== 'TEXT' ||\n integerCell(id, 'notnull') !== 1 ||\n integerCell(id, 'pk') <= 0\n )\n throw new StorageOperationError('D1 schema has incompatible document_id');\n for (const field of metadata.fields) {\n const actual = columns.get(field.name);\n if (!actual && !requireAll) continue;\n if (\n !actual ||\n stringCell(actual, 'type').toUpperCase() !== sqlTypes[field.type] ||\n integerCell(actual, 'notnull') !== 1 ||\n integerCell(actual, 'pk') !== 0\n )\n throw new StorageOperationError(`D1 schema has incompatible column: ${field.name}`);\n }\n };\n let columns = await inspect();\n if (columns.size === 0) {\n const definitions = [\n '\"document_id\" TEXT PRIMARY KEY NOT NULL',\n ...metadata.fields.map((f) => `${quote(f.name)} ${sqlTypes[f.type]} NOT NULL`),\n ];\n await execute(`CREATE TABLE IF NOT EXISTS ${table} (${definitions.join(', ')})`);\n columns = await inspect();\n validate(columns, true);\n } else {\n validate(columns, false);\n const missing = metadata.fields.filter((f) => !columns.has(f.name));\n if (missing.length) {\n const result = await execute(`SELECT EXISTS(SELECT 1 FROM ${table} LIMIT 1) AS \"has_rows\"`);\n if (result.rows.length !== 1)\n throw new StorageOperationError('D1 row-existence query returned invalid result');\n const exists = integerCell(result.rows[0]!, 'has_rows');\n if (exists !== 0 && exists !== 1)\n throw new StorageOperationError('D1 row-existence query returned invalid value');\n if (exists)\n throw new StorageOperationError(\n 'D1 schema requires a rebuild before adding required indexed fields',\n );\n for (const field of missing)\n await execute(\n `ALTER TABLE ${table} ADD COLUMN ${quote(field.name)} ${sqlTypes[field.type]} NOT NULL DEFAULT ${defaults[field.type]}`,\n );\n validate(await inspect(), true);\n }\n }\n for (const field of metadata.fields) {\n const name = `idx_${metadata.collection}_${field.name}`;\n const inspectNames = async (): Promise<string[]> =>\n (await execute(`PRAGMA index_list(${table})`)).rows.map((r) => stringCell(r, 'name'));\n if (!(await inspectNames()).includes(name))\n await execute(`CREATE INDEX IF NOT EXISTS ${quote(name)} ON ${table} (${quote(field.name)})`);\n if (!(await inspectNames()).includes(name))\n throw new StorageOperationError('D1 index name is unavailable');\n const info = await execute(`PRAGMA index_info(${quote(name)})`);\n if (info.rows.length !== 1 || stringCell(info.rows[0]!, 'name') !== field.name)\n throw new StorageOperationError('D1 index is incompatible with indexed field');\n }\n}\n\n/** Shared D1 engine. Transports supply only prepared SQL execution. */\nexport class D1IndexStore implements IndexStore {\n readonly #registrations = new Map<\n string,\n { metadata: CollectionMetadata; ready: Promise<void> }\n >();\n constructor(private readonly transport: D1Transport) {}\n async initialize(input: CollectionMetadata): Promise<void> {\n const metadata = validateMetadata(input);\n const existing = this.#registrations.get(metadata.collection);\n if (existing) {\n if (JSON.stringify(existing.metadata) !== JSON.stringify(metadata))\n throw new TypeError('collection is already initialized with different metadata');\n return existing.ready;\n }\n const ready = Promise.resolve().then(() => initializeSchema(this.transport, metadata));\n this.#registrations.set(metadata.collection, { metadata, ready });\n return ready;\n }\n async upsert(entry: IndexEntry): Promise<void> {\n const registration = this.registration(entry.documentKey.collection);\n const statement = compileUpsert(registration.metadata, entry);\n await registration.ready;\n await this.transport.execute(statement);\n }\n async query(query: IndexQuery): Promise<IndexPage> {\n const registration = this.registration(query.collection);\n const statement = compileQuery(registration.metadata, query);\n await registration.ready;\n return queryPage(registration.metadata, query, await this.transport.execute(statement));\n }\n async delete(documentKey: DocumentKey): Promise<void> {\n key(documentKey.collection, documentKey.id);\n const registration = this.registration(documentKey.collection);\n await registration.ready;\n await this.transport.execute({\n sql: `DELETE FROM ${quote(documentKey.collection)} WHERE \"document_id\" = ?1`,\n params: [documentKey.id],\n });\n }\n async clear(collection: string): Promise<void> {\n const registration = this.registration(collection);\n await registration.ready;\n await this.transport.execute({ sql: `DELETE FROM ${quote(collection)}`, params: [] });\n }\n private registration(collection: string): { metadata: CollectionMetadata; ready: Promise<void> } {\n const registration = this.#registrations.get(collection);\n if (!registration) throw new StorageOperationError('D1 collection is not initialized');\n return registration;\n }\n}\n\n/** Validate transport rows without accepting arrays, nulls, or primitive values. */\nexport function sqlRows(value: unknown): SqlResult {\n if (\n !Array.isArray(value) ||\n value.some((row) => row === null || typeof row !== 'object' || Array.isArray(row))\n )\n throw new StorageOperationError('D1 returned invalid rows');\n return { rows: value as Record<string, unknown>[] };\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DocumentKey } from '../core/model.js';
|
|
2
|
+
export declare function collectionPrefix(collection: string): string;
|
|
3
|
+
export declare function objectKey(documentKey: DocumentKey): string;
|
|
4
|
+
export declare function documentKeyFromObject(collection: string, object: string): DocumentKey;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { javaUtf8 } from '../core/cursor.js';
|
|
2
|
+
import { key, text } from '../core/model.js';
|
|
3
|
+
function segment(value) {
|
|
4
|
+
return Array.from(javaUtf8(value), (byte) => (byte >= 65 && byte <= 90) ||
|
|
5
|
+
(byte >= 97 && byte <= 122) ||
|
|
6
|
+
(byte >= 48 && byte <= 57) ||
|
|
7
|
+
[45, 46, 95, 126].includes(byte)
|
|
8
|
+
? String.fromCharCode(byte)
|
|
9
|
+
: `%${byte.toString(16).toUpperCase().padStart(2, '0')}`).join('');
|
|
10
|
+
}
|
|
11
|
+
export function collectionPrefix(collection) {
|
|
12
|
+
return `${segment(text(collection, 'collection'))}/`;
|
|
13
|
+
}
|
|
14
|
+
export function objectKey(documentKey) {
|
|
15
|
+
const validated = key(documentKey.collection, documentKey.id);
|
|
16
|
+
return collectionPrefix(validated.collection) + segment(validated.id);
|
|
17
|
+
}
|
|
18
|
+
export function documentKeyFromObject(collection, object) {
|
|
19
|
+
const prefix = collectionPrefix(collection);
|
|
20
|
+
if (!object.startsWith(prefix))
|
|
21
|
+
throw new TypeError('object key is outside collection');
|
|
22
|
+
const encoded = object.slice(prefix.length);
|
|
23
|
+
if (!encoded || encoded.includes('/') || /[^\x00-\x7F]/.test(encoded))
|
|
24
|
+
throw new TypeError('invalid object key');
|
|
25
|
+
let id;
|
|
26
|
+
try {
|
|
27
|
+
id = decodeURIComponent(encoded);
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
throw new TypeError('invalid object key encoding', { cause });
|
|
31
|
+
}
|
|
32
|
+
if (segment(id) !== encoded)
|
|
33
|
+
throw new TypeError('object key is not canonical');
|
|
34
|
+
return key(collection, id);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=r2-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-key.js","sourceRoot":"","sources":["../../src/storage/r2-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAC1C,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;QAC1B,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,GAAG,CAAC;QAC3B,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;QAC1B,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAC9B,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;QAC3B,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3D,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,GAAG,CAAC;AACvD,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,WAAwB;IAChD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9D,OAAO,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,UAAkB,EAAE,MAAc;IACtE,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;QACnE,MAAM,IAAI,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC5C,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC,6BAA6B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO;QAAE,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;IAChF,OAAO,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import { javaUtf8 } from '../core/cursor.js';\nimport { key, text } from '../core/model.js';\nimport type { DocumentKey } from '../core/model.js';\n\nfunction segment(value: string): string {\n return Array.from(javaUtf8(value), (byte) =>\n (byte >= 65 && byte <= 90) ||\n (byte >= 97 && byte <= 122) ||\n (byte >= 48 && byte <= 57) ||\n [45, 46, 95, 126].includes(byte)\n ? String.fromCharCode(byte)\n : `%${byte.toString(16).toUpperCase().padStart(2, '0')}`,\n ).join('');\n}\nexport function collectionPrefix(collection: string): string {\n return `${segment(text(collection, 'collection'))}/`;\n}\nexport function objectKey(documentKey: DocumentKey): string {\n const validated = key(documentKey.collection, documentKey.id);\n return collectionPrefix(validated.collection) + segment(validated.id);\n}\nexport function documentKeyFromObject(collection: string, object: string): DocumentKey {\n const prefix = collectionPrefix(collection);\n if (!object.startsWith(prefix)) throw new TypeError('object key is outside collection');\n const encoded = object.slice(prefix.length);\n if (!encoded || encoded.includes('/') || /[^\\x00-\\x7F]/.test(encoded))\n throw new TypeError('invalid object key');\n let id: string;\n try {\n id = decodeURIComponent(encoded);\n } catch (cause) {\n throw new TypeError('invalid object key encoding', { cause });\n }\n if (segment(id) !== encoded) throw new TypeError('object key is not canonical');\n return key(collection, id);\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { StoredDocument } from '../core/model.js';
|
|
2
|
+
import type { DocumentKey, DocumentPage, DocumentStore } from '../core/model.js';
|
|
3
|
+
import { D1IndexStore } from '../storage/d1.js';
|
|
4
|
+
/** Structural binding contracts accept Cloudflare's generated runtime types. */
|
|
5
|
+
export interface R2BucketBinding {
|
|
6
|
+
put(key: string, value: Uint8Array): Promise<unknown>;
|
|
7
|
+
get(key: string): Promise<{
|
|
8
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
9
|
+
} | null>;
|
|
10
|
+
delete(key: string): Promise<void>;
|
|
11
|
+
list(options: {
|
|
12
|
+
prefix: string;
|
|
13
|
+
limit: number;
|
|
14
|
+
cursor?: string;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
objects: {
|
|
17
|
+
key: string;
|
|
18
|
+
}[];
|
|
19
|
+
truncated: boolean;
|
|
20
|
+
cursor?: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface D1DatabaseBinding {
|
|
24
|
+
prepare(sql: string): {
|
|
25
|
+
bind(...values: (string | number)[]): {
|
|
26
|
+
all(): Promise<{
|
|
27
|
+
success: boolean;
|
|
28
|
+
results?: unknown;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
all(): Promise<{
|
|
32
|
+
success: boolean;
|
|
33
|
+
results?: unknown;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare class WorkersR2DocumentStore implements DocumentStore {
|
|
38
|
+
private readonly bucket;
|
|
39
|
+
constructor(bucket: R2BucketBinding);
|
|
40
|
+
put(documentKey: DocumentKey, document: StoredDocument): Promise<void>;
|
|
41
|
+
get(documentKey: DocumentKey): Promise<StoredDocument>;
|
|
42
|
+
delete(documentKey: DocumentKey): Promise<void>;
|
|
43
|
+
list(collection: string, cursor: string | undefined, limit: number): Promise<DocumentPage>;
|
|
44
|
+
}
|
|
45
|
+
export declare class WorkersD1IndexStore extends D1IndexStore {
|
|
46
|
+
constructor(database: D1DatabaseBinding);
|
|
47
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { DocumentNotFoundError, StorageOperationError } from '../core/errors.js';
|
|
2
|
+
import { positive, StoredDocument, text } from '../core/model.js';
|
|
3
|
+
import { D1IndexStore, sqlRows } from '../storage/d1.js';
|
|
4
|
+
import { collectionPrefix, documentKeyFromObject, objectKey } from '../storage/r2-key.js';
|
|
5
|
+
export class WorkersR2DocumentStore {
|
|
6
|
+
bucket;
|
|
7
|
+
constructor(bucket) {
|
|
8
|
+
this.bucket = bucket;
|
|
9
|
+
}
|
|
10
|
+
async put(documentKey, document) {
|
|
11
|
+
const location = objectKey(documentKey);
|
|
12
|
+
const bytes = document.content();
|
|
13
|
+
try {
|
|
14
|
+
await this.bucket.put(location, bytes);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new StorageOperationError('R2 put failed');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async get(documentKey) {
|
|
21
|
+
const location = objectKey(documentKey);
|
|
22
|
+
let result;
|
|
23
|
+
try {
|
|
24
|
+
result = await this.bucket.get(location);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new StorageOperationError('R2 get failed');
|
|
28
|
+
}
|
|
29
|
+
if (result === null)
|
|
30
|
+
throw new DocumentNotFoundError(documentKey);
|
|
31
|
+
try {
|
|
32
|
+
return new StoredDocument(new Uint8Array(await result.arrayBuffer()));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new StorageOperationError('R2 body read failed');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async delete(documentKey) {
|
|
39
|
+
const location = objectKey(documentKey);
|
|
40
|
+
try {
|
|
41
|
+
await this.bucket.delete(location);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new StorageOperationError('R2 delete failed');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async list(collection, cursor, limit) {
|
|
48
|
+
const options = {
|
|
49
|
+
prefix: collectionPrefix(collection),
|
|
50
|
+
limit: positive(limit, 'limit'),
|
|
51
|
+
...(cursor !== undefined ? { cursor: text(cursor, 'cursor') } : {}),
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
const result = await this.bucket.list(options);
|
|
55
|
+
if (result.objects.length > limit || typeof result.truncated !== 'boolean')
|
|
56
|
+
throw new TypeError();
|
|
57
|
+
if (result.truncated && !result.cursor)
|
|
58
|
+
throw new TypeError();
|
|
59
|
+
return {
|
|
60
|
+
documentKeys: result.objects.map((item) => documentKeyFromObject(collection, item.key)),
|
|
61
|
+
...(result.truncated ? { nextCursor: result.cursor } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new StorageOperationError('R2 list failed or returned an invalid page');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class BindingD1Transport {
|
|
70
|
+
database;
|
|
71
|
+
constructor(database) {
|
|
72
|
+
this.database = database;
|
|
73
|
+
}
|
|
74
|
+
async execute(statement) {
|
|
75
|
+
let result;
|
|
76
|
+
try {
|
|
77
|
+
const prepared = this.database.prepare(statement.sql);
|
|
78
|
+
result = await (statement.params.length ? prepared.bind(...statement.params) : prepared).all();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new StorageOperationError('D1 binding operation failed');
|
|
82
|
+
}
|
|
83
|
+
if (!result || result.success !== true)
|
|
84
|
+
throw new StorageOperationError('D1 binding reported failure');
|
|
85
|
+
return sqlRows(result.results ?? []);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class WorkersD1IndexStore extends D1IndexStore {
|
|
89
|
+
constructor(database) {
|
|
90
|
+
super(new BindingD1Transport(database));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workers/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { DocumentNotFoundError, StorageOperationError } from '../core/errors.js';\nimport { positive, StoredDocument, text } from '../core/model.js';\nimport type { DocumentKey, DocumentPage, DocumentStore } from '../core/model.js';\nimport { D1IndexStore, sqlRows } from '../storage/d1.js';\nimport type { D1Transport, SqlResult, SqlStatement } from '../storage/d1.js';\nimport { collectionPrefix, documentKeyFromObject, objectKey } from '../storage/r2-key.js';\n\n/** Structural binding contracts accept Cloudflare's generated runtime types. */\nexport interface R2BucketBinding {\n put(key: string, value: Uint8Array): Promise<unknown>;\n get(key: string): Promise<{ arrayBuffer(): Promise<ArrayBuffer> } | null>;\n delete(key: string): Promise<void>;\n list(options: {\n prefix: string;\n limit: number;\n cursor?: string;\n }): Promise<{ objects: { key: string }[]; truncated: boolean; cursor?: string }>;\n}\nexport interface D1DatabaseBinding {\n prepare(sql: string): {\n bind(...values: (string | number)[]): {\n all(): Promise<{ success: boolean; results?: unknown }>;\n };\n all(): Promise<{ success: boolean; results?: unknown }>;\n };\n}\nexport class WorkersR2DocumentStore implements DocumentStore {\n constructor(private readonly bucket: R2BucketBinding) {}\n async put(documentKey: DocumentKey, document: StoredDocument): Promise<void> {\n const location = objectKey(documentKey);\n const bytes = document.content();\n try {\n await this.bucket.put(location, bytes);\n } catch {\n throw new StorageOperationError('R2 put failed');\n }\n }\n async get(documentKey: DocumentKey): Promise<StoredDocument> {\n const location = objectKey(documentKey);\n let result: { arrayBuffer(): Promise<ArrayBuffer> } | null;\n try {\n result = await this.bucket.get(location);\n } catch {\n throw new StorageOperationError('R2 get failed');\n }\n if (result === null) throw new DocumentNotFoundError(documentKey);\n try {\n return new StoredDocument(new Uint8Array(await result.arrayBuffer()));\n } catch {\n throw new StorageOperationError('R2 body read failed');\n }\n }\n async delete(documentKey: DocumentKey): Promise<void> {\n const location = objectKey(documentKey);\n try {\n await this.bucket.delete(location);\n } catch {\n throw new StorageOperationError('R2 delete failed');\n }\n }\n async list(collection: string, cursor: string | undefined, limit: number): Promise<DocumentPage> {\n const options = {\n prefix: collectionPrefix(collection),\n limit: positive(limit, 'limit'),\n ...(cursor !== undefined ? { cursor: text(cursor, 'cursor') } : {}),\n };\n try {\n const result = await this.bucket.list(options);\n if (result.objects.length > limit || typeof result.truncated !== 'boolean')\n throw new TypeError();\n if (result.truncated && !result.cursor) throw new TypeError();\n return {\n documentKeys: result.objects.map((item) => documentKeyFromObject(collection, item.key)),\n ...(result.truncated ? { nextCursor: result.cursor! } : {}),\n };\n } catch {\n throw new StorageOperationError('R2 list failed or returned an invalid page');\n }\n }\n}\nclass BindingD1Transport implements D1Transport {\n constructor(private readonly database: D1DatabaseBinding) {}\n async execute(statement: SqlStatement): Promise<SqlResult> {\n let result: { success: boolean; results?: unknown };\n try {\n const prepared = this.database.prepare(statement.sql);\n result = await (\n statement.params.length ? prepared.bind(...statement.params) : prepared\n ).all();\n } catch {\n throw new StorageOperationError('D1 binding operation failed');\n }\n if (!result || result.success !== true)\n throw new StorageOperationError('D1 binding reported failure');\n return sqlRows(result.results ?? []);\n }\n}\nexport class WorkersD1IndexStore extends D1IndexStore {\n constructor(database: D1DatabaseBinding) {\n super(new BindingD1Transport(database));\n }\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { StoredDocument, defineSchema } from '@nexcraft-dev/r2d1';
|
|
2
|
+
import type { DocumentCodec } from '@nexcraft-dev/r2d1';
|
|
3
|
+
|
|
4
|
+
export interface User {
|
|
5
|
+
id: string;
|
|
6
|
+
country: string;
|
|
7
|
+
rank: bigint;
|
|
8
|
+
payload: string;
|
|
9
|
+
}
|
|
10
|
+
export const schema = defineSchema<User>({
|
|
11
|
+
collection: 'users',
|
|
12
|
+
id: 'id',
|
|
13
|
+
indexes: { country: 'string', rank: 'long' },
|
|
14
|
+
});
|
|
15
|
+
function encode(value: string): string {
|
|
16
|
+
const bytes = new TextEncoder().encode(value);
|
|
17
|
+
let binary = '';
|
|
18
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
19
|
+
return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
|
|
20
|
+
}
|
|
21
|
+
function decode(value: string): string {
|
|
22
|
+
const binary = atob(value.replaceAll('-', '+').replaceAll('_', '/'));
|
|
23
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(
|
|
24
|
+
Uint8Array.from(binary, (c) => c.charCodeAt(0)),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
/** Matches the Java reference project's IntegrationDocumentCodec, not a universal Java format. */
|
|
28
|
+
export const codec: DocumentCodec<User> = {
|
|
29
|
+
serialize(value) {
|
|
30
|
+
return new StoredDocument(
|
|
31
|
+
new TextEncoder().encode(
|
|
32
|
+
[
|
|
33
|
+
encode(value.id),
|
|
34
|
+
encode(value.country),
|
|
35
|
+
value.rank.toString(),
|
|
36
|
+
encode(value.payload),
|
|
37
|
+
].join('.'),
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
deserialize(document) {
|
|
42
|
+
const fields = new TextDecoder('utf-8', { fatal: true }).decode(document.content()).split('.');
|
|
43
|
+
if (fields.length !== 4 || !/^-?\d+$/.test(fields[2]!))
|
|
44
|
+
throw new TypeError('Invalid integration document encoding');
|
|
45
|
+
const rank = BigInt(fields[2]!);
|
|
46
|
+
if (rank < -(1n << 63n) || rank >= 1n << 63n) throw new TypeError('Invalid Long');
|
|
47
|
+
return {
|
|
48
|
+
id: decode(fields[0]!),
|
|
49
|
+
country: decode(fields[1]!),
|
|
50
|
+
rank,
|
|
51
|
+
payload: decode(fields[3]!),
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
package/examples/node.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { R2D1 } from '@nexcraft-dev/r2d1';
|
|
2
|
+
import { NodeR2DocumentStore, NodeD1IndexStore } from '@nexcraft-dev/r2d1/node';
|
|
3
|
+
import type { NodeR2Options, NodeD1Options } from '@nexcraft-dev/r2d1/node';
|
|
4
|
+
import { schema, codec } from './common.js';
|
|
5
|
+
|
|
6
|
+
/** Call with a dedicated example bucket and database. This function writes one document. */
|
|
7
|
+
export async function runNodeExample(r2: NodeR2Options, d1: NodeD1Options): Promise<void> {
|
|
8
|
+
const documents = new NodeR2DocumentStore(r2);
|
|
9
|
+
try {
|
|
10
|
+
const users = await new R2D1(documents, new NodeD1IndexStore(d1)).collection(schema, codec);
|
|
11
|
+
await users.put({ id: 'example-user', country: 'NZ', rank: 42n, payload: 'Example' });
|
|
12
|
+
console.log(await users.get('example-user'));
|
|
13
|
+
const page = await users
|
|
14
|
+
.query()
|
|
15
|
+
.where('country')
|
|
16
|
+
.eq('NZ')
|
|
17
|
+
.sortBy('rank', 'DESC')
|
|
18
|
+
.limit(20)
|
|
19
|
+
.fetch();
|
|
20
|
+
console.log(page.items);
|
|
21
|
+
if (page.nextCursor)
|
|
22
|
+
console.log(
|
|
23
|
+
await users
|
|
24
|
+
.query()
|
|
25
|
+
.where('country')
|
|
26
|
+
.eq('NZ')
|
|
27
|
+
.sortBy('rank', 'DESC')
|
|
28
|
+
.limit(20)
|
|
29
|
+
.after(page.nextCursor)
|
|
30
|
+
.fetch(),
|
|
31
|
+
);
|
|
32
|
+
await users.delete('example-user');
|
|
33
|
+
} finally {
|
|
34
|
+
documents.close();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { R2Bucket, D1Database } from '@cloudflare/workers-types';
|
|
2
|
+
import { R2D1 } from '@nexcraft-dev/r2d1';
|
|
3
|
+
import { WorkersR2DocumentStore, WorkersD1IndexStore } from '@nexcraft-dev/r2d1/workers';
|
|
4
|
+
import { schema, codec } from './common.js';
|
|
5
|
+
|
|
6
|
+
interface Env {
|
|
7
|
+
DOCUMENTS: R2Bucket;
|
|
8
|
+
INDEXES: D1Database;
|
|
9
|
+
}
|
|
10
|
+
export default {
|
|
11
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
12
|
+
const users = await new R2D1(
|
|
13
|
+
new WorkersR2DocumentStore(env.DOCUMENTS),
|
|
14
|
+
new WorkersD1IndexStore(env.INDEXES),
|
|
15
|
+
).collection(schema, codec);
|
|
16
|
+
const id = new URL(request.url).searchParams.get('id');
|
|
17
|
+
if (!id) return new Response('Supply ?id=<document-id>', { status: 400 });
|
|
18
|
+
const user = await users.get(id);
|
|
19
|
+
if (!user) return new Response('Not found', { status: 404 });
|
|
20
|
+
return Response.json({ ...user, rank: user.rank.toString() });
|
|
21
|
+
},
|
|
22
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexcraft-dev/r2d1",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Document storage on Cloudflare R2 with rebuildable D1 indexes for Node.js and Workers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"examples"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./node": {
|
|
22
|
+
"types": "./dist/node/index.d.ts",
|
|
23
|
+
"import": "./dist/node/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./workers": {
|
|
26
|
+
"types": "./dist/workers/index.d.ts",
|
|
27
|
+
"import": "./dist/workers/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"scripts": {
|
|
32
|
+
"format": "prettier --write .",
|
|
33
|
+
"format:check": "prettier --check .",
|
|
34
|
+
"clean": "node --input-type=module -e \"import {rmSync} from 'node:fs'; rmSync('dist',{recursive:true,force:true})\"",
|
|
35
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.examples.json",
|
|
37
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
38
|
+
"test:live": "npm run build && node --test test/live/*.test.mjs",
|
|
39
|
+
"verify:java": "npm run build && node scripts/java-fixtures.mjs",
|
|
40
|
+
"check:package": "node scripts/check-package.mjs",
|
|
41
|
+
"prepack": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@aws-sdk/client-s3": "3.1161.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@cloudflare/workers-types": "5.20261101.1",
|
|
48
|
+
"@types/node": "^22.0.0",
|
|
49
|
+
"esbuild": "0.28.2",
|
|
50
|
+
"miniflare": "4.20260730.0",
|
|
51
|
+
"prettier": "3.9.10",
|
|
52
|
+
"typescript": "7.0.2"
|
|
53
|
+
},
|
|
54
|
+
"types": "./dist/index.d.ts",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/nexcraft-dev/r2d1-ts.git"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public",
|
|
61
|
+
"registry": "https://registry.npmjs.org/"
|
|
62
|
+
}
|
|
63
|
+
}
|