@browserlessapi/inferlo 0.7.0 → 0.8.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.
Files changed (78) hide show
  1. package/README.md +3 -2
  2. package/dist/app.d.ts +24 -0
  3. package/dist/app.js +336 -13
  4. package/dist/app.js.map +1 -1
  5. package/dist/authjs.d.ts +70 -0
  6. package/dist/authjs.js +94 -0
  7. package/dist/authjs.js.map +1 -0
  8. package/dist/better-auth.d.ts +89 -0
  9. package/dist/better-auth.js +75 -0
  10. package/dist/better-auth.js.map +1 -0
  11. package/dist/cli/main.js +1 -1
  12. package/dist/cli/main.js.map +1 -1
  13. package/dist/client-ip.d.ts +5 -0
  14. package/dist/client-ip.js +5 -0
  15. package/dist/client-ip.js.map +1 -1
  16. package/dist/cross-site.d.ts +14 -0
  17. package/dist/cross-site.js +56 -0
  18. package/dist/cross-site.js.map +1 -0
  19. package/dist/db/postgres.js +19 -5
  20. package/dist/db/postgres.js.map +1 -1
  21. package/dist/describe.d.ts +9 -2
  22. package/dist/describe.js +11 -1
  23. package/dist/describe.js.map +1 -1
  24. package/dist/disk-storage.d.ts +6 -0
  25. package/dist/disk-storage.js +96 -0
  26. package/dist/disk-storage.js.map +1 -0
  27. package/dist/files.d.ts +23 -0
  28. package/dist/files.js +168 -0
  29. package/dist/files.js.map +1 -0
  30. package/dist/index.d.ts +15 -4
  31. package/dist/index.js +6 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/jobs.d.ts +10 -4
  34. package/dist/jobs.js +7 -2
  35. package/dist/jobs.js.map +1 -1
  36. package/dist/jwt.d.ts +83 -0
  37. package/dist/jwt.js +312 -0
  38. package/dist/jwt.js.map +1 -0
  39. package/dist/model.js +6 -1
  40. package/dist/model.js.map +1 -1
  41. package/dist/oidc.d.ts +70 -0
  42. package/dist/oidc.js +379 -0
  43. package/dist/oidc.js.map +1 -0
  44. package/dist/openapi.js +17 -2
  45. package/dist/openapi.js.map +1 -1
  46. package/dist/passport.d.ts +54 -0
  47. package/dist/passport.js +68 -0
  48. package/dist/passport.js.map +1 -0
  49. package/dist/postgres-js.d.ts +39 -0
  50. package/dist/postgres-js.js +71 -0
  51. package/dist/postgres-js.js.map +1 -0
  52. package/dist/redis-sockets.d.ts +24 -0
  53. package/dist/redis-sockets.js +75 -0
  54. package/dist/redis-sockets.js.map +1 -0
  55. package/dist/route.d.ts +103 -2
  56. package/dist/route.js +27 -0
  57. package/dist/route.js.map +1 -1
  58. package/dist/schema.d.ts +21 -1
  59. package/dist/schema.js +70 -0
  60. package/dist/schema.js.map +1 -1
  61. package/dist/serve.d.ts +18 -0
  62. package/dist/serve.js +137 -2
  63. package/dist/serve.js.map +1 -1
  64. package/dist/sessions.js +35 -4
  65. package/dist/sessions.js.map +1 -1
  66. package/dist/sockets.d.ts +58 -0
  67. package/dist/sockets.js +111 -0
  68. package/dist/sockets.js.map +1 -0
  69. package/dist/storage.d.ts +47 -0
  70. package/dist/storage.js +123 -0
  71. package/dist/storage.js.map +1 -0
  72. package/dist/websocket.d.ts +81 -0
  73. package/dist/websocket.js +251 -0
  74. package/dist/websocket.js.map +1 -0
  75. package/llms.txt +1 -0
  76. package/package.json +43 -3
  77. package/templates/app/AGENTS.md +5 -3
  78. package/templates/app/test/notes.test.ts +2 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sockets.js","sourceRoot":"","sources":["../src/sockets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /** What storage.put() stored. */
2
+ export interface StoredFile {
3
+ key: string;
4
+ /** Size in bytes. */
5
+ size: number;
6
+ /** Media type, for example "image/png". */
7
+ type: string;
8
+ }
9
+ /** Where files are kept. Implement it to use another store; Inferlo checks keys and types before calling it. */
10
+ export interface StorageStore {
11
+ /** Shown in describe() and as app.storage.store, for example "memory" or "disk". */
12
+ readonly name: string;
13
+ /** Store the bytes and their media type under the key, replacing what was there. */
14
+ put(key: string, bytes: Uint8Array, type: string): Promise<void>;
15
+ /** The bytes and media type stored under the key, or null. */
16
+ get(key: string): Promise<{
17
+ bytes: Uint8Array;
18
+ type: string;
19
+ } | null>;
20
+ /** Remove the key. Resolves to true when it existed. */
21
+ delete(key: string): Promise<boolean>;
22
+ }
23
+ export interface StoragePutOptions {
24
+ /** Media type to store. Default: the File's or Blob's type, or "application/octet-stream". */
25
+ type?: string;
26
+ }
27
+ export interface Storage {
28
+ /** The store's name ("memory", "disk", ...), or null when createApp has no storage option. */
29
+ readonly store: string | null;
30
+ /**
31
+ * Store a file under a key such as "avatars/42.png" (segments of letters, digits, ".", "_" and "-", separated by "/"),
32
+ * replacing what was there. Keys are the same for every user and tenant: put the tenant or user id in the key.
33
+ */
34
+ put(key: string, data: Blob | Uint8Array | ArrayBuffer | string, options?: StoragePutOptions): Promise<StoredFile>;
35
+ /** The file stored under the key as a web File (name: the key's last segment), or null when there is none. */
36
+ get(key: string): Promise<File | null>;
37
+ /** Remove the file. Resolves to true when it existed. */
38
+ delete(key: string): Promise<boolean>;
39
+ }
40
+ /** Throws IL6039 unless the key is safe for every store (no "..", no leading "/", no hidden files). */
41
+ export declare function checkStorageKey(key: unknown): string;
42
+ /** Throws IL6038 unless the value implements StorageStore. */
43
+ export declare function checkStorageStore(value: unknown): StorageStore | undefined;
44
+ /** The ctx.storage and app.storage API on top of a store (or one that throws IL6037 when there is none). */
45
+ export declare function createStorage(store: StorageStore | undefined): Storage;
46
+ /** Files kept in this process's memory: for tests and single-process development. Lost on restart. */
47
+ export declare function memoryStorage(): StorageStore;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * File storage: ctx.storage in routes, sockets and jobs, app.storage everywhere else.
3
+ *
4
+ * const app = createApp({ routes, storage: diskStorage({ dir: "./uploads" }) });
5
+ *
6
+ * const saved = await storage.put(`avatars/${user.id}.png`, body.avatar); // a File from t.file(), a Blob, bytes or text
7
+ * const file = await storage.get(`avatars/${user.id}.png`); // a web File, or null
8
+ * await storage.delete(`avatars/${user.id}.png`); // true when it existed
9
+ *
10
+ * The store is chosen once in createApp({ storage }): memoryStorage() (one process, tests), diskStorage({ dir })
11
+ * (a local or mounted directory), or any object that implements StorageStore.
12
+ */
13
+ import { InferloError } from "./errors.js";
14
+ const MAX_KEY_LENGTH = 1024;
15
+ const SEGMENT = /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,254}$/;
16
+ const MEDIA_TYPE = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
17
+ const DEFAULT_TYPE = "application/octet-stream";
18
+ function invalidKey(key, why) {
19
+ return new InferloError("IL6039", `Invalid storage key ${JSON.stringify(key)}: ${why}.`, {
20
+ fix: 'Use keys made of segments of letters, digits, ".", "_" and "-" (not starting with "."), separated by "/", for example `avatars/${user.id}.png`. Never use a file name sent by the client as the key.',
21
+ });
22
+ }
23
+ /** Throws IL6039 unless the key is safe for every store (no "..", no leading "/", no hidden files). */
24
+ export function checkStorageKey(key) {
25
+ if (typeof key !== "string" || key.length === 0)
26
+ throw invalidKey(key, "keys are non-empty strings");
27
+ if (key.length > MAX_KEY_LENGTH)
28
+ throw invalidKey(key.slice(0, 40) + "...", `keys are at most ${MAX_KEY_LENGTH} characters`);
29
+ for (const segment of key.split("/")) {
30
+ if (!SEGMENT.test(segment))
31
+ throw invalidKey(key, segment === "" ? 'empty segment (a leading, trailing or double "/")' : `segment "${segment}" is not allowed`);
32
+ }
33
+ return key;
34
+ }
35
+ function checkType(type) {
36
+ const lower = type.toLowerCase();
37
+ if (!MEDIA_TYPE.test(lower)) {
38
+ throw new InferloError("IL6039", `Invalid media type ${JSON.stringify(type)} for storage.put().`, {
39
+ fix: 'Pass a plain media type without parameters, for example { type: "image/png" } or "application/pdf".',
40
+ });
41
+ }
42
+ return lower;
43
+ }
44
+ async function toBytes(data) {
45
+ if (typeof data === "string")
46
+ return new TextEncoder().encode(data);
47
+ if (data instanceof Uint8Array)
48
+ return data;
49
+ if (data instanceof ArrayBuffer)
50
+ return new Uint8Array(data);
51
+ if (typeof Blob !== "undefined" && data instanceof Blob)
52
+ return new Uint8Array(await data.arrayBuffer());
53
+ throw new InferloError("IL6039", "storage.put() takes a File, Blob, Uint8Array, ArrayBuffer or string.", {
54
+ fix: "Pass the File from a t.file() field directly: await storage.put(key, body.avatar).",
55
+ });
56
+ }
57
+ function blobType(data) {
58
+ return typeof Blob !== "undefined" && data instanceof Blob && data.type ? data.type.split(";")[0].trim() : "";
59
+ }
60
+ /** Throws IL6038 unless the value implements StorageStore. */
61
+ export function checkStorageStore(value) {
62
+ if (value === undefined)
63
+ return undefined;
64
+ const store = value;
65
+ if (!store || typeof store !== "object" || typeof store.name !== "string" || !store.name || typeof store.put !== "function" || typeof store.get !== "function" || typeof store.delete !== "function") {
66
+ throw new InferloError("IL6038", "Invalid storage option in createApp.", {
67
+ fix: 'Pass a store: createApp({ storage: diskStorage({ dir: "./uploads" }) }), memoryStorage() in tests, or an object with name, put(key, bytes, type), get(key) and delete(key).',
68
+ });
69
+ }
70
+ return store;
71
+ }
72
+ /** The ctx.storage and app.storage API on top of a store (or one that throws IL6037 when there is none). */
73
+ export function createStorage(store) {
74
+ const need = () => {
75
+ if (store)
76
+ return store;
77
+ throw new InferloError("IL6037", "ctx.storage was used, but createApp has no storage option.", {
78
+ fix: 'Choose where files are kept: createApp({ storage: diskStorage({ dir: "./uploads" }) }), or memoryStorage() in tests.',
79
+ });
80
+ };
81
+ return {
82
+ store: store?.name ?? null,
83
+ async put(key, data, options = {}) {
84
+ const s = need();
85
+ checkStorageKey(key);
86
+ const type = checkType(options.type ?? (blobType(data) || DEFAULT_TYPE));
87
+ const bytes = await toBytes(data);
88
+ await s.put(key, bytes, type);
89
+ return { key, size: bytes.byteLength, type };
90
+ },
91
+ async get(key) {
92
+ const s = need();
93
+ checkStorageKey(key);
94
+ const found = await s.get(key);
95
+ if (!found)
96
+ return null;
97
+ return new File([found.bytes], key.slice(key.lastIndexOf("/") + 1), { type: found.type || DEFAULT_TYPE });
98
+ },
99
+ async delete(key) {
100
+ const s = need();
101
+ checkStorageKey(key);
102
+ return s.delete(key);
103
+ },
104
+ };
105
+ }
106
+ /** Files kept in this process's memory: for tests and single-process development. Lost on restart. */
107
+ export function memoryStorage() {
108
+ const files = new Map();
109
+ return {
110
+ name: "memory",
111
+ async put(key, bytes, type) {
112
+ files.set(key, { bytes: bytes.slice(), type });
113
+ },
114
+ async get(key) {
115
+ const found = files.get(key);
116
+ return found ? { bytes: found.bytes.slice(), type: found.type } : null;
117
+ },
118
+ async delete(key) {
119
+ return files.delete(key);
120
+ },
121
+ };
122
+ }
123
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * WebSocket plumbing shared by the app (src/app.ts) and the servers (src/serve.ts): the per-request upgrader a server
3
+ * registers, the record of upgraded requests, Deno's built-in upgrade, and the RFC 6455 frame reader and writer used
4
+ * on Node.js, where the runtime has no WebSocket server of its own. Web APIs only.
5
+ */
6
+ /** One open connection, as the app sees it: send a text message, or close with a code and reason. */
7
+ export interface SocketTransport {
8
+ send(text: string): void;
9
+ close(code: number, reason: string): void;
10
+ }
11
+ /** What the app does with a connection's events. */
12
+ export interface SocketListener {
13
+ message(data: string | Uint8Array): void;
14
+ close(code: number, reason: string): void;
15
+ }
16
+ /**
17
+ * Accepts the WebSocket upgrade of one request: connects the app's listener to the new connection and returns the
18
+ * response to answer with (Deno), or undefined when the server answers the handshake itself (Node.js, Bun).
19
+ */
20
+ export type Upgrader = (connect: (transport: SocketTransport) => SocketListener) => Promise<Response | undefined>;
21
+ /** Set by serve() for each request it can upgrade (Node.js upgrade requests, every Bun request). */
22
+ export declare const upgraders: WeakMap<Request, Upgrader>;
23
+ /** Requests the app upgraded, with Deno's upgrade response (null on Node.js and Bun, which answer it themselves). */
24
+ export declare const upgrades: WeakMap<Request, Response | null>;
25
+ /**
26
+ * On Deno, a request from Deno.serve (or deno serve), which passes its handler info with a remoteAddr to app.fetch,
27
+ * can be upgraded with Deno.upgradeWebSocket. Requests made any other way (app.request) cannot.
28
+ */
29
+ export declare function denoUpgrader(req: Request, info: unknown): Upgrader | undefined;
30
+ type BunSocket = {
31
+ data: {
32
+ connect: (transport: SocketTransport) => SocketListener;
33
+ listener?: SocketListener;
34
+ };
35
+ send(text: string): void;
36
+ close(code?: number, reason?: string): void;
37
+ };
38
+ /**
39
+ * The websocket handlers for Bun.serve, so a Bun server that runs app.fetch serves the app's WebSocket routes:
40
+ * Bun.serve({ fetch: app.fetch, websocket: bunWebSocket }). serve() and app.listen() on Bun use them already.
41
+ */
42
+ export declare const bunWebSocket: {
43
+ open(ws: BunSocket): void;
44
+ message(ws: BunSocket, data: string | Uint8Array): void;
45
+ close(ws: BunSocket, code: number, reason: string): void;
46
+ };
47
+ /** On Bun, app.fetch(req, server) gets the Bun server, whose upgrade() hands the connection to bunWebSocket. */
48
+ export declare function bunUpgrader(req: Request, server: unknown): Upgrader | undefined;
49
+ /** The Sec-WebSocket-Accept value for a client's Sec-WebSocket-Key (RFC 6455 section 4.2.2). */
50
+ export declare function acceptKey(key: string): Promise<string>;
51
+ /** True for a well-formed Sec-WebSocket-Key: 16 random bytes in base64. */
52
+ export declare function validKey(key: string | null): key is string;
53
+ export declare const OP_CONTINUATION = 0;
54
+ export declare const OP_TEXT = 1;
55
+ export declare const OP_BINARY = 2;
56
+ export declare const OP_CLOSE = 8;
57
+ export declare const OP_PING = 9;
58
+ export declare const OP_PONG = 10;
59
+ /** A complete, unmasked server frame. */
60
+ export declare function encodeFrame(opcode: number, payload: Uint8Array): Uint8Array;
61
+ /** A close frame with a status code and a reason (cut to the 123 bytes a close frame has room for). */
62
+ export declare function encodeClose(code: number, reason: string): Uint8Array;
63
+ export interface FrameEvents {
64
+ /** A complete text (string) or binary (bytes) message. */
65
+ message(data: string | Uint8Array): void;
66
+ ping(payload: Uint8Array): void;
67
+ /** The client's close frame: its code (1005 when it sent none) and reason. */
68
+ close(code: number, reason: string): void;
69
+ /** The client broke the protocol: close the connection with this code. */
70
+ error(code: number, reason: string): void;
71
+ }
72
+ /**
73
+ * Reads client frames (RFC 6455): masked, possibly fragmented, with ping, pong and close control frames in between.
74
+ * A message larger than maxMessageBytes is an error (1009), so a client cannot make the server buffer without end.
75
+ */
76
+ export declare class FrameReader {
77
+ #private;
78
+ constructor(maxMessageBytes: number, events: FrameEvents);
79
+ push(chunk: Uint8Array): void;
80
+ }
81
+ export {};
@@ -0,0 +1,251 @@
1
+ /**
2
+ * WebSocket plumbing shared by the app (src/app.ts) and the servers (src/serve.ts): the per-request upgrader a server
3
+ * registers, the record of upgraded requests, Deno's built-in upgrade, and the RFC 6455 frame reader and writer used
4
+ * on Node.js, where the runtime has no WebSocket server of its own. Web APIs only.
5
+ */
6
+ import { InferloError } from "./errors.js";
7
+ /** Set by serve() for each request it can upgrade (Node.js upgrade requests, every Bun request). */
8
+ export const upgraders = new WeakMap();
9
+ /** Requests the app upgraded, with Deno's upgrade response (null on Node.js and Bun, which answer it themselves). */
10
+ export const upgrades = new WeakMap();
11
+ /**
12
+ * On Deno, a request from Deno.serve (or deno serve), which passes its handler info with a remoteAddr to app.fetch,
13
+ * can be upgraded with Deno.upgradeWebSocket. Requests made any other way (app.request) cannot.
14
+ */
15
+ export function denoUpgrader(req, info) {
16
+ const deno = globalThis.Deno;
17
+ if (!deno?.upgradeWebSocket || typeof info !== "object" || info === null || !("remoteAddr" in info))
18
+ return undefined;
19
+ return async (connect) => {
20
+ const { socket, response } = deno.upgradeWebSocket(req);
21
+ const listener = connect({
22
+ send: (text) => {
23
+ if (socket.readyState === 1)
24
+ socket.send(text);
25
+ },
26
+ close: (code, reason) => socket.close(code, reason),
27
+ });
28
+ socket.onmessage = (event) => listener.message(typeof event.data === "string" ? event.data : new Uint8Array(event.data));
29
+ socket.onclose = (event) => listener.close(event.code, event.reason);
30
+ socket.onerror = () => { };
31
+ return response;
32
+ };
33
+ }
34
+ /**
35
+ * The websocket handlers for Bun.serve, so a Bun server that runs app.fetch serves the app's WebSocket routes:
36
+ * Bun.serve({ fetch: app.fetch, websocket: bunWebSocket }). serve() and app.listen() on Bun use them already.
37
+ */
38
+ export const bunWebSocket = {
39
+ open(ws) {
40
+ ws.data.listener = ws.data.connect({ send: (text) => ws.send(text), close: (code, reason) => ws.close(code, reason) });
41
+ },
42
+ message(ws, data) {
43
+ ws.data.listener?.message(typeof data === "string" ? data : new Uint8Array(data));
44
+ },
45
+ close(ws, code, reason) {
46
+ ws.data.listener?.close(code, reason);
47
+ },
48
+ };
49
+ /** On Bun, app.fetch(req, server) gets the Bun server, whose upgrade() hands the connection to bunWebSocket. */
50
+ export function bunUpgrader(req, server) {
51
+ if (!globalThis.Bun || typeof server?.upgrade !== "function")
52
+ return undefined;
53
+ return async (connect) => {
54
+ let upgraded;
55
+ try {
56
+ upgraded = server.upgrade(req, { data: { connect } });
57
+ }
58
+ catch {
59
+ upgraded = false;
60
+ }
61
+ if (!upgraded) {
62
+ throw new InferloError("IL2011", "This Bun server cannot accept WebSocket connections.", { status: 501, fix: 'Pass the websocket handlers to Bun.serve: Bun.serve({ fetch: app.fetch, websocket: bunWebSocket }), with bunWebSocket from "@browserlessapi/inferlo".' });
63
+ }
64
+ return undefined;
65
+ };
66
+ }
67
+ /** The Sec-WebSocket-Accept value for a client's Sec-WebSocket-Key (RFC 6455 section 4.2.2). */
68
+ export async function acceptKey(key) {
69
+ const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
70
+ let binary = "";
71
+ for (const byte of new Uint8Array(digest))
72
+ binary += String.fromCharCode(byte);
73
+ return btoa(binary);
74
+ }
75
+ /** True for a well-formed Sec-WebSocket-Key: 16 random bytes in base64. */
76
+ export function validKey(key) {
77
+ return typeof key === "string" && /^[A-Za-z0-9+/]{22}==$/.test(key);
78
+ }
79
+ export const OP_CONTINUATION = 0x0;
80
+ export const OP_TEXT = 0x1;
81
+ export const OP_BINARY = 0x2;
82
+ export const OP_CLOSE = 0x8;
83
+ export const OP_PING = 0x9;
84
+ export const OP_PONG = 0xa;
85
+ /** A complete, unmasked server frame. */
86
+ export function encodeFrame(opcode, payload) {
87
+ const length = payload.byteLength;
88
+ const headerLength = length < 126 ? 2 : length < 65536 ? 4 : 10;
89
+ const out = new Uint8Array(headerLength + length);
90
+ out[0] = 0x80 | opcode;
91
+ if (length < 126)
92
+ out[1] = length;
93
+ else if (length < 65536) {
94
+ out[1] = 126;
95
+ out[2] = length >> 8;
96
+ out[3] = length & 0xff;
97
+ }
98
+ else {
99
+ out[1] = 127;
100
+ new DataView(out.buffer).setBigUint64(2, BigInt(length));
101
+ }
102
+ out.set(payload, headerLength);
103
+ return out;
104
+ }
105
+ /** A close frame with a status code and a reason (cut to the 123 bytes a close frame has room for). */
106
+ export function encodeClose(code, reason) {
107
+ const text = new TextEncoder().encode(reason).subarray(0, 123);
108
+ const payload = new Uint8Array(2 + text.byteLength);
109
+ payload[0] = code >> 8;
110
+ payload[1] = code & 0xff;
111
+ payload.set(text, 2);
112
+ return encodeFrame(OP_CLOSE, payload);
113
+ }
114
+ /**
115
+ * Reads client frames (RFC 6455): masked, possibly fragmented, with ping, pong and close control frames in between.
116
+ * A message larger than maxMessageBytes is an error (1009), so a client cannot make the server buffer without end.
117
+ */
118
+ export class FrameReader {
119
+ #buffer = new Uint8Array(0);
120
+ #parts = [];
121
+ #size = 0;
122
+ #opcode = -1;
123
+ #done = false;
124
+ #max;
125
+ #events;
126
+ #decoder = new TextDecoder("utf-8", { fatal: true });
127
+ constructor(maxMessageBytes, events) {
128
+ this.#max = maxMessageBytes;
129
+ this.#events = events;
130
+ }
131
+ push(chunk) {
132
+ if (this.#done)
133
+ return;
134
+ if (this.#buffer.byteLength === 0)
135
+ this.#buffer = chunk;
136
+ else {
137
+ const joined = new Uint8Array(this.#buffer.byteLength + chunk.byteLength);
138
+ joined.set(this.#buffer);
139
+ joined.set(chunk, this.#buffer.byteLength);
140
+ this.#buffer = joined;
141
+ }
142
+ while (!this.#done && this.#next())
143
+ ;
144
+ }
145
+ #fail(code, reason) {
146
+ this.#done = true;
147
+ this.#events.error(code, reason);
148
+ return false;
149
+ }
150
+ /** Reads one frame from the buffer; false when more bytes are needed or reading stopped. */
151
+ #next() {
152
+ const b = this.#buffer;
153
+ if (b.byteLength < 2)
154
+ return false;
155
+ const fin = (b[0] & 0x80) !== 0;
156
+ const opcode = b[0] & 0x0f;
157
+ if ((b[0] & 0x70) !== 0)
158
+ return this.#fail(1002, "Extensions are not supported");
159
+ if ((b[1] & 0x80) === 0)
160
+ return this.#fail(1002, "Client frames must be masked");
161
+ let length = b[1] & 0x7f;
162
+ let offset = 2;
163
+ if (length === 126) {
164
+ if (b.byteLength < 4)
165
+ return false;
166
+ length = (b[2] << 8) | b[3];
167
+ offset = 4;
168
+ }
169
+ else if (length === 127) {
170
+ if (b.byteLength < 10)
171
+ return false;
172
+ const big = new DataView(b.buffer, b.byteOffset + 2, 8).getBigUint64(0);
173
+ if (big > BigInt(this.#max))
174
+ return this.#fail(1009, "Message too big");
175
+ length = Number(big);
176
+ offset = 10;
177
+ }
178
+ const control = opcode >= 0x8;
179
+ if (control && (!fin || length > 125))
180
+ return this.#fail(1002, "Invalid control frame");
181
+ if (![OP_CONTINUATION, OP_TEXT, OP_BINARY, OP_CLOSE, OP_PING, OP_PONG].includes(opcode))
182
+ return this.#fail(1002, "Unknown opcode");
183
+ if (!control && this.#size + length > this.#max)
184
+ return this.#fail(1009, "Message too big");
185
+ if (b.byteLength < offset + 4 + length)
186
+ return false;
187
+ const mask = b.subarray(offset, offset + 4);
188
+ const payload = b.slice(offset + 4, offset + 4 + length);
189
+ for (let i = 0; i < payload.byteLength; i++)
190
+ payload[i] ^= mask[i & 3];
191
+ this.#buffer = b.subarray(offset + 4 + length);
192
+ if (opcode === OP_PING)
193
+ this.#events.ping(payload);
194
+ else if (opcode === OP_PONG) {
195
+ // Answers to pings; nothing to do.
196
+ }
197
+ else if (opcode === OP_CLOSE) {
198
+ if (payload.byteLength === 1)
199
+ return this.#fail(1002, "Invalid close frame");
200
+ const code = payload.byteLength >= 2 ? (payload[0] << 8) | payload[1] : 1005;
201
+ let reason = "";
202
+ try {
203
+ reason = this.#decoder.decode(payload.subarray(2));
204
+ }
205
+ catch {
206
+ return this.#fail(1007, "Close reason is not UTF-8");
207
+ }
208
+ this.#done = true;
209
+ this.#events.close(code, reason);
210
+ return false;
211
+ }
212
+ else {
213
+ if (opcode === OP_CONTINUATION ? this.#opcode === -1 : this.#opcode !== -1)
214
+ return this.#fail(1002, "Unexpected frame order");
215
+ if (opcode !== OP_CONTINUATION)
216
+ this.#opcode = opcode;
217
+ this.#parts.push(payload);
218
+ this.#size += length;
219
+ if (fin) {
220
+ const data = this.#parts.length === 1 ? this.#parts[0] : concat(this.#parts, this.#size);
221
+ const kind = this.#opcode;
222
+ this.#parts = [];
223
+ this.#size = 0;
224
+ this.#opcode = -1;
225
+ if (kind === OP_TEXT) {
226
+ let text;
227
+ try {
228
+ text = this.#decoder.decode(data);
229
+ }
230
+ catch {
231
+ return this.#fail(1007, "Text message is not UTF-8");
232
+ }
233
+ this.#events.message(text);
234
+ }
235
+ else
236
+ this.#events.message(data);
237
+ }
238
+ }
239
+ return true;
240
+ }
241
+ }
242
+ function concat(parts, size) {
243
+ const out = new Uint8Array(size);
244
+ let at = 0;
245
+ for (const part of parts) {
246
+ out.set(part, at);
247
+ at += part.byteLength;
248
+ }
249
+ return out;
250
+ }
251
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -11,6 +11,7 @@ Key rules: routes are private unless `auth: "public"`; validate input with `t.*`
11
11
  - [Schemas](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/schema.md): t.* types, modifiers, validation, JSON Schema
12
12
  - [Models and database](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/database.md): model(), queries, adapters, db push, transactions
13
13
  - [Routes](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/routes.md): options, handler context, return values, errors, middleware, deprecated routes (Deprecation and Sunset headers), API versions with apiVersion("v1", routes) and an OpenAPI document per version
14
+ - [WebSockets](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/websockets.md): route.ws(path, { message, auth, rateLimit }).handle(fn) with the same auth, permissions and rate limits as HTTP routes, JSON messages validated and handled in order, ctx.socket, origin checks, on Node.js, Bun and Deno
14
15
  - [Auth](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/auth.md): auth.resolve, roles, permissions() with can() and authorize() and the route permission option, bearer API keys, passwords
15
16
  - [AI](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/ai.md): providers, aliases and fallback, structured output, streaming, embeddings, usage and budgets
16
17
  - [Modules](https://github.com/BrowserlessAPI/Inferlo/blob/main/docs/modules.md): module("billing", { imports, models, routes, jobs, provides }) splits a large app into feature modules with explicit dependencies, createApp({ modules }), checked at startup and shown in describe