@miniflare/durable-objects
Advanced tools
Comparing version 2.0.0-rc.1 to 2.0.0-rc.2
import { Awaitable } from '@miniflare/shared'; | ||
import { Compatibility } from '@miniflare/shared'; | ||
import { Context } from '@miniflare/shared'; | ||
@@ -29,3 +30,3 @@ import { MiniflareError } from '@miniflare/shared'; | ||
export declare type DurableObjectErrorCode = "ERR_SCRIPT_NOT_FOUND" | "ERR_CLASS_NOT_FOUND" | "ERR_RESPONSE_TYPE"; | ||
export declare type DurableObjectErrorCode = "ERR_SCRIPT_NOT_FOUND" | "ERR_CLASS_NOT_FOUND" | "ERR_RESPONSE_TYPE" | "ERR_DESERIALIZATION"; | ||
@@ -58,3 +59,3 @@ export declare type DurableObjectFactory = (id: DurableObjectId) => Promise<DurableObjectState>; | ||
#private; | ||
constructor(objectName: string, factory: DurableObjectFactory, requireFullUrl?: boolean); | ||
constructor(objectName: string, factory: DurableObjectFactory, compat?: Compatibility); | ||
newUniqueId(_options?: NewUniqueIdOptions): DurableObjectId; | ||
@@ -131,3 +132,3 @@ idFromName(name: string): DurableObjectId; | ||
readonly id: DurableObjectId; | ||
constructor(factory: DurableObjectFactory, id: DurableObjectId, requireFullUrl?: boolean); | ||
constructor(factory: DurableObjectFactory, id: DurableObjectId, compat?: Compatibility); | ||
get name(): string | undefined; | ||
@@ -134,0 +135,0 @@ fetch(input: RequestInfo_2, init?: RequestInit_2): Promise<Response_2>; |
@@ -13,2 +13,7 @@ var __defProp = Object.defineProperty; | ||
// packages/durable-objects/src/error.ts | ||
import { MiniflareError } from "@miniflare/shared"; | ||
var DurableObjectError = class extends MiniflareError { | ||
}; | ||
// packages/durable-objects/src/namespace.ts | ||
@@ -23,4 +28,122 @@ import { createHash, webcrypto } from "crypto"; | ||
} from "@miniflare/core"; | ||
import { InputGate, OutputGate } from "@miniflare/shared"; | ||
import { Response as BaseResponse } from "undici"; | ||
import { | ||
InputGate, | ||
OutputGate | ||
} from "@miniflare/shared"; | ||
import { Request as BaseRequest, Response as BaseResponse } from "undici"; | ||
function hexEncode(value) { | ||
return Array.from(value).map((byte) => byte.toString(16).padStart(2, "0")).join(""); | ||
} | ||
var kObjectName = Symbol("kObjectName"); | ||
var DurableObjectId = class { | ||
constructor(objectName, hexId, name) { | ||
this.name = name; | ||
this[kObjectName] = objectName; | ||
this.#hexId = hexId; | ||
} | ||
[kObjectName]; | ||
#hexId; | ||
equals(other) { | ||
if (!(other instanceof DurableObjectId)) | ||
return false; | ||
return this.#hexId === other.#hexId; | ||
} | ||
toString() { | ||
return this.#hexId; | ||
} | ||
}; | ||
var kInstance = Symbol("kInstance"); | ||
var kFetch = Symbol("kFetch"); | ||
var DurableObjectState = class { | ||
constructor(id, storage) { | ||
this.id = id; | ||
this.storage = storage; | ||
} | ||
#inputGate = new InputGate(); | ||
[kInstance]; | ||
waitUntil(_promise) { | ||
} | ||
blockConcurrencyWhile(closure) { | ||
return this.#inputGate.runWithClosed(closure); | ||
} | ||
[kFetch](request) { | ||
const outputGate = new OutputGate(); | ||
return outputGate.runWith(() => this.#inputGate.runWith(() => this[kInstance].fetch(request))); | ||
} | ||
}; | ||
var DurableObjectStub = class { | ||
constructor(factory, id, compat) { | ||
this.id = id; | ||
this.#factory = factory; | ||
this.#compat = compat; | ||
} | ||
#factory; | ||
#compat; | ||
get name() { | ||
return this.id.name; | ||
} | ||
async fetch(input, init) { | ||
const state = await this.#factory(this.id); | ||
if (!this.#compat?.isEnabled("durable_object_fetch_requires_full_url") && typeof input === "string") { | ||
input = new URL(input, "https://fake-host"); | ||
} | ||
if (this.#compat?.isEnabled("fetch_refuses_unknown_protocols")) { | ||
const url = input instanceof URL ? input : new URL(input instanceof Request || input instanceof BaseRequest ? input.url : input.toString()); | ||
if (url.protocol !== "http:" && url.protocol !== "https:") { | ||
throw new TypeError(`Fetch API cannot load: ${url.toString()}`); | ||
} | ||
} | ||
const request = input instanceof Request && !init ? input : new Request(input, init); | ||
const res = await state[kFetch](withInputGating(withImmutableHeaders(request))); | ||
const validRes = res instanceof Response || res instanceof BaseResponse; | ||
if (!validRes) { | ||
throw new DurableObjectError("ERR_RESPONSE_TYPE", "Durable Object fetch handler didn't respond with a Response object"); | ||
} | ||
return res; | ||
} | ||
}; | ||
var HEX_ID_REGEXP = /^[A-Za-z0-9]{64}$/; | ||
var DurableObjectNamespace = class { | ||
#objectName; | ||
#factory; | ||
#objectNameHash; | ||
#objectNameHashHex; | ||
#compat; | ||
constructor(objectName, factory, compat) { | ||
this.#objectName = objectName; | ||
this.#factory = factory; | ||
this.#objectNameHash = createHash("sha256").update(this.#objectName).digest().slice(0, 8); | ||
this.#objectNameHashHex = hexEncode(this.#objectNameHash); | ||
this.#compat = compat; | ||
} | ||
newUniqueId(_options) { | ||
const id = new Uint8Array(32); | ||
const view = new DataView(id.buffer); | ||
view.setBigUint64(1, BigInt(Date.now())); | ||
webcrypto.getRandomValues(new DataView(id.buffer, 9, 15)); | ||
id.set(this.#objectNameHash, 24); | ||
return new DurableObjectId(this.#objectName, hexEncode(id)); | ||
} | ||
idFromName(name) { | ||
const id = createHash("sha256").update(this.#objectName).update(name).digest(); | ||
id[0] |= 128; | ||
id.set(this.#objectNameHash, 24); | ||
return new DurableObjectId(this.#objectName, hexEncode(id), name); | ||
} | ||
idFromString(hexId) { | ||
if (!HEX_ID_REGEXP.test(hexId)) { | ||
throw new TypeError("Invalid Durable Object ID. Durable Object IDs must be 64 hex digits."); | ||
} | ||
if (!hexId.endsWith(this.#objectNameHashHex)) { | ||
throw new TypeError("Invalid Durable Object ID. The ID does not match this Durable Object class."); | ||
} | ||
return new DurableObjectId(this.#objectName, hexId.toLowerCase()); | ||
} | ||
get(id) { | ||
if (id[kObjectName] !== this.#objectName || !id.toString().endsWith(this.#objectNameHashHex)) { | ||
throw new TypeError("ID is not for this Durable Object class."); | ||
} | ||
return new DurableObjectStub(this.#factory, id, this.#compat); | ||
} | ||
}; | ||
@@ -30,6 +153,6 @@ // packages/durable-objects/src/plugin.ts | ||
import { | ||
MiniflareError, | ||
Option, | ||
OptionType, | ||
Plugin | ||
Plugin, | ||
resolveStoragePersist | ||
} from "@miniflare/shared"; | ||
@@ -263,2 +386,9 @@ | ||
} | ||
function helpfulDeserialize(buffer) { | ||
try { | ||
return deserialize(buffer); | ||
} catch (e) { | ||
throw new DurableObjectError("ERR_DESERIALIZATION", "Unable to deserialize stored Durable Object data due to an invalid or unsupported version.\nThe Durable Object data storage format changed in Miniflare 2. You cannot load Durable Object data created with Miniflare 1 and must delete it.", e); | ||
} | ||
} | ||
async function get(storage, keys) { | ||
@@ -281,4 +411,5 @@ if (Array.isArray(keys)) { | ||
const value2 = values[i]; | ||
if (value2 !== void 0) | ||
res.set(defined[i], deserialize(value2.value)); | ||
if (value2 !== void 0) { | ||
res.set(defined[i], helpfulDeserialize(value2.value)); | ||
} | ||
} | ||
@@ -289,3 +420,3 @@ return res; | ||
const value = await storage.get(keys); | ||
return value && deserialize(value.value); | ||
return value && helpfulDeserialize(value.value); | ||
} | ||
@@ -587,7 +718,6 @@ async function list(storage, options = {}) { | ||
// packages/durable-objects/src/plugin.ts | ||
var DurableObjectError = class extends MiniflareError { | ||
}; | ||
var DurableObjectsPlugin = class extends Plugin { | ||
durableObjects; | ||
durableObjectsPersist; | ||
#persist; | ||
#processedObjects; | ||
@@ -603,2 +733,3 @@ #requireFullUrl; | ||
this.assignOptions(options); | ||
this.#persist = resolveStoragePersist(ctx.rootPath, this.durableObjectsPersist); | ||
this.#processedObjects = Object.entries(this.durableObjects ?? {}).map(([name, options2]) => { | ||
@@ -620,3 +751,3 @@ const className = typeof options2 === "object" ? options2.className : options2; | ||
statePromise = (async () => { | ||
const objectStorage = new DurableObjectStorage(await storage.storage(key, this.durableObjectsPersist)); | ||
const objectStorage = new DurableObjectStorage(await storage.storage(key, this.#persist)); | ||
const state = new DurableObjectState(id, objectStorage); | ||
@@ -633,3 +764,3 @@ const constructor = this.#constructors.get(objectName); | ||
const factory = (id) => this.getObject(storage, id); | ||
return new DurableObjectNamespace(objectName, factory, this.#requireFullUrl); | ||
return new DurableObjectNamespace(objectName, factory, this.ctx.compat); | ||
} | ||
@@ -700,116 +831,5 @@ setup(storageFactory) { | ||
], DurableObjectsPlugin.prototype, "durableObjectsPersist", 2); | ||
// packages/durable-objects/src/namespace.ts | ||
function hexEncode(value) { | ||
return Array.from(value).map((byte) => byte.toString(16).padStart(2, "0")).join(""); | ||
} | ||
var kObjectName = Symbol("kObjectName"); | ||
var DurableObjectId2 = class { | ||
constructor(objectName, hexId, name) { | ||
this.name = name; | ||
this[kObjectName] = objectName; | ||
this.#hexId = hexId; | ||
} | ||
[kObjectName]; | ||
#hexId; | ||
equals(other) { | ||
if (!(other instanceof DurableObjectId2)) | ||
return false; | ||
return this.#hexId === other.#hexId; | ||
} | ||
toString() { | ||
return this.#hexId; | ||
} | ||
}; | ||
var kInstance = Symbol("kInstance"); | ||
var kFetch = Symbol("kFetch"); | ||
var DurableObjectState = class { | ||
constructor(id, storage) { | ||
this.id = id; | ||
this.storage = storage; | ||
} | ||
#inputGate = new InputGate(); | ||
[kInstance]; | ||
waitUntil(_promise) { | ||
} | ||
blockConcurrencyWhile(closure) { | ||
return this.#inputGate.runWithClosed(closure); | ||
} | ||
[kFetch](request) { | ||
const outputGate = new OutputGate(); | ||
return outputGate.runWith(() => this.#inputGate.runWith(() => this[kInstance].fetch(request))); | ||
} | ||
}; | ||
var DurableObjectStub = class { | ||
constructor(factory, id, requireFullUrl) { | ||
this.id = id; | ||
this.#factory = factory; | ||
this.#requireFullUrl = requireFullUrl; | ||
} | ||
#factory; | ||
#requireFullUrl; | ||
get name() { | ||
return this.id.name; | ||
} | ||
async fetch(input, init) { | ||
const state = await this.#factory(this.id); | ||
if (!this.#requireFullUrl && typeof input === "string") { | ||
input = new URL(input, "https://fake-host"); | ||
} | ||
const request = input instanceof Request && !init ? input : new Request(input, init); | ||
const res = await state[kFetch](withInputGating(withImmutableHeaders(request))); | ||
const validRes = res instanceof Response || res instanceof BaseResponse; | ||
if (!validRes) { | ||
throw new DurableObjectError("ERR_RESPONSE_TYPE", "Durable Object fetch handler didn't respond with a Response object"); | ||
} | ||
return res; | ||
} | ||
}; | ||
var HEX_ID_REGEXP = /^[A-Za-z0-9]{64}$/; | ||
var DurableObjectNamespace = class { | ||
#objectName; | ||
#factory; | ||
#objectNameHash; | ||
#objectNameHashHex; | ||
#requireFullUrl; | ||
constructor(objectName, factory, requireFullUrl) { | ||
this.#objectName = objectName; | ||
this.#factory = factory; | ||
this.#objectNameHash = createHash("sha256").update(this.#objectName).digest().slice(0, 8); | ||
this.#objectNameHashHex = hexEncode(this.#objectNameHash); | ||
this.#requireFullUrl = requireFullUrl; | ||
} | ||
newUniqueId(_options) { | ||
const id = new Uint8Array(32); | ||
const view = new DataView(id.buffer); | ||
view.setBigUint64(1, BigInt(Date.now())); | ||
webcrypto.getRandomValues(new DataView(id.buffer, 9, 15)); | ||
id.set(this.#objectNameHash, 24); | ||
return new DurableObjectId2(this.#objectName, hexEncode(id)); | ||
} | ||
idFromName(name) { | ||
const id = createHash("sha256").update(this.#objectName).update(name).digest(); | ||
id[0] |= 128; | ||
id.set(this.#objectNameHash, 24); | ||
return new DurableObjectId2(this.#objectName, hexEncode(id), name); | ||
} | ||
idFromString(hexId) { | ||
if (!HEX_ID_REGEXP.test(hexId)) { | ||
throw new TypeError("Invalid Durable Object ID. Durable Object IDs must be 64 hex digits."); | ||
} | ||
if (!hexId.endsWith(this.#objectNameHashHex)) { | ||
throw new TypeError("Invalid Durable Object ID. The ID does not match this Durable Object class."); | ||
} | ||
return new DurableObjectId2(this.#objectName, hexId.toLowerCase()); | ||
} | ||
get(id) { | ||
if (id[kObjectName] !== this.#objectName || !id.toString().endsWith(this.#objectNameHashHex)) { | ||
throw new TypeError("ID is not for this Durable Object class."); | ||
} | ||
return new DurableObjectStub(this.#factory, id, this.#requireFullUrl); | ||
} | ||
}; | ||
export { | ||
DurableObjectError, | ||
DurableObjectId2 as DurableObjectId, | ||
DurableObjectId, | ||
DurableObjectNamespace, | ||
@@ -816,0 +836,0 @@ DurableObjectState, |
{ | ||
"name": "@miniflare/durable-objects", | ||
"version": "2.0.0-rc.1", | ||
"version": "2.0.0-rc.2", | ||
"description": "Durable Objects module for Miniflare: a fun, full-featured, fully-local simulator for Cloudflare Workers", | ||
@@ -39,10 +39,10 @@ "keywords": [ | ||
"dependencies": { | ||
"@miniflare/core": "2.0.0-rc.1", | ||
"@miniflare/shared": "2.0.0-rc.1", | ||
"@miniflare/storage-memory": "2.0.0-rc.1", | ||
"undici": "^4.9.3" | ||
"@miniflare/core": "2.0.0-rc.2", | ||
"@miniflare/shared": "2.0.0-rc.2", | ||
"@miniflare/storage-memory": "2.0.0-rc.2", | ||
"undici": "^4.10.2" | ||
}, | ||
"devDependencies": { | ||
"@miniflare/shared-test": "2.0.0-rc.1" | ||
"@miniflare/shared-test": "2.0.0-rc.2" | ||
} | ||
} |
# `@miniflare/durable-objects` | ||
Durable Objects module for [Miniflare](https://github.com/cloudflare/miniflare): | ||
a fun, full-featured, fully-local simulator for Cloudflare Workers | ||
a fun, full-featured, fully-local simulator for Cloudflare Workers. See | ||
[📌 Durable Objects](https://miniflare.dev/durable-objects.html) for more | ||
details. | ||
## Example | ||
```js | ||
import { DurableObjectStorage } from "@miniflare/durable-objects"; | ||
import { MemoryStorage } from "@miniflare/storage-memory"; | ||
const storage = new DurableObjectStorage(new MemoryStorage()); | ||
await storage.put("key", "value"); | ||
console.log(await storage.get("key")); // value | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52436
996
18
+ Added@miniflare/core@2.0.0-rc.2(transitive)
+ Added@miniflare/shared@2.0.0-rc.2(transitive)
+ Added@miniflare/storage-memory@2.0.0-rc.2(transitive)
- Removed@miniflare/core@2.0.0-rc.1(transitive)
- Removed@miniflare/shared@2.0.0-rc.1(transitive)
- Removed@miniflare/storage-memory@2.0.0-rc.1(transitive)
Updated@miniflare/core@2.0.0-rc.2
Updated@miniflare/shared@2.0.0-rc.2
Updatedundici@^4.10.2