palbase 0.0.0-stage → 0.1.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 (100) 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/3OGEBGYZ.js +20 -0
  10. package/dist/chunks/3VASGTIN.js +2 -0
  11. package/dist/chunks/3XT4L2XV.js +2 -0
  12. package/dist/chunks/45J7YXRB.js +2 -0
  13. package/dist/chunks/6FWCNTAM.js +2 -0
  14. package/dist/chunks/DAOLIUNZ.js +2 -0
  15. package/dist/chunks/E5Q4VTW4.js +2 -0
  16. package/dist/chunks/HKJU2FKE.js +2 -0
  17. package/dist/chunks/IKVOXHEE.js +2 -0
  18. package/dist/chunks/JAWWIVZH.js +2 -0
  19. package/dist/chunks/LYEFPLJZ.js +4 -0
  20. package/dist/chunks/QXUMLO7O.js +13 -0
  21. package/dist/chunks/UAY56IQR.js +6 -0
  22. package/dist/cli.js +815 -0
  23. package/dist/refuse/ai.js +2 -0
  24. package/dist/refuse/backend.js +2 -0
  25. package/dist/refuse/server.js +2 -0
  26. package/dist/server/index.js +2 -0
  27. package/dist/server/testing.js +2 -0
  28. package/dist/web/index.js +2 -0
  29. package/dist/web/palbe.js +2 -0
  30. package/dist/web/vite.js +3 -0
  31. package/package.json +107 -4
  32. package/types/ai/src/ai.d.ts +178 -0
  33. package/types/ai/src/index.d.ts +5 -0
  34. package/types/ai/src/partial.d.ts +1 -0
  35. package/types/ai/src/schema.d.ts +47 -0
  36. package/types/backend/src/bootstrap.d.ts +72 -0
  37. package/types/backend/src/cache.d.ts +32 -0
  38. package/types/backend/src/container.d.ts +49 -0
  39. package/types/backend/src/database.d.ts +148 -0
  40. package/types/backend/src/db-env.d.ts +4 -0
  41. package/types/backend/src/decorators.d.ts +293 -0
  42. package/types/backend/src/engine-db.d.ts +24 -0
  43. package/types/backend/src/engine-exports.d.ts +3 -0
  44. package/types/backend/src/engine.d.ts +124 -0
  45. package/types/backend/src/env.d.ts +27 -0
  46. package/types/backend/src/errors.d.ts +70 -0
  47. package/types/backend/src/i18n.d.ts +21 -0
  48. package/types/backend/src/index.d.ts +23 -0
  49. package/types/backend/src/openapi.d.ts +73 -0
  50. package/types/backend/src/platform-calls.d.ts +41 -0
  51. package/types/backend/src/requests.d.ts +81 -0
  52. package/types/backend/src/rooms.d.ts +168 -0
  53. package/types/backend/src/router.d.ts +22 -0
  54. package/types/backend/src/runtime.d.ts +56 -0
  55. package/types/backend/src/services.d.ts +262 -0
  56. package/types/backend/src/stack.d.ts +20 -0
  57. package/types/backend/src/strings-files.d.ts +3 -0
  58. package/types/backend/src/testing/index.d.ts +3 -0
  59. package/types/backend/src/testing/unit.d.ts +66 -0
  60. package/types/backend/src/upload.d.ts +125 -0
  61. package/types/backend/src/webhooks.d.ts +113 -0
  62. package/types/internal/src/event.d.ts +9 -0
  63. package/types/internal/src/modules/ai.d.ts +678 -0
  64. package/types/internal/src/modules/analytics.d.ts +1414 -0
  65. package/types/internal/src/modules/auth.d.ts +5660 -0
  66. package/types/internal/src/modules/cache.d.ts +158 -0
  67. package/types/internal/src/modules/events.d.ts +19 -0
  68. package/types/internal/src/modules/flags.d.ts +957 -0
  69. package/types/internal/src/modules/index.d.ts +13 -0
  70. package/types/internal/src/modules/machines.d.ts +1584 -0
  71. package/types/internal/src/modules/notify.d.ts +1450 -0
  72. package/types/internal/src/modules/purchases.d.ts +2944 -0
  73. package/types/internal/src/modules/realtime.d.ts +326 -0
  74. package/types/internal/src/modules/secrets.d.ts +275 -0
  75. package/types/internal/src/modules/storage.d.ts +691 -0
  76. package/types/internal/src/modules/stores.d.ts +1516 -0
  77. package/types/internal/src/retry.d.ts +14 -0
  78. package/types/internal/src/transport.d.ts +106 -0
  79. package/types/server/src/auth/dpop.d.ts +47 -0
  80. package/types/server/src/auth/jwt.d.ts +115 -0
  81. package/types/server/src/index.d.ts +9 -0
  82. package/types/server/src/pages.d.ts +4 -0
  83. package/types/server/src/purchases.d.ts +51 -0
  84. package/types/server/src/secrets-env.d.ts +22 -0
  85. package/types/server/src/server.d.ts +40 -0
  86. package/types/server/src/testing.d.ts +91 -0
  87. package/types/server/src/webhooks.d.ts +72 -0
  88. package/types/web/src/analytics.d.ts +87 -0
  89. package/types/web/src/auth.d.ts +155 -0
  90. package/types/web/src/backend-error.d.ts +38 -0
  91. package/types/web/src/backend-names.d.ts +25 -0
  92. package/types/web/src/backends.d.ts +84 -0
  93. package/types/web/src/client.d.ts +51 -0
  94. package/types/web/src/index.d.ts +11 -0
  95. package/types/web/src/palbe.d.ts +2 -0
  96. package/types/web/src/purchases.d.ts +73 -0
  97. package/types/web/src/rooms.d.ts +61 -0
  98. package/types/web/src/session.d.ts +91 -0
  99. package/types/web/src/socket.d.ts +87 -0
  100. package/types/web/src/vite.d.ts +49 -0
@@ -0,0 +1,14 @@
1
+ import { PalbaseError, type Transport } from './transport.js';
2
+ export interface RetryPolicy {
3
+ /** Attempts in all, the first included. */
4
+ attempts: number;
5
+ /** A Retry-After longer than this is not waited for: the error goes to the caller. */
6
+ maxWaitSeconds: number;
7
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
8
+ }
9
+ export declare const DEFAULT_RETRY: RetryPolicy;
10
+ export declare function withRetry(next: Transport, policy?: RetryPolicy): Transport;
11
+ /** Seconds to wait before the next attempt, or undefined when this refusal is not one to wait out. */
12
+ export declare function waitSeconds(e: PalbaseError, attempt: number, maxWait: number): number | undefined;
13
+ /** A v4 UUID: crypto.randomUUID where the page is a secure context, getRandomValues elsewhere (an http page has no randomUUID). */
14
+ export declare function idempotencyKey(): string;
@@ -0,0 +1,106 @@
1
+ export type Scheme = 'ApiKeyAuth' | 'SecretKeyAuth' | 'AdminAuth' | 'userJWT' | 'OAuthClient' | 'OAuthAccessToken';
2
+ export interface Credentials {
3
+ /** pb_<ref>_c…: ships in apps. */
4
+ publishableKey?: string;
5
+ /** pb_<ref>_s…: servers only. */
6
+ secretKey?: string;
7
+ /** The signed-in user's access token, asked for on every call that takes one. */
8
+ accessToken?: () => string | undefined | Promise<string | undefined>;
9
+ }
10
+ export type AnswerKind = 'json' | 'empty' | 'raw' | 'redirect';
11
+ export interface CallSpec {
12
+ op: string;
13
+ method: string;
14
+ path: string;
15
+ auth: string[][];
16
+ answers: Record<number, AnswerKind>;
17
+ query?: Record<string, unknown>;
18
+ headers?: Record<string, unknown>;
19
+ body?: {
20
+ kind: 'json' | 'form' | 'raw';
21
+ contentType: string;
22
+ value: unknown;
23
+ };
24
+ /** The contract takes an Idempotency-Key on this write. */
25
+ idempotent?: boolean;
26
+ signal?: AbortSignal;
27
+ /** The request outlives the page that sends it (fetch keepalive: 64 KiB of body at most) — what a page sends as it closes. */
28
+ keepalive?: boolean;
29
+ /**
30
+ * Told as the body's bytes go out: an upload's progress. Sent through the
31
+ * transport's XMLHttpRequest when it has one — fetch tells nothing of a
32
+ * body as it goes.
33
+ */
34
+ onUploadProgress?: (loaded: number, total: number) => void;
35
+ }
36
+ export interface Reply<S extends number, T> {
37
+ status: S;
38
+ data: T;
39
+ headers: Headers;
40
+ }
41
+ export interface Transport {
42
+ send(spec: CallSpec): Promise<Reply<number, unknown>>;
43
+ }
44
+ export interface ErrorEnvelope {
45
+ error: string;
46
+ error_description?: string;
47
+ status?: number;
48
+ request_id?: string;
49
+ retry_after?: number;
50
+ }
51
+ export declare class PalbaseError extends Error {
52
+ readonly status: number;
53
+ readonly code: string;
54
+ readonly requestId?: string;
55
+ /** Seconds the server asked to wait (Retry-After or the envelope's retry_after). */
56
+ readonly retryAfter?: number;
57
+ /** The refusal's body as sent, for the answers that are not the envelope (OAuth errors, proof-of-work). */
58
+ readonly body?: unknown;
59
+ readonly op: string;
60
+ constructor(init: {
61
+ op: string;
62
+ status: number;
63
+ code: string;
64
+ message: string;
65
+ requestId?: string;
66
+ retryAfter?: number;
67
+ body?: unknown;
68
+ cause?: unknown;
69
+ });
70
+ }
71
+ /**
72
+ * One path parameter as one URL segment. encodeURIComponent leaves `.` as it
73
+ * is, and a URL resolves `.` and `..` segments: an object named `..` would
74
+ * send DELETE …/objects/.. to …/buckets/b/ — the bucket. Empty, `.` and `..`
75
+ * are refused before anything is sent; every other value, `%2e%2e` included,
76
+ * is encoded and stays one segment.
77
+ */
78
+ export declare function segment(op: string, name: string, value: unknown): string;
79
+ /**
80
+ * What the SDK calls to send a request: `fetch`, or a stand-in of the same
81
+ * shape — a test's, a proxy's. Any runtime's `fetch` is one (Bun's, which
82
+ * carries more, too).
83
+ */
84
+ export type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
85
+ export interface FetchTransportOptions {
86
+ /** https://<ref>.<domain> — http only for a loopback address. */
87
+ baseUrl: string;
88
+ credentials: Credentials;
89
+ fetch?: Fetch;
90
+ /**
91
+ * Solve auth's proof-of-work and send the request again (default true).
92
+ * A server that signs users in for many people behind one address turns it
93
+ * off: a hard challenge would hold its event loop's turns for every user.
94
+ */
95
+ solveProofOfWork?: boolean;
96
+ /**
97
+ * What a call that follows its upload's progress is sent with
98
+ * (CallSpec.onUploadProgress). Without one, every call goes through fetch.
99
+ */
100
+ xmlHttpRequest?: typeof XMLHttpRequest;
101
+ }
102
+ export declare function createFetchTransport(opts: FetchTransportOptions): Transport;
103
+ /** The project's origin, checked once: https (http only on loopback), no user, query or fragment. */
104
+ export declare function projectBase(raw: string): URL;
105
+ /** A body that can be sent again: everything but a stream or an async iterable. */
106
+ export declare function replayable(body: BodyInit | null | undefined): boolean;
@@ -0,0 +1,47 @@
1
+ export interface ProofContext {
2
+ /** The DPoP header, as sent (null when there was none). */
3
+ proof: string | null;
4
+ method: string;
5
+ /** The request's address as its client used it: scheme://host/path. */
6
+ url: string;
7
+ }
8
+ /** A proof checked but not yet spent: the key that signed it, its jti's hash, and until when it is remembered (ms). */
9
+ export interface ProofUse {
10
+ jkt: string;
11
+ jti: string;
12
+ until: number;
13
+ }
14
+ /**
15
+ * The proofs a process has taken, by the key that signed them, until they
16
+ * would be too old to take again (each under the hash of its jti: a fixed size
17
+ * whatever the client sent).
18
+ *
19
+ * When as many are held as it keeps and none is old enough to forget, it
20
+ * forgets the keys holding the most — whole — until a tenth is free. One
21
+ * client signing proofs as fast as it can fills the store with its own: it is
22
+ * its own that are forgotten (and its own proofs it could then use twice),
23
+ * never another's request refused. Refusing every new proof instead would let
24
+ * one signed-in user stop every other.
25
+ */
26
+ export declare class SeenProofs {
27
+ private byKey;
28
+ private held;
29
+ private sweptAt;
30
+ private readonly max;
31
+ constructor(max?: number);
32
+ /** Spends the proof, or throws TokenError('proof') when it was spent before (or checked too long ago to be told). */
33
+ spend(use: ProofUse, now: number): void;
34
+ /** A key's proofs leave mostly in the order they came: the oldest are at the front. */
35
+ private forgetOld;
36
+ private makeRoom;
37
+ }
38
+ /** RFC 7638's thumbprint of a P-256 key: its four members, in this order, no whitespace. */
39
+ export declare function thumbprint(x: string, y: string): Promise<string>;
40
+ /** The address a proof is for: scheme, host and path, as the client sent it (none when it carries a user or a password). */
41
+ export declare function proofTarget(raw: string): string | undefined;
42
+ /**
43
+ * Checks the proof for `token`, bound to `jkt`, or throws TokenError('proof')
44
+ * saying why it is not taken. What it returns is spent (SeenProofs.spend) once
45
+ * the request is otherwise taken.
46
+ */
47
+ export declare function verifyProof(token: string, jkt: string, ctx: ProofContext, nowMs: number): Promise<ProofUse>;
@@ -0,0 +1,115 @@
1
+ import { type Fetch } from '../../../internal/src/transport.js';
2
+ import { type ProofContext } from './dpop.js';
3
+ export type { ProofContext };
4
+ export interface TokenUser {
5
+ /** The user's id (the token's sub). */
6
+ id: string;
7
+ email: string | null;
8
+ emailVerified: boolean;
9
+ role: string | null;
10
+ sessionId: string | null;
11
+ /** Every claim of the token, as signed. */
12
+ claims: Readonly<Record<string, unknown>>;
13
+ }
14
+ export type TokenRefusal = 'malformed' | 'algorithm' | 'unknown_key' | 'signature' | 'issuer' | 'audience' | 'project' | 'expired' | 'not_yet_valid' | 'not_a_user' | 'bound'
15
+ /** The DPoP scheme with a token bound to no key. */
16
+ | 'not_bound'
17
+ /** A bound token's DPoP proof that cannot be taken (dpop.ts says why). */
18
+ | 'proof' | 'not_a_session' | 'session_ended'
19
+ /** Not a JWT, and not one of the app's own tokens (AppConfig.tokens). */
20
+ | 'unknown_token';
21
+ export declare class TokenError extends Error {
22
+ readonly reason: TokenRefusal;
23
+ constructor(reason: TokenRefusal, message: string);
24
+ }
25
+ /** Tokens cannot be checked now — the key set or a session's standing is out of reach (503, not 401). */
26
+ export declare class AuthUnavailable extends Error {
27
+ constructor(message: string, options?: {
28
+ cause?: unknown;
29
+ });
30
+ }
31
+ /** Whether the session the token belongs to still stands: true, false, or a throw when that cannot be told now. */
32
+ export type SessionStanding = (token: string, sessionId: string) => Promise<boolean>;
33
+ /**
34
+ * The platform's answer to whether a token's session stands: GET /auth/user
35
+ * with the token and the project's secret key — the edge answers it from the
36
+ * session's standing; a 401 is a session that has ended.
37
+ */
38
+ export declare function sessionStanding(opts: {
39
+ url: string;
40
+ secretKey: string;
41
+ fetch?: Fetch;
42
+ }): SessionStanding;
43
+ /** A request as a server framework holds it: its method, its URL (absolute, or its path and query) and its headers. */
44
+ export interface RequestLike {
45
+ method: string;
46
+ url: string;
47
+ headers: Headers | Record<string, string | string[] | undefined>;
48
+ }
49
+ export interface VerifierOptions {
50
+ /** The project's address: https://<ref>.<domain> — the tokens' issuer. */
51
+ issuer: string;
52
+ /** The project's id: the tokens' project_id. */
53
+ projectId: string;
54
+ fetch?: Fetch;
55
+ /** Epoch milliseconds (default Date.now). */
56
+ now?: () => number;
57
+ /** How long the key set is kept when its answer does not say (and at most), ms. */
58
+ maxKeyAgeMs?: number;
59
+ /** An unknown kid makes the key set be read again — at most this often, ms. */
60
+ refetchIntervalMs?: number;
61
+ /** How long the key set is waited for, ms (default 5000). */
62
+ fetchTimeoutMs?: number;
63
+ /** The platform's answer to "does this token's session stand" (bootstrap asks GET /auth/user). */
64
+ standing: SessionStanding;
65
+ /** How long a session's standing is kept, ms (default 15000). */
66
+ standingTtlMs?: number;
67
+ }
68
+ export declare class TokenVerifier {
69
+ private readonly issuer;
70
+ private readonly projectId;
71
+ private readonly fetchImpl;
72
+ private readonly now;
73
+ private readonly maxKeyAgeMs;
74
+ private readonly refetchIntervalMs;
75
+ private readonly fetchTimeoutMs;
76
+ private readonly standing;
77
+ private readonly standingTtlMs;
78
+ private standings;
79
+ private asking;
80
+ private keys;
81
+ private keysUntil;
82
+ private lastFetch;
83
+ private lastFailure;
84
+ private inflight;
85
+ private seenProofs;
86
+ constructor(opts: VerifierOptions);
87
+ /**
88
+ * The user the token is; a TokenError says why it is not taken. `proof` is
89
+ * the request's DPoP proof when it came with the DPoP scheme (none for a Bearer).
90
+ */
91
+ verify(token: string, proof?: ProofContext): Promise<TokenUser>;
92
+ /**
93
+ * The user a request is, from its Authorization header — a Bearer, or a
94
+ * token bound to the caller's key with the request's DPoP proof (a native
95
+ * app's session) — or null when it carries none; a TokenError says why the
96
+ * one it carries is not taken. What a server that is not a Palbase backend
97
+ * (Express, NestJS, Hono) checks a request's user with.
98
+ */
99
+ verifyRequest(request: RequestLike): Promise<TokenUser | null>;
100
+ private stands;
101
+ private check;
102
+ private key;
103
+ private refresh;
104
+ private load;
105
+ }
106
+ /**
107
+ * The address a request was sent to, as its client used it — what a DPoP
108
+ * proof's htu names. A machine is reached only through the platform's edge,
109
+ * which ends TLS and chooses the machine by the host the client asked for,
110
+ * passing it on as the Host header: the scheme is the edge's
111
+ * (x-forwarded-proto, its first), the host that header (a Request made here,
112
+ * with no Host header, is its URL's). A Host that is not a host name or an
113
+ * address, with an optional port, is no address: no proof is for it.
114
+ */
115
+ export declare function requestAddress(headers: Headers, url: URL): string;
@@ -0,0 +1,9 @@
1
+ export { createServer, type PalbaseServer, type ServerOptions } from './server.js';
2
+ export { EVENT_TYPES, signatureHeader, verifySignature, verifyWebhook, verifyWebhookRequest, WebhookSignatureError, type EventType, type PalbaseEvent, type PlatformEvent, type PlatformEvents, type SignatureOptions, type VerifyOptions, type WebhookEvent, type WebhookRefusal, } from './webhooks.js';
3
+ export { AuthUnavailable, requestAddress, sessionStanding, TokenError, TokenVerifier, type ProofContext, type RequestLike, type SessionStanding, type TokenRefusal, type TokenUser, type VerifierOptions, } from './auth/jwt.js';
4
+ export { isEntitled, ReservationSettled, withReservation, type CustomerInfo, type Reservation, type SpendReceipt } from './purchases.js';
5
+ export { pages } from './pages.js';
6
+ export { PalbaseError } from '../../internal/src/transport.js';
7
+ export type { RetryPolicy } from '../../internal/src/retry.js';
8
+ export type * as types from '../../internal/src/modules/index.js';
9
+ export { loadSecrets, platformOwn, processOwn, type LoadSecretsOptions, type LoadedSecrets } from './secrets-env.js';
@@ -0,0 +1,4 @@
1
+ import type { Reply } from '../../internal/src/transport.js';
2
+ export declare function pages<T extends {
3
+ next_cursor?: string | null;
4
+ }>(call: (cursor: string | undefined) => Promise<Reply<number, T>>): AsyncGenerator<T>;
@@ -0,0 +1,51 @@
1
+ import type { purchases } from '../../internal/src/modules/index.js';
2
+ export type CustomerInfo = purchases.CustomerInfo;
3
+ export type SpendReceipt = purchases.SpendReceipt;
4
+ /**
5
+ * Whether the customer has the entitlement at `now` (this moment unless
6
+ * given): it is active and, unless it is for life (expires_at null), ends
7
+ * after now. Read from the CustomerInfo alone — it is as of when the module
8
+ * computed it (requested_at), so one held a while ends when its entitlement
9
+ * does. An entitlement it does not name, or an end it cannot read, is none.
10
+ */
11
+ export declare function isEntitled(info: Pick<CustomerInfo, 'entitlements'>, key: string, now?: Date | number): boolean;
12
+ /** What a job holds while it runs: credits of the catalog or units of one of its limits, one of them. */
13
+ export type Reservation = ({
14
+ credits: string;
15
+ limit?: never;
16
+ } | {
17
+ limit: string;
18
+ credits?: never;
19
+ }) & {
20
+ /** How many it holds: all of it is taken once the job ran. */
21
+ amount: number;
22
+ /**
23
+ * Names the reservation for good (1 to 255 characters): sent again — a
24
+ * request retried, a job run again — it is the same hold, never a second.
25
+ * The request's own Idempotency-Key, or the id of the work it pays for.
26
+ */
27
+ idempotencyKey: string;
28
+ /** How long the hold lasts, in seconds (600 unless said, 86400 at most): a job that runs longer finds it gone, and pays nothing. */
29
+ ttlSeconds?: number;
30
+ /** The caller's own notes, kept with the spend (a JSON object, 2048 bytes at most). */
31
+ metadata?: Record<string, unknown>;
32
+ };
33
+ /** The reservation's Idempotency-Key named one that is over: committed, released or run out. Its job is not run again. */
34
+ export declare class ReservationSettled extends Error {
35
+ readonly receipt: SpendReceipt;
36
+ constructor(receipt: SpendReceipt);
37
+ }
38
+ type Spends = Pick<purchases.Client, 'holdSpend' | 'commitSpend' | 'releaseSpend'>;
39
+ /**
40
+ * Runs `fn` paid by what it holds: the customer's credits (or a limit's
41
+ * units) are held first — refused there (409 credits_insufficient,
42
+ * limit_reached), fn never runs — and taken in full once fn returns; when fn
43
+ * throws, the hold is released and the error is fn's. A release that cannot
44
+ * be made leaves the hold to run out. The same idempotencyKey again (a
45
+ * retried request) is the same hold: while it is held its job runs; once it
46
+ * is over, ReservationSettled.
47
+ */
48
+ export declare function withReservation<T>(server: {
49
+ purchases: Spends;
50
+ }, customerId: string, reservation: Reservation, fn: (hold: SpendReceipt) => T | Promise<T>): Promise<T>;
51
+ export {};
@@ -0,0 +1,22 @@
1
+ import type { Fetch } from '../../internal/src/transport.js';
2
+ export interface LoadSecretsOptions {
3
+ /** Where the values go, and where PALBASE_SECRETS, PALBASE_URL and PALBASE_SECRET_KEY are read (default process.env). */
4
+ env?: Record<string, string | undefined>;
5
+ /** The project's address and secret key, when not in env. */
6
+ url?: string;
7
+ secretKey?: string;
8
+ fetch?: Fetch;
9
+ }
10
+ export interface LoadedSecrets {
11
+ set: 'development' | 'production';
12
+ /** The names set from the project's Secrets. */
13
+ loaded: string[];
14
+ /** The names the environment already held, left as they were. */
15
+ kept: string[];
16
+ /** The names not set: not a variable's name, or one that would change the process itself. */
17
+ refused: string[];
18
+ }
19
+ export declare function processOwn(name: string): boolean;
20
+ /** Whether the platform (or env:pull) sets this variable itself: it is not a project's secret. */
21
+ export declare function platformOwn(name: string): boolean;
22
+ export declare function loadSecrets(opts?: LoadSecretsOptions): Promise<LoadedSecrets | undefined>;
@@ -0,0 +1,40 @@
1
+ import { type Fetch, type Transport } from '../../internal/src/transport.js';
2
+ import { type RetryPolicy } from '../../internal/src/retry.js';
3
+ import * as modules from '../../internal/src/modules/index.js';
4
+ export interface ServerOptions {
5
+ /** The project's address: https://<ref>.<domain> */
6
+ url: string;
7
+ /** The project's secret key (pb_<ref>_s…), from the server's environment. */
8
+ secretKey: string;
9
+ fetch?: Fetch;
10
+ retry?: RetryPolicy;
11
+ /**
12
+ * Solve auth's proof-of-work here and send the call again (default false).
13
+ * A server signs users in for many people behind one address: a hard
14
+ * challenge would hold its event loop for all of them. Left off, the
15
+ * refusal (403 pow_required) reaches the caller with the challenge, for the
16
+ * user's own device to solve.
17
+ */
18
+ solveProofOfWork?: boolean;
19
+ }
20
+ export interface PalbaseServer {
21
+ /** The ai module's calls as its contract states them; palbase/ai is the way to use them (ask, generate, stream, embed, image). */
22
+ ai: ReturnType<typeof modules.ai.client>;
23
+ analytics: ReturnType<typeof modules.analytics.client>;
24
+ auth: ReturnType<typeof modules.auth.client>;
25
+ /** The project's cache entries; palbase/backend's Cache is the way to use them. */
26
+ cache: ReturnType<typeof modules.cache.client>;
27
+ flags: ReturnType<typeof modules.flags.client>;
28
+ machines: ReturnType<typeof modules.machines.client>;
29
+ notify: ReturnType<typeof modules.notify.client>;
30
+ purchases: ReturnType<typeof modules.purchases.client>;
31
+ realtime: ReturnType<typeof modules.realtime.client>;
32
+ secrets: ReturnType<typeof modules.secrets.client>;
33
+ storage: ReturnType<typeof modules.storage.client>;
34
+ stores: ReturnType<typeof modules.stores.client>;
35
+ /** The transport every call goes through (the secret key, retries). */
36
+ transport: Transport;
37
+ /** The project's address its calls go to — what a public URL of its is made with. */
38
+ url: string;
39
+ }
40
+ export declare function createServer(opts: ServerOptions): PalbaseServer;
@@ -0,0 +1,91 @@
1
+ import type { Fetch } from '../../internal/src/transport.js';
2
+ export interface TestApiConfig {
3
+ /** The app's address: where its routes are. */
4
+ baseUrl: string;
5
+ /**
6
+ * A publishable key of the project's: what a sign-in at its auth is made
7
+ * with, sent on every request when given. A run's test users come with
8
+ * their tokens (signInAs), so only signIn needs it.
9
+ */
10
+ apiKey?: string;
11
+ /** Where the project's auth is (PALBASE_TEST_AUTH_URL); the app's address when not given. */
12
+ authUrl?: string;
13
+ /**
14
+ * v2 addressed a candidate release beside the serving one with this. A v3
15
+ * test run is given the build under test as its address, so there is no
16
+ * candidate to name: it is not sent.
17
+ */
18
+ candidateToken?: string;
19
+ /** The run's test users, by name. */
20
+ identities?: Record<string, TestIdentity>;
21
+ fetch?: Fetch;
22
+ }
23
+ /** A test user this run may act as: its token when the run made one, its password otherwise. */
24
+ export interface TestIdentity {
25
+ id?: string;
26
+ email: string;
27
+ password: string;
28
+ accessToken?: string;
29
+ }
30
+ export interface RecordedRequest {
31
+ method: string;
32
+ path: string;
33
+ status: number;
34
+ ms: number;
35
+ }
36
+ /** The app's error envelope, as a refusal answers it. */
37
+ export interface ErrorEnvelope {
38
+ error?: string;
39
+ error_description?: string;
40
+ status?: number;
41
+ request_id?: string;
42
+ data?: unknown;
43
+ [key: string]: unknown;
44
+ }
45
+ /** A refusal: its status, its code (error), its data and the whole body. */
46
+ export declare class TestApiError extends Error {
47
+ readonly status: number;
48
+ readonly error: string;
49
+ readonly data: unknown;
50
+ readonly body: ErrorEnvelope;
51
+ constructor(method: string, path: string, status: number, body: unknown);
52
+ }
53
+ export interface CallOptions {
54
+ headers?: Record<string, string>;
55
+ }
56
+ export interface TestApi {
57
+ get<T = unknown>(path: string, opts?: CallOptions): Promise<T>;
58
+ post<T = unknown>(path: string, body?: unknown, opts?: CallOptions): Promise<T>;
59
+ patch<T = unknown>(path: string, body?: unknown, opts?: CallOptions): Promise<T>;
60
+ put<T = unknown>(path: string, body?: unknown, opts?: CallOptions): Promise<T>;
61
+ delete<T = unknown>(path: string, opts?: CallOptions): Promise<T>;
62
+ /** HTTP QUERY: a read with a body. */
63
+ query<T = unknown>(path: string, body?: unknown, opts?: CallOptions): Promise<T>;
64
+ /** Signs in at the project's auth; the requests after it carry the user's token. */
65
+ signIn(credentials: {
66
+ email: string;
67
+ password: string;
68
+ }): Promise<{
69
+ id: string;
70
+ email?: string;
71
+ }>;
72
+ /** Acts as the run's test user of that name: its token when it has one, a sign-in otherwise. */
73
+ signInAs(name: string): Promise<{
74
+ id: string;
75
+ email?: string;
76
+ }>;
77
+ signOut(): Promise<void>;
78
+ /** The requests after it carry no user. */
79
+ asAnonymous(): void;
80
+ readonly requests: readonly RecordedRequest[];
81
+ }
82
+ export declare function createTestApi(config: TestApiConfig): TestApi;
83
+ export interface TestRun {
84
+ /** Whether these tests are run against an app (PALBASE_TEST_BASE_URL is set) — HTTP tests skip themselves otherwise. */
85
+ readonly stack: boolean;
86
+ /** Whether the run made a test user of that name. */
87
+ minted(name: string): boolean;
88
+ }
89
+ export declare const testRun: TestRun;
90
+ /** The run's client, made from PALBASE_TEST_* the first time it is used. */
91
+ export declare const api: TestApi;
@@ -0,0 +1,72 @@
1
+ import type { WebhookEvent } from '../../internal/src/event.js';
2
+ import { EVENT_TYPES, type EventType, type PalbaseEvent, type PlatformEvents } from '../../internal/src/modules/index.js';
3
+ export { EVENT_TYPES };
4
+ export type { EventType, PalbaseEvent, PlatformEvents, WebhookEvent };
5
+ /**
6
+ * An event of a type the modules' contracts declare (x-palbase-events), its
7
+ * data as the contract says: PlatformEvent<'stores.delivery.changed'> carries
8
+ * a DeliveryChangedEvent. PalbaseEvent's member of that type: what a handler
9
+ * of it is given.
10
+ */
11
+ export type PlatformEvent<K extends EventType> = Extract<PalbaseEvent, {
12
+ type: K;
13
+ }>;
14
+ export type WebhookRefusal = 'missing_signature' | 'malformed_signature' | 'stale_timestamp' | 'signature_mismatch' | 'invalid_body' | 'body_too_large';
15
+ export declare class WebhookSignatureError extends Error {
16
+ readonly code: WebhookRefusal;
17
+ constructor(code: WebhookRefusal, message: string);
18
+ }
19
+ export interface SignatureOptions {
20
+ /** The key the request was signed with. */
21
+ secret: string;
22
+ /** The Palbase-Signature header. */
23
+ signature: string | null | undefined;
24
+ /** The body exactly as received (empty for a GET). */
25
+ body: string | Uint8Array | ArrayBuffer;
26
+ /** How far t may be from now, in seconds (default 300). */
27
+ toleranceSeconds?: number;
28
+ /** Epoch milliseconds (default Date.now). */
29
+ now?: () => number;
30
+ }
31
+ export interface VerifyOptions extends SignatureOptions {
32
+ /** The endpoint's secret (whsec_…), given once when the endpoint was set. */
33
+ secret: string;
34
+ }
35
+ export declare const DEFAULT_MAX_WEBHOOK_BYTES: number;
36
+ /**
37
+ * The event, once its signature and time are checked; a WebhookSignatureError
38
+ * otherwise. A PalbaseEvent: a switch on its type narrows its data to what
39
+ * that type's contract says. An event of a type no contract declares — one a
40
+ * newer platform sends, or one whose module declares none yet (user.deleted)
41
+ * — is none of the switch's cases at run time and reaches its default: take
42
+ * it there (a 2xx), never throw, or the platform delivers it again and again.
43
+ * verifyWebhook<T>() reads such an event's data as T.
44
+ */
45
+ export declare function verifyWebhook(opts: VerifyOptions): Promise<PalbaseEvent>;
46
+ export declare function verifyWebhook<T>(opts: VerifyOptions): Promise<WebhookEvent<T>>;
47
+ /**
48
+ * Returns once a v1 of the Palbase-Signature header is the HMAC of its t and
49
+ * the body under secret, and t is within the tolerance of now; throws a
50
+ * WebhookSignatureError otherwise (missing_signature, malformed_signature,
51
+ * stale_timestamp, signature_mismatch). verifyWebhook's check without the
52
+ * event: for the platform's calls to a machine, under its PALBASE_PLATFORM_SECRET.
53
+ */
54
+ export declare function verifySignature(opts: SignatureOptions): Promise<void>;
55
+ /**
56
+ * What a Palbase-Signature header says — its time and its v1s — once it is
57
+ * one, and its time within the tolerance of now; throws a
58
+ * WebhookSignatureError otherwise (missing_signature, malformed_signature,
59
+ * stale_timestamp). verifySignature's first half, on the header alone: a
60
+ * request with none, a broken or an old one is refused before its body is
61
+ * read.
62
+ */
63
+ export declare function signatureHeader(opts: Pick<SignatureOptions, 'signature' | 'toleranceSeconds' | 'now'>): {
64
+ t: string;
65
+ v1: string[];
66
+ };
67
+ type RequestOptions = Pick<VerifyOptions, 'toleranceSeconds' | 'now'> & {
68
+ maxBytes?: number;
69
+ };
70
+ /** verifyWebhook for a fetch Request (Node, Bun, Deno, Workers): reads its body, at most maxBytes, and its header. */
71
+ export declare function verifyWebhookRequest(request: Request, secret: string, opts?: RequestOptions): Promise<PalbaseEvent>;
72
+ export declare function verifyWebhookRequest<T>(request: Request, secret: string, opts?: RequestOptions): Promise<WebhookEvent<T>>;
@@ -0,0 +1,87 @@
1
+ import type { Transport } from '../../internal/src/transport.js';
2
+ import type { SessionManager } from './session.js';
3
+ export interface AnalyticsOptions {
4
+ /** A $pageview for the first page and each navigation after it (history and back/forward). Default true. */
5
+ pageViews?: boolean;
6
+ /** LCP, CLS, INP, FCP and TTFB, as web_vital samples. Default true. */
7
+ webVitals?: boolean;
8
+ /** Each fetch the page makes, as a network sample: method, address (no query), status, time. Default true. */
9
+ network?: boolean;
10
+ /** The app's version, kept with every event. */
11
+ appVersion?: string;
12
+ }
13
+ export interface AnalyticsTiming {
14
+ /** How long an event waits for others before they go together (ms). */
15
+ flushMs?: number;
16
+ /** Events that go at once without waiting. */
17
+ flushAt?: number;
18
+ now?: () => number;
19
+ }
20
+ export declare class Analytics {
21
+ private readonly api;
22
+ private readonly closingApi;
23
+ private readonly flushMs;
24
+ private readonly flushAt;
25
+ private readonly now;
26
+ private readonly anonKey;
27
+ private readonly sessionKey;
28
+ private events;
29
+ private samples;
30
+ private timer;
31
+ private memoryAnon;
32
+ private memorySession;
33
+ private appVersion;
34
+ private stopAuto;
35
+ private readonly session;
36
+ /** Someone signed in: their identify goes with the next send, never on the sign-in itself. */
37
+ private identifyPending;
38
+ constructor(transport: Transport, session: SessionManager, origin: string, timing?: AnalyticsTiming);
39
+ /** Records an event: a name (a letter or $, then letters, digits and _.:-$; 64 at most) and its properties. */
40
+ capture(event: string, properties?: Record<string, unknown>): void;
41
+ /**
42
+ * Records a page view: the page's address (the current one unless given),
43
+ * its title, where the visitor came from. What the SDK reads on its own
44
+ * goes without query and fragment — the page's address, and the browser's
45
+ * document.referrer, which on a same-site arrival is the whole previous URL
46
+ * (a reset link's token, a sign-in callback's code). What the app passes
47
+ * is sent as it passed it.
48
+ */
49
+ page(page?: {
50
+ url?: string;
51
+ title?: string;
52
+ referrer?: string;
53
+ properties?: Record<string, unknown>;
54
+ }): void;
55
+ /**
56
+ * Ties this browser's anonymous events to the signed-in user, with their
57
+ * traits. Done on its own when someone signs in; the user's token makes the
58
+ * events theirs from then on.
59
+ */
60
+ identify(traits?: Record<string, unknown>): Promise<void>;
61
+ private sendIdentify;
62
+ /** Forgets this browser's anonymous id and session: the next visitor here is someone new (done on sign-out). */
63
+ reset(): void;
64
+ /** Sends what is held now. */
65
+ flush(closing?: boolean): Promise<void>;
66
+ /**
67
+ * Starts what the browser tells of itself: page views, web vitals,
68
+ * request timings (each on unless turned off). Returns what stops them.
69
+ */
70
+ start(opts?: AnalyticsOptions): () => void;
71
+ private hold;
72
+ private sample;
73
+ private soon;
74
+ /** The next batch: a hundred at most, and when the page is closing, what keepalive can carry. */
75
+ private take;
76
+ private distinctId;
77
+ private anonymousId;
78
+ private sessionId;
79
+ private watchPages;
80
+ private watchFetch;
81
+ }
82
+ /**
83
+ * LCP, CLS and INP as they stand when the page is hidden (they settle then);
84
+ * FCP and TTFB once. INP is the slowest interaction seen, CLS the shifts not
85
+ * caused by input. Returns what stops the observers (and reports what stands).
86
+ */
87
+ export declare function observeWebVitals(record: (name: 'LCP' | 'CLS' | 'INP' | 'FCP' | 'TTFB', value: number) => void): () => void;