-
-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
worktop/durable
module (#80)
* feat: add `Bindings` type export * fix(types): add `passThroughOnException` to FetchEvent * chore: adjust tsconfig * feat: begin `worktop/modules` entry * fix(modules): restrict `Bindings` only to known keys * feat: add `define` and `listen` helpers * chore: add `/modules` to gitignore * chore(example): add `basic-module` example * chore: setup `worktop/durable` entry * feat: add abstract `Actor` class * fix(router): add `Durable.Namespace` to `Bindings` value * chore(types): assert `Durable.Namespace` as binding * chore(types): add `Durable` test w/ `Router` inside Actor * chore: cleanup * fix(durable): allow `storage.get` void return * chore(types): check `D.Object` and `D.Storage` native types * chore: add readme docs
- Loading branch information
Showing
13 changed files
with
433 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ node_modules | |
/request | ||
/response | ||
/modules | ||
/durable | ||
/router | ||
/utils | ||
/ws | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import type { Bindings } from 'worktop'; | ||
import type { WebSocket } from 'worktop/ws'; | ||
|
||
export namespace Durable { | ||
export interface Namespace { | ||
get(id: ObjectID): Object; | ||
idFromName(name: string): ObjectID; | ||
idFromString(hex: string): ObjectID; | ||
newUniqueId(options?: { | ||
jurisdiction: 'eu'; | ||
}): ObjectID; | ||
} | ||
|
||
export interface ObjectID { | ||
name?: string; | ||
toString(): string; | ||
} | ||
|
||
export interface Object { | ||
id: ObjectID; | ||
fetch: typeof fetch; | ||
name?: string; | ||
} | ||
|
||
export interface State { | ||
id: ObjectID; | ||
storage: Storage; | ||
waitUntil(f: any): void; | ||
blockConcurrencyWhile(f: any): Promise<void>; | ||
} | ||
|
||
export namespace Storage { | ||
export namespace Options { | ||
export interface Get { | ||
/** Bypass in-memory cache management */ | ||
noCache?: boolean; | ||
/** Opt out of race-condition protections */ | ||
allowConcurrency?: boolean; | ||
} | ||
|
||
export interface Put { | ||
/** Bypass in-memory cache management */ | ||
noCache?: boolean; | ||
/** Do not wait for disk flush */ | ||
allowUnconfirmed?: boolean; | ||
} | ||
|
||
export interface List extends Options.Get { | ||
/** begin listing results from this key, inclusive */ | ||
start?: string; | ||
/** stop listing results at this key, exclusive */ | ||
end?: string; | ||
/** only include results if key begins with prefix */ | ||
prefix?: string; | ||
/** if true, results given in descending lexicographic order */ | ||
reverse?: boolean; | ||
/** maximum number of results to return */ | ||
limit?: number; | ||
} | ||
} | ||
} | ||
|
||
export interface Storage { | ||
get<T>(key: string, options?: Storage.Options.Get): Promise<T | void>; | ||
get<T>(keys: string[], options?: Storage.Options.Get): Promise<Map<string, T>>; | ||
|
||
put<T>(key: string, value: T, options?: Storage.Options.Put): Promise<void>; | ||
put<T>(entries: Record<string, T>, options?: Storage.Options.Put): Promise<void>; | ||
|
||
delete(key: string, options?: Storage.Options.Put): Promise<boolean>; | ||
delete(keys: string[], options?: Storage.Options.Put): Promise<number>; | ||
deleteAll(options?: Storage.Options.Put): Promise<void>; | ||
|
||
list<T>(options?: Storage.Options.List): Promise<Map<string, T>>; | ||
} | ||
} | ||
|
||
export abstract class Actor { | ||
public DEBUG: boolean; | ||
constructor(state: Durable.State, bindings: Bindings); | ||
setup?(state: Durable.State, bindings: Bindings): Promise<void> | void; | ||
|
||
abstract receive(req: Request): Promise<Response> | Response; | ||
fetch(input: RequestInfo, init?: RequestInit): Promise<Response>; | ||
|
||
onconnect?(req: Request, ws: WebSocket): Promise<void> | void; | ||
connect(req: Request): Promise<Response>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { connect } from 'worktop/ws'; | ||
import type { Bindings } from 'worktop'; | ||
import type { WebSocket } from 'worktop/ws'; | ||
import type { Durable } from 'worktop/durable'; | ||
|
||
export abstract class Actor { | ||
DEBUG: boolean; | ||
|
||
constructor(state: Durable.State, bindings: Bindings) { | ||
if (this.setup) state.blockConcurrencyWhile(this.setup(state, bindings)); | ||
this.DEBUG = false; | ||
} | ||
|
||
setup?(state: Durable.State, bindings: Bindings): Promise<void> | void; | ||
onconnect?(req: Request, ws: WebSocket): Promise<void> | void; | ||
|
||
abstract receive(req: Request): Promise<Response> | Response; | ||
|
||
async connect(req: Request): Promise<Response> { | ||
let error = connect(req); | ||
if (error) return error; | ||
|
||
let { 0: client, 1: server } = new WebSocketPair; | ||
|
||
server.accept(); | ||
|
||
function closer() { | ||
server.close(); | ||
} | ||
|
||
server.addEventListener('close', closer); | ||
server.addEventListener('error', closer); | ||
|
||
if (this.onconnect) { | ||
await this.onconnect(req, server); | ||
} | ||
|
||
return new Response(null, { | ||
status: 101, | ||
statusText: 'Switching Protocols', | ||
webSocket: client, | ||
}); | ||
} | ||
|
||
async fetch(input: RequestInfo, init?: RequestInit): Promise<Response> { | ||
try { | ||
let request = new Request(input, init); | ||
return await this.receive(request); | ||
} catch (err) { | ||
let msg = this.DEBUG && (err as Error).stack; | ||
return new Response(msg || 'Error with receive', { status: 400 }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.