palbase 0.0.0-stage → 0.2.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 (109) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +2 -2
  3. package/dist/ai/index.js +17 -0
  4. package/dist/backend/engine.js +2 -0
  5. package/dist/backend/env.js +1 -0
  6. package/dist/backend/index.js +12 -0
  7. package/dist/backend/stack.js +1 -0
  8. package/dist/backend/test.js +2 -0
  9. package/dist/chunks/5W6XJM46.js +13 -0
  10. package/dist/chunks/6OPBXB7Z.js +2 -0
  11. package/dist/chunks/7LOMYBLN.js +20 -0
  12. package/dist/chunks/AN5LCF5K.js +55 -0
  13. package/dist/chunks/ECUYQ73P.js +2 -0
  14. package/dist/chunks/HDX6YUAB.js +2 -0
  15. package/dist/chunks/HVC2C35F.js +2 -0
  16. package/dist/chunks/I3JNX6MZ.js +4 -0
  17. package/dist/chunks/JEJ5CFTK.js +2 -0
  18. package/dist/chunks/K3C2IEDS.js +2 -0
  19. package/dist/chunks/KRBVYYME.js +2 -0
  20. package/dist/chunks/NIJHTHNF.js +2 -0
  21. package/dist/chunks/TL4H6V3P.js +2 -0
  22. package/dist/chunks/UTRQWCMD.js +6 -0
  23. package/dist/chunks/ZFPDYRRY.js +2 -0
  24. package/dist/cli.js +815 -0
  25. package/dist/refuse/ai.js +2 -0
  26. package/dist/refuse/backend.js +2 -0
  27. package/dist/refuse/server.js +2 -0
  28. package/dist/server/index.js +2 -0
  29. package/dist/server/testing.js +2 -0
  30. package/dist/web/index.js +2 -0
  31. package/dist/web/palbe.js +2 -0
  32. package/dist/web/paywall.js +2 -0
  33. package/dist/web/vite.js +3 -0
  34. package/package.json +111 -4
  35. package/types/ai/src/ai.d.ts +178 -0
  36. package/types/ai/src/index.d.ts +5 -0
  37. package/types/ai/src/partial.d.ts +1 -0
  38. package/types/ai/src/schema.d.ts +47 -0
  39. package/types/backend/src/bootstrap.d.ts +72 -0
  40. package/types/backend/src/cache.d.ts +32 -0
  41. package/types/backend/src/container.d.ts +49 -0
  42. package/types/backend/src/database.d.ts +148 -0
  43. package/types/backend/src/db-env.d.ts +4 -0
  44. package/types/backend/src/decorators.d.ts +293 -0
  45. package/types/backend/src/engine-db.d.ts +24 -0
  46. package/types/backend/src/engine-exports.d.ts +3 -0
  47. package/types/backend/src/engine.d.ts +124 -0
  48. package/types/backend/src/env.d.ts +27 -0
  49. package/types/backend/src/errors.d.ts +70 -0
  50. package/types/backend/src/i18n.d.ts +21 -0
  51. package/types/backend/src/index.d.ts +23 -0
  52. package/types/backend/src/openapi.d.ts +73 -0
  53. package/types/backend/src/platform-calls.d.ts +41 -0
  54. package/types/backend/src/requests.d.ts +81 -0
  55. package/types/backend/src/rooms.d.ts +168 -0
  56. package/types/backend/src/router.d.ts +22 -0
  57. package/types/backend/src/runtime.d.ts +56 -0
  58. package/types/backend/src/services.d.ts +262 -0
  59. package/types/backend/src/stack.d.ts +20 -0
  60. package/types/backend/src/strings-files.d.ts +3 -0
  61. package/types/backend/src/testing/index.d.ts +3 -0
  62. package/types/backend/src/testing/unit.d.ts +66 -0
  63. package/types/backend/src/upload.d.ts +125 -0
  64. package/types/backend/src/webhooks.d.ts +113 -0
  65. package/types/internal/src/event.d.ts +9 -0
  66. package/types/internal/src/modules/ai.d.ts +678 -0
  67. package/types/internal/src/modules/analytics.d.ts +1414 -0
  68. package/types/internal/src/modules/auth.d.ts +5660 -0
  69. package/types/internal/src/modules/cache.d.ts +158 -0
  70. package/types/internal/src/modules/events.d.ts +19 -0
  71. package/types/internal/src/modules/flags.d.ts +957 -0
  72. package/types/internal/src/modules/index.d.ts +13 -0
  73. package/types/internal/src/modules/machines.d.ts +1584 -0
  74. package/types/internal/src/modules/notify.d.ts +1450 -0
  75. package/types/internal/src/modules/purchases.d.ts +4113 -0
  76. package/types/internal/src/modules/realtime.d.ts +326 -0
  77. package/types/internal/src/modules/secrets.d.ts +275 -0
  78. package/types/internal/src/modules/storage.d.ts +691 -0
  79. package/types/internal/src/modules/stores.d.ts +1516 -0
  80. package/types/internal/src/retry.d.ts +14 -0
  81. package/types/internal/src/transport.d.ts +106 -0
  82. package/types/server/src/auth/dpop.d.ts +47 -0
  83. package/types/server/src/auth/jwt.d.ts +115 -0
  84. package/types/server/src/index.d.ts +9 -0
  85. package/types/server/src/pages.d.ts +4 -0
  86. package/types/server/src/purchases.d.ts +51 -0
  87. package/types/server/src/secrets-env.d.ts +22 -0
  88. package/types/server/src/server.d.ts +40 -0
  89. package/types/server/src/testing.d.ts +91 -0
  90. package/types/server/src/webhooks.d.ts +72 -0
  91. package/types/web/src/analytics.d.ts +87 -0
  92. package/types/web/src/auth.d.ts +155 -0
  93. package/types/web/src/backend-error.d.ts +38 -0
  94. package/types/web/src/backend-names.d.ts +25 -0
  95. package/types/web/src/backends.d.ts +84 -0
  96. package/types/web/src/client.d.ts +51 -0
  97. package/types/web/src/index.d.ts +13 -0
  98. package/types/web/src/palbe.d.ts +2 -0
  99. package/types/web/src/paywall/icons.d.ts +5 -0
  100. package/types/web/src/paywall/index.d.ts +5 -0
  101. package/types/web/src/paywall/placement.d.ts +85 -0
  102. package/types/web/src/paywall/prices.d.ts +40 -0
  103. package/types/web/src/paywall/render.d.ts +54 -0
  104. package/types/web/src/paywall/resolve.d.ts +149 -0
  105. package/types/web/src/purchases.d.ts +86 -0
  106. package/types/web/src/rooms.d.ts +61 -0
  107. package/types/web/src/session.d.ts +91 -0
  108. package/types/web/src/socket.d.ts +87 -0
  109. package/types/web/src/vite.d.ts +49 -0
@@ -0,0 +1,70 @@
1
+ import { HttpError, type FieldError } from 'palgres';
2
+ import type { StandardSchemaV1 } from './decorators.js';
3
+ export { BadRequest, Conflict, DeadlockDetected, HttpError, NotFound, SerializationFailure, TooManyRequests, UniqueViolation } from 'palgres';
4
+ export type FieldIssue = FieldError;
5
+ export declare function isHttpError(e: unknown): e is HttpError;
6
+ /** Any refusal: its status, code and description of the caller's choosing. */
7
+ export declare class PalError extends HttpError {
8
+ constructor(status: number, code: string, description: string, data?: unknown);
9
+ }
10
+ export declare class Unauthorized extends HttpError {
11
+ constructor(message?: string, code?: string, data?: unknown);
12
+ }
13
+ export declare class Forbidden extends HttpError {
14
+ constructor(message?: string, code?: string, data?: unknown);
15
+ }
16
+ export interface Envelope {
17
+ error: string;
18
+ error_description: string;
19
+ status: number;
20
+ request_id: string;
21
+ data?: unknown;
22
+ }
23
+ export declare function envelope(e: HttpError, requestId: string): Envelope;
24
+ /** One error a release can throw by its wire code: what the contract types it as. */
25
+ export interface RegisteredError {
26
+ code: string;
27
+ status: number;
28
+ /** The class name the analysis reads at a throw site. */
29
+ className: string;
30
+ data?: StandardSchemaV1;
31
+ builtin: boolean;
32
+ }
33
+ /** The errors of a release that defined none of its own: the named refusals, by code. */
34
+ export declare function createErrorRegistry(): Map<string, RegisteredError>;
35
+ /** The registry defineError writes to, by wire code. */
36
+ export declare function errorRegistry(): Map<string, RegisteredError>;
37
+ /** What a schema takes. */
38
+ type InputOf<S> = S extends {
39
+ readonly '~standard': {
40
+ readonly types?: {
41
+ readonly input: infer I;
42
+ };
43
+ };
44
+ } ? I : unknown;
45
+ /** A defined error that carries data: `new RoomLocked({ retryAfter: 30 })`. */
46
+ export interface DefinedErrorWithData<S extends StandardSchemaV1> {
47
+ new (data: InputOf<S>, message?: string): HttpError;
48
+ readonly code: string;
49
+ readonly status: number;
50
+ }
51
+ /** A defined error without data: `new RoomGone()`. */
52
+ export interface DefinedError {
53
+ new (message?: string): HttpError;
54
+ readonly code: string;
55
+ readonly status: number;
56
+ }
57
+ /**
58
+ * A project's own error, as v2 defined one: its wire code, its status and,
59
+ * when it carries data, the data's schema — what every generated client
60
+ * types it as (`catch .roomLocked(let data)`). Called at a module's top level
61
+ * with literals: the build reads them where it is thrown.
62
+ *
63
+ * export const RoomLocked = defineError("room_locked", 409, z.object({ retryAfter: z.number() }));
64
+ * throw new RoomLocked({ retryAfter: 30 });
65
+ *
66
+ * A code is the project's once: defined again with another status or data, it
67
+ * is refused (a client matches an error by its code).
68
+ */
69
+ export declare function defineError<S extends StandardSchemaV1>(code: string, status: number, data: S): DefinedErrorWithData<S>;
70
+ export declare function defineError(code: string, status: number): DefinedError;
@@ -0,0 +1,21 @@
1
+ /** The app's strings: its source language, its languages, and each key's cell per language. */
2
+ export interface StringsTable {
3
+ version: 1;
4
+ source: string;
5
+ locales: string[];
6
+ strings: Record<string, Record<string, {
7
+ value: string;
8
+ state: 'translated' | 'needs_review' | 'missing';
9
+ }>>;
10
+ }
11
+ /** The languages a request asks for, best first — each tag and then its base language — and the source last. */
12
+ export declare function chainFromAcceptLanguage(header: string | null, source: string | undefined): string[];
13
+ /** The key's text in the first language of the chain that has it — the key itself in the source language. */
14
+ export declare function lookup(table: StringsTable, key: string, chain: readonly string[]): {
15
+ text: string;
16
+ locale: string;
17
+ };
18
+ /** {{name}} filled from vars ({{{name}}} left as it is); a value not given is empty. */
19
+ export declare function interpolate(text: string, vars?: Record<string, unknown>): string;
20
+ /** The sentence `key` (written in the source language) in the caller's language, {{name}} filled. */
21
+ export declare function t(key: string, vars?: Record<string, string | number | boolean | null | undefined>): string;
@@ -0,0 +1,23 @@
1
+ import 'reflect-metadata';
2
+ export { Body, Client, Controller, Delete, Get, getRegisteredControllers, getRoutes, Headers, Injectable, Job, Module, On, OptionalUser, Param, Patch, Post, Put, Query, QueryParams, Req, RequestId, Resolve, TraceId, User, Webhook, type AuthConfig, type AuthSpec, type ControllerOptions, type EventHandler, type JobMeta, type JobOptions, type ModuleDef, type OnOptions, type ParamResolver, type RateLimitConfig, type ResolveContext, type RouteInfo, type RouteOptions, type SignatureSpec, type StandardSchemaV1, type Token, type WebhookOptions, type WebhookProvider, } from './decorators.js';
3
+ export { Upload, UploadedObject, type UploadConfig } from './upload.js';
4
+ export { OnAuthorize, OnEmpty, OnFirst, OnJoin, OnLeave, OnMessage, Room, type RoomAuthorizeContext, type RoomContext, type RoomGrant, type RoomMessageContext, type RoomOptions, type RoomUser, } from './rooms.js';
5
+ export { getWebhookManifest, type WebhookEventHandler, type WebhookMeta } from './webhooks.js';
6
+ export { DiError, type DiErrorKind } from './container.js';
7
+ export { BadRequest, Conflict, DeadlockDetected, Forbidden, HttpError, NotFound, PalError, SerializationFailure, TooManyRequests, Unauthorized, UniqueViolation, defineError, type DefinedError, type DefinedErrorWithData, type Envelope, type FieldIssue, } from './errors.js';
8
+ export { createApp, MAX_BODY_BYTES, type App, type AppOptions, type AppTokens, type ClientInfo, type CorsOptions, type UserT } from './engine.js';
9
+ export { ContractRunEnded, MigrationRunEnded, PalbaseFactory, type AppConfig, type PalbaseApplication } from './bootstrap.js';
10
+ export { SchemaNotPushed } from './database.js';
11
+ export type { DatabaseConfig } from './database.js';
12
+ export { bigint, boolean, check, date, defineSchema, defineTable, enumType, foreignKey, index, integer, jsonb, numeric, ownedByUser, policy, raw, sqlType, text, timestamp, userRef, uuid, type AnyColumn, type ColumnMap, type PolicyExprCtx, type SchemaDef, type TableDef, type TableHandle, type TableRef, } from 'palgres/dsl';
13
+ export type { Embed, TablesOf } from 'palgres';
14
+ export { increment, now } from 'palgres';
15
+ export type { WhereFilter } from 'palgres';
16
+ export type { PalbaseConnection } from './env.js';
17
+ export { Database, Log, Palbase, type AtomicDatabase, type Logger } from './runtime.js';
18
+ export { Flags, Notifications, Realtime, Secrets, Storage, type EnvBuckets, type PalbaseBatchOverrideOperation, type PalbaseBatchSetOverridesResult, type PalbaseBucketClient, type StorageService, type PalbaseClearAllOverridesResult, type PalbaseClearOverrideResult, type PalbaseEmailSendParams, type PalbaseEmailSendResponse, type PalbaseFileObject, type PalbaseFlag, type PalbaseFlagContext, type PalbaseFlagSource, type PalbaseFlagValue, type PalbaseFlagVariant, type PalbaseFlagsServiceClient, type PalbaseListOptions, type PalbaseLocalizedText, type PalbasePushRecipient, type PalbasePushSendParams, type PalbasePushSendResponse, type PalbaseResult, type PalbaseSetOverrideResult, type PalbaseSetOverridesResult, type PalbaseSignedUrlResponse, type PalbaseUploadOptions, type TypedBucketClient, } from './services.js';
19
+ export { Cache, CACHE_MAX_TTL, type CacheClient } from './cache.js';
20
+ export { t, type StringsTable } from './i18n.js';
21
+ export { AuthUnavailable, TokenError, TokenVerifier, type SessionStanding, type TokenRefusal, type TokenUser } from '../../server/src/index.js';
22
+ export { EVENT_TYPES, PalbaseError, type EventType, type PalbaseEvent, type PlatformEvent, type PlatformEvents, type WebhookEvent } from '../../server/src/index.js';
23
+ export { z } from 'zod';
@@ -0,0 +1,73 @@
1
+ import type { RateLimitConfig, StandardSchemaV1 } from './decorators.js';
2
+ import type { RegisteredError } from './errors.js';
3
+ import type { RoomDeclaration } from './rooms.js';
4
+ import type { Route } from './router.js';
5
+ /** Where the platform delivers the project's events to the app. */
6
+ export declare const PLATFORM_EVENTS_PATH = "/webhooks/palbase";
7
+ /** What the engine decided for a route: the document says what it enforces. */
8
+ export interface RouteFacts {
9
+ auth: {
10
+ required: boolean;
11
+ verifiedEmail: boolean;
12
+ };
13
+ /** The limit the route spends, or false when it has none. */
14
+ limit: RateLimitConfig | false;
15
+ }
16
+ export interface ContractOptions {
17
+ /** The app's database runs each request as its caller's identity (or keeps its claims). */
18
+ databaseSeesCaller: boolean;
19
+ /** The app has a database: its writes take an Idempotency-Key. */
20
+ idempotency?: boolean;
21
+ /** The app's @Webhook classes, as the engine serves them. */
22
+ webhooks?: readonly WebhookFacts[];
23
+ /** The project's events the app takes at POST /webhooks/palbase: its @On types, and user.deleted when its tables keep users. */
24
+ platformEvents?: readonly string[];
25
+ /** The buckets whose storage events (file.*) those handlers take; undefined when one of them takes every bucket's. */
26
+ platformEventBuckets?: readonly string[];
27
+ /**
28
+ * What the app's source says a route answers and throws — read from its
29
+ * types when its contract is written (contract/analyze.ts) — or undefined
30
+ * for a route it says nothing of: that route answers JSON of no schema.
31
+ */
32
+ routes?: (route: Route) => RouteContract | undefined;
33
+ /** The release's errors by wire code (defineError's registry): what a thrown code is typed as. */
34
+ errors?: ReadonlyMap<string, RegisteredError>;
35
+ /** The app's @Room classes, as their decorators declare them. */
36
+ rooms?: readonly RoomDeclaration[];
37
+ }
38
+ /** What a route answers and throws, as its source declares. */
39
+ export interface RouteContract {
40
+ /** void: it answers nothing (204); raw: the Response its handler built; otherwise the schema of its JSON answer, or of each item of it. */
41
+ returns: {
42
+ kind: 'void';
43
+ } | {
44
+ kind: 'raw';
45
+ } | {
46
+ kind: 'json';
47
+ schema: StandardSchemaV1;
48
+ array: boolean;
49
+ };
50
+ /** Every error the route can throw, in the order it is first thrown. */
51
+ throws: readonly ThrownError[];
52
+ }
53
+ /** One error a route throws: the name every client gives its case, its wire code and status. */
54
+ export interface ThrownError {
55
+ name: string;
56
+ code: string;
57
+ status: number;
58
+ }
59
+ /** A @Webhook as the engine serves it: POST /webhooks/<name>. */
60
+ export interface WebhookFacts {
61
+ name: string;
62
+ /** The class declaring it. */
63
+ cls: string;
64
+ provider?: string;
65
+ /** The header a signature spelled out arrives in. */
66
+ signatureHeader?: string;
67
+ /** The env var its secret is read from. */
68
+ secretEnv: string;
69
+ events: string[];
70
+ }
71
+ type Json = Record<string, unknown>;
72
+ export declare function openapiDocument(table: readonly Route[], factsOf: (route: Route) => RouteFacts, opts: ContractOptions): Json;
73
+ export {};
@@ -0,0 +1,41 @@
1
+ export declare const UPLOAD_AUTHORIZE_PATH = "/_palbase/storage/upload/authorize";
2
+ export declare const UPLOAD_COMPLETE_PATH = "/_palbase/storage/upload/complete";
3
+ export declare const ROOMS_PATH = "/_palbase/realtime/rooms";
4
+ /**
5
+ * The most a platform's call carries: the machines module refuses to send
6
+ * more (an upload's completion — its client's body and the object — is the
7
+ * largest).
8
+ */
9
+ export declare const PLATFORM_CALL_MAX: number;
10
+ /** The caller, as the module that checked its token names it. */
11
+ export interface SignedUser {
12
+ /** The user's id; absent when nobody is signed in. */
13
+ id?: string;
14
+ session?: string;
15
+ /** anon, authenticated, or service_role for a call made with the project's secret key. */
16
+ role: string;
17
+ /** The user's email and whether it is verified, as auth said when it checked the token; absent when it said none. */
18
+ email?: string;
19
+ emailVerified?: boolean;
20
+ }
21
+ /** The `user` of a call, or undefined when it is not one. */
22
+ export declare function signedUserOf(v: unknown): SignedUser | undefined;
23
+ /** The caller's address as the edge saw it, which the module names beside the user ("client"), or undefined. */
24
+ export declare function clientOf(v: unknown): string | undefined;
25
+ /**
26
+ * Who a platform's call is counted against, for a route's limit: the caller
27
+ * the module names in the signed call — the user, the session of one signed
28
+ * in to no one, or else the address the edge saw it come from (client) — as a
29
+ * request's own is counted by its user or its address. Never the
30
+ * connection's address, which is the platform's and every caller's alike,
31
+ * nor a header, which is anyone's to send. Named by none of them, the callers
32
+ * of one role share the route's limit: a limit each could step around by
33
+ * saying something of itself is none.
34
+ */
35
+ export declare function limitKeyOf(user: SignedUser, client?: string): string;
36
+ /**
37
+ * The claims the request's database is told for a signed-in caller — what
38
+ * auth.uid() reads (sub), its role and session — or null for nobody: what a
39
+ * token's claims would say of the same user, of what the call names.
40
+ */
41
+ export declare function claimsOf(user: SignedUser | undefined): Record<string, unknown> | null;
@@ -0,0 +1,81 @@
1
+ export interface AnsweredRequest {
2
+ requestId: string;
3
+ method: string;
4
+ /** The route's path as declared ("/orgs/{id}", without its method), or the path when no route took it. */
5
+ endpoint: string;
6
+ /** The path as asked. */
7
+ path: string;
8
+ status: number;
9
+ durationMs: number;
10
+ /** The signed-in caller, when there was one. */
11
+ userId?: string;
12
+ }
13
+ /** Where traces go: analytics' batch of request traces, as the project's server. */
14
+ export interface TraceSink {
15
+ sendTraceBatch(input: {
16
+ body: {
17
+ traces: TraceBody[];
18
+ };
19
+ signal?: AbortSignal;
20
+ }): Promise<unknown>;
21
+ }
22
+ export interface TraceBody {
23
+ request_id: string;
24
+ source: 'backend';
25
+ endpoint: string;
26
+ method: string;
27
+ status: number;
28
+ duration_ms: number;
29
+ timestamp: number;
30
+ distinct_id?: string;
31
+ }
32
+ export interface RequestTelemetryOptions {
33
+ /** Lines a second the request log writes before it counts instead (the machine's log keeps 1200 a minute). */
34
+ linesPerSecond?: number;
35
+ /** Traces sent in one batch (analytics takes 100). */
36
+ batchSize?: number;
37
+ /** How long a trace waits for its batch to fill. */
38
+ flushMs?: number;
39
+ /** Traces held while analytics does not take them; past this the oldest go. */
40
+ maxPending?: number;
41
+ /** How long one batch's call may take, its retries included (default 10 000 ms); past it, the call is let go and its traces dropped. */
42
+ sendMs?: number;
43
+ /** How long a flush waits for analytics in all (default 2000 ms); past it, what is not sent is dropped, and said. */
44
+ shutdownMs?: number;
45
+ now?: () => number;
46
+ }
47
+ export declare class RequestTelemetry {
48
+ private readonly linesPerSecond;
49
+ private readonly batchSize;
50
+ private readonly flushMs;
51
+ private readonly maxPending;
52
+ private readonly sendMs;
53
+ private readonly shutdownMs;
54
+ private readonly now;
55
+ private tokens;
56
+ private refilled;
57
+ private unlogged;
58
+ private unloggedSince;
59
+ private pending;
60
+ private timer;
61
+ private sending;
62
+ private inFlight;
63
+ private pausedUntil;
64
+ private lastSendWarning;
65
+ private readonly sink;
66
+ constructor(sink: TraceSink | undefined, opts?: RequestTelemetryOptions);
67
+ record(r: AnsweredRequest): void;
68
+ /**
69
+ * Sends what is held, and waits for it — the app's shutdown, a test — for
70
+ * shutdownMs at most. When that is up, the call analytics has not answered
71
+ * is let go and what is still held is dropped, said once with how many: a
72
+ * connection hung on the way would otherwise hold the process until it is
73
+ * killed, and the database's close with it.
74
+ */
75
+ flush(): Promise<void>;
76
+ private letGo;
77
+ private log;
78
+ private trace;
79
+ private schedule;
80
+ private send;
81
+ }
@@ -0,0 +1,168 @@
1
+ import type { realtime } from '../../internal/src/modules/index.js';
2
+ import { type StandardSchemaV1 } from './decorators.js';
3
+ import { type SignedUser } from './platform-calls.js';
4
+ export declare const ROOM: unique symbol;
5
+ export declare const ROOM_HOOKS: unique symbol;
6
+ /** The moments a room's methods are told of. */
7
+ export type RoomHook = 'authorize' | 'first' | 'join' | 'leave' | 'empty';
8
+ export interface RoomOptions {
9
+ /**
10
+ * What the room sends its members, by event name, each its payload's
11
+ * schema: the room's public surface, typed in every generated client. A
12
+ * room emits from anywhere in its class, so there is no return type to read
13
+ * them from — they are declared.
14
+ */
15
+ events: Record<string, StandardSchemaV1>;
16
+ /**
17
+ * How long the room is held after its last member left before it is empty
18
+ * (@OnEmpty), in milliseconds: 60000 unless said. Raise it for an upstream
19
+ * that is expensive to start again; one who comes back within it finds the
20
+ * room as it was.
21
+ */
22
+ graceMs?: number;
23
+ }
24
+ /** A room as its decorators declare it. */
25
+ export interface RoomDeclaration {
26
+ pattern: string;
27
+ events: Record<string, StandardSchemaV1>;
28
+ graceMs: number;
29
+ /** Each moment, the method told of it. */
30
+ hooks: Partial<Record<RoomHook, string>>;
31
+ /** Each message a member may send: its method, and its payload's schema — an object of no prototype, whatever the names. */
32
+ messages: Record<string, {
33
+ fn: string;
34
+ schema: StandardSchemaV1;
35
+ }>;
36
+ }
37
+ /** Who a member is, to a room's methods. */
38
+ export interface RoomUser {
39
+ /** The user's id; "" in @OnFirst and @OnEmpty, which no one in particular caused. */
40
+ id: string;
41
+ }
42
+ /** What @OnAuthorize is given: who asks to join, and the channel's parameters. */
43
+ export interface RoomAuthorizeContext {
44
+ user: RoomUser;
45
+ params: Readonly<Record<string, string>>;
46
+ }
47
+ /**
48
+ * What @OnAuthorize answers: true lets the user in to hear and to send; an
49
+ * object says which — { read: false } is no way in (admitted to hear nothing
50
+ * is not admitted), { write: false } a listener who may not send; null,
51
+ * undefined or false keep the user out. A room with no @OnAuthorize lets
52
+ * nobody in, and one whose @OnAuthorize throws lets that user in nowhere.
53
+ * So does any other answer — a number, a string, an object that says
54
+ * neither read nor write ({} too): only a grant lets anyone in, and the
55
+ * app's log says what was answered instead.
56
+ */
57
+ export type RoomGrant = boolean | null | undefined | {
58
+ read?: boolean;
59
+ write?: boolean;
60
+ };
61
+ /** What a room's moments are given (@OnFirst, @OnJoin, @OnLeave, @OnEmpty). */
62
+ export interface RoomContext {
63
+ /** The channel: "ai:42". */
64
+ room: string;
65
+ /** The pattern's parameters: { sessionId: "42" }. */
66
+ params: Readonly<Record<string, string>>;
67
+ /**
68
+ * The room's own: aborted when it empties (before @OnEmpty runs), or when
69
+ * owner.release() gives it up — what @OnFirst started stops on it.
70
+ */
71
+ signal: AbortSignal;
72
+ /** Its members now, across realtime's replicas; 0 in @OnMessage. */
73
+ count: number;
74
+ user: RoomUser;
75
+ /**
76
+ * An event to the room's members — in @OnJoin, to the member who joined
77
+ * alone. Sent in the order emitted, and never thrown: realtime is lossy,
78
+ * and what could not be sent is in the app's log.
79
+ */
80
+ emit(event: string, payload?: unknown): void;
81
+ owner: {
82
+ /** Runs `work` past the method, until the room's signal aborts: an upstream's loop. Its failure is logged, never the app's crash. */
83
+ hold(work: (signal: AbortSignal) => unknown): void;
84
+ /** Gives the room up now: its signal aborts, and the next member to come finds it unheld (@OnFirst again). */
85
+ release(): void;
86
+ };
87
+ }
88
+ /** What @OnMessage is given: the room's context, the member, and the payload as its schema made it. */
89
+ export interface RoomMessageContext<P = unknown> extends RoomContext {
90
+ payload: P;
91
+ }
92
+ /**
93
+ * A class whose methods manage the room `pattern` names. It is a provider a
94
+ * module lists (its constructor injected as any provider's), built once: what
95
+ * @OnFirst starts and @OnEmpty stops lives in it.
96
+ *
97
+ * @Room('ai:{sessionId}', { events: { token: Token } })
98
+ * class AiRoom {
99
+ * @OnAuthorize() can({ user, params }: RoomAuthorizeContext): RoomGrant { … }
100
+ * @OnFirst() open(ctx: RoomContext) { ctx.owner.hold((signal) => this.stream(ctx, signal)); }
101
+ * @OnEmpty() close(ctx: RoomContext) { … }
102
+ * @OnMessage('say', Say) say(ctx: RoomMessageContext<Say>) { … }
103
+ * }
104
+ */
105
+ export declare function Room(pattern: string, options: RoomOptions): ClassDecorator;
106
+ /** Who may join: answers a RoomGrant (RoomAuthorizeContext). No @OnAuthorize, and nobody joins. */
107
+ export declare const OnAuthorize: () => MethodDecorator;
108
+ /**
109
+ * The room is occupied and held by no one: start what it needs. Told once
110
+ * across realtime's replicas when its first member comes — before that
111
+ * member is answered — and again only after it emptied.
112
+ */
113
+ export declare const OnFirst: () => MethodDecorator;
114
+ /** A member joined (after it was answered): what it emits goes to that member alone. */
115
+ export declare const OnJoin: () => MethodDecorator;
116
+ /** A member left. */
117
+ export declare const OnLeave: () => MethodDecorator;
118
+ /** Nobody has been in the room for its graceMs: stop what @OnFirst started. Its signal is aborted already. */
119
+ export declare const OnEmpty: () => MethodDecorator;
120
+ /**
121
+ * A message a member sends, `name`, its payload checked against `schema`
122
+ * before the method runs (RoomMessageContext): a payload that does not fit
123
+ * never reaches it, and the member hears why.
124
+ */
125
+ export declare function OnMessage(name: string, schema: StandardSchemaV1): MethodDecorator;
126
+ /** The room a class declares, or undefined: a room is one by its decorator, never its name. */
127
+ export declare function roomOf(cls: Function): RoomDeclaration | undefined;
128
+ /** A room's channel's parameters, or undefined when the channel is not one of its. */
129
+ export declare function matchRoom(pattern: string, channel: string): Record<string, string> | undefined;
130
+ /** One event to a room's members, as realtime's broadcast takes it: its channel (topic), and conn_id for one membership alone. */
131
+ type RoomBroadcast = realtime.BroadcastMessage;
132
+ export interface RoomDeps {
133
+ /**
134
+ * `work` as the platform's caller names it — in a transaction as `user`,
135
+ * or as the app itself when the moment is no one's (first, empty) —
136
+ * committed unless it throws, or the platform hung up before it ended.
137
+ */
138
+ run<T>(user: SignedUser | undefined, where: string, signal: AbortSignal | undefined, work: () => Promise<T>): Promise<T>;
139
+ /** realtime's POST /realtime/broadcast, as the app (its secret key). */
140
+ broadcast(messages: RoomBroadcast[]): Promise<unknown>;
141
+ /** The platform's infrastructure, not the method, failed: the call is answered 503, and realtime asks again. */
142
+ isOutage(e: unknown): boolean;
143
+ }
144
+ /** What a room's call is answered: JSON, nothing (204), or a refusal. */
145
+ export type RoomAnswer = {
146
+ status: 200;
147
+ body: unknown;
148
+ } | {
149
+ status: 204;
150
+ } | {
151
+ status: number;
152
+ code: string;
153
+ message: string;
154
+ retryAfter?: number;
155
+ };
156
+ export interface Rooms {
157
+ readonly declarations: readonly RoomDeclaration[];
158
+ /** The platform's call about a room — authorize, message or lifecycle — its JSON body. */
159
+ serve(kind: string, body: unknown, signal?: AbortSignal): Promise<RoomAnswer>;
160
+ /** Every room let go: what was held is aborted (the app is stopping). */
161
+ close(): void;
162
+ }
163
+ /** The app's rooms, served: `classes` its providers that are rooms, each with its instance. */
164
+ export declare function createRooms(classes: {
165
+ cls: Function;
166
+ instance: object;
167
+ }[], deps: RoomDeps): Rooms;
168
+ export {};
@@ -0,0 +1,22 @@
1
+ import { type AuthSpec, type ParamMeta, type RouteMeta } from './decorators.js';
2
+ export interface Route {
3
+ /** "<METHOD> <path as declared>": the rate limit's key and the logs' name. */
4
+ id: string;
5
+ method: string;
6
+ segments: ({
7
+ param: string;
8
+ } | string)[];
9
+ controller: Function;
10
+ fn: string;
11
+ params: ParamMeta[];
12
+ meta: RouteMeta;
13
+ controllerAuth: AuthSpec | undefined;
14
+ }
15
+ export declare function routeTable(controllers: Function[]): Route[];
16
+ export type Match = {
17
+ route: Route;
18
+ params: Record<string, string>;
19
+ } | {
20
+ malformed: true;
21
+ } | null;
22
+ export declare function match(table: Route[], method: string, pathname: string): Match;
@@ -0,0 +1,56 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import type { PalbaseServer } from '../../server/src/index.js';
3
+ import type { AtomicDatabase as AtomicOf, EnvTypedDatabase } from 'palgres';
4
+ import type { Schemas, Tables } from './db-env.js';
5
+ export interface RequestScope {
6
+ requestId: string;
7
+ /** The request's database: its transaction, as its caller. Set when the app has one. */
8
+ database?: object;
9
+ /** The signed-in user it serves (a room's member), once known: Flags are read for them. */
10
+ userId?: string;
11
+ /** A test's stand-ins for the platform's services (withServices): each one read here first. */
12
+ services?: Readonly<Record<string, object | undefined>>;
13
+ /** The app's strings, and the languages this request is answered in, best first: what t() reads. */
14
+ strings?: import('./i18n.js').StringsTable;
15
+ locales?: readonly string[];
16
+ /** The languages t() answered in, for the response's Content-Language. */
17
+ usedLocales?: string[];
18
+ }
19
+ /**
20
+ * A service whose calls go to the stand-in a test gave for it (withServices),
21
+ * and to `impl` everywhere else.
22
+ */
23
+ export declare function scoped<T extends object>(name: string, impl: T): T;
24
+ /**
25
+ * The service a call goes to: a test's stand-in for it, or `impl`. In a
26
+ * test's scope a service it gave no stand-in for is refused by name, as
27
+ * v2's was — its real calls would reach a platform no test serves. Log is
28
+ * the exception: a line written is the test's to read or not.
29
+ */
30
+ export declare function serviceOf<T extends object>(name: string, key: PropertyKey, impl: T): T;
31
+ export declare const requestScope: AsyncLocalStorage<RequestScope>;
32
+ /** The project's modules: auth, storage, notify, secrets, flags, analytics, realtime, machines. */
33
+ export declare const Palbase: PalbaseServer;
34
+ /**
35
+ * The request's database, as v2's: `Database.public.<table>`, `$query`,
36
+ * `$transaction`, `$atomic`, `$asService()`. It is the request's own
37
+ * transaction — committed when the handler returns, rolled back when it
38
+ * throws — so it is reached inside a request (a handler, a service it calls,
39
+ * an @On), never at import or from a timer.
40
+ */
41
+ export declare const Database: EnvTypedDatabase<Tables, Schemas>;
42
+ /** One `Database.$atomic()` transaction's handle over the app's tables: the type of its callback's `tx`, as v2 named it. */
43
+ export type AtomicDatabase = AtomicOf<Tables, Schemas>;
44
+ /** A logger's shape, as v2 named it: Log is one, and so is what a test hands in its place. */
45
+ export interface Logger {
46
+ info(message: string, ...args: unknown[]): void;
47
+ warn(message: string, ...args: unknown[]): void;
48
+ error(message: string, ...args: unknown[]): void;
49
+ debug(message: string, ...args: unknown[]): void;
50
+ }
51
+ export declare const Log: {
52
+ debug: (message: string, fields?: Record<string, unknown>) => void;
53
+ info: (message: string, fields?: Record<string, unknown>) => void;
54
+ warn: (message: string, fields?: Record<string, unknown>) => void;
55
+ error: (message: string, fields?: Record<string, unknown>) => void;
56
+ };