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,155 @@
1
+ import { auth as authModule } from '../../internal/src/modules/index.js';
2
+ import type { Transport } from '../../internal/src/transport.js';
3
+ import type { AuthEvent, Session, SessionManager } from './session.js';
4
+ type A = typeof authModule;
5
+ export type User = authModule.User;
6
+ export type SignupRequest = authModule.SignupRequest;
7
+ export type MfaFactor = authModule.MFARequiredResponse['factors'][number];
8
+ /** The part of navigator.credentials passkeys use. */
9
+ export interface CredentialsLike {
10
+ create(options: {
11
+ publicKey: PublicKeyCredentialCreationOptions;
12
+ }): Promise<unknown>;
13
+ get(options: {
14
+ publicKey: PublicKeyCredentialRequestOptions;
15
+ }): Promise<unknown>;
16
+ }
17
+ export type SignInResult = {
18
+ user: User;
19
+ mfaRequired?: undefined;
20
+ } | {
21
+ user?: undefined;
22
+ mfaRequired: {
23
+ mfaToken: string;
24
+ factors: MfaFactor[];
25
+ };
26
+ };
27
+ export declare class Auth {
28
+ /** Every client operation of the auth contract, through the session's transport. */
29
+ readonly api: ReturnType<A['client']>;
30
+ /**
31
+ * The same operations with the key alone. A flow that starts a session
32
+ * never carries the one already held: auth reads a bearer on a sign-in as
33
+ * "this user is here" — a social sign-in with one LINKS the new identity to
34
+ * that account — so whoever is signed in on this device must not ride along.
35
+ */
36
+ private open;
37
+ private session;
38
+ private credentials?;
39
+ readonly mfa: {
40
+ /** Completes a sign-in that asked for a second factor (TOTP, or a code sent by SMS or email). */
41
+ challenge: (mfaToken: string, type: 'totp' | 'sms' | 'email', code: string) => Promise<User>;
42
+ /** Completes it with one of the recovery codes instead. */
43
+ recovery: (mfaToken: string, code: string) => Promise<User>;
44
+ /** Sends the sign-in code by SMS; finish with challenge(mfaToken, 'sms', code). */
45
+ sendSmsCode: (mfaToken: string) => Promise<void>;
46
+ /** Sends the sign-in code by email; finish with challenge(mfaToken, 'email', code). */
47
+ sendEmailCode: (mfaToken: string) => Promise<void>;
48
+ /** Starts a TOTP factor: the secret for the authenticator app, and the recovery codes. */
49
+ enrollTotp: () => Promise<{
50
+ enrollmentId: string;
51
+ secret: string;
52
+ otpUrl: string;
53
+ qrCode: string;
54
+ recoveryCodes: string[];
55
+ }>;
56
+ /** Confirms the TOTP factor with a code the app shows. */
57
+ verifyTotp: (code: string) => Promise<void>;
58
+ enrollSms: (phone: string) => Promise<authModule.MFASMSEnrollResponse>;
59
+ verifySms: (code: string) => Promise<void>;
60
+ enrollEmail: () => Promise<void>;
61
+ factors: () => Promise<authModule.MFAFactorList>;
62
+ /** Removes a factor; the account's password confirms it. */
63
+ removeFactor: (id: string, currentPassword: string) => Promise<void>;
64
+ regenerateRecoveryCodes: () => Promise<authModule.MFARecoveryCodesResponse>;
65
+ };
66
+ readonly passkey: {
67
+ /** Adds a passkey to the signed-in user. */
68
+ register: (name?: string) => Promise<authModule.WebAuthnRegisteredCredential>;
69
+ /**
70
+ * Signs in with a passkey. With the user's id the authenticator is asked
71
+ * for that user's credentials; without it, for any passkey of the site
72
+ * (discoverable).
73
+ */
74
+ signIn: (opts?: {
75
+ userId?: string;
76
+ }) => Promise<User>;
77
+ /**
78
+ * Creates the account with a passkey and no password, then signs in with
79
+ * it (the sign-up answers no tokens). The identifier is the user's email.
80
+ */
81
+ signUp: (identifier: string, userName?: string) => Promise<User>;
82
+ list: () => Promise<authModule.WebAuthnCredentialList>;
83
+ remove: (id: string) => Promise<void>;
84
+ };
85
+ readonly magicLink: {
86
+ send: (email: string) => Promise<void>;
87
+ /** The token from the link the email carried. */
88
+ verify: (token: string) => Promise<User>;
89
+ };
90
+ readonly phone: {
91
+ sendCode: (phone: string) => Promise<authModule.PhoneOTPRequestResult>;
92
+ verifyCode: (phone: string, code: string) => Promise<SignInResult>;
93
+ };
94
+ readonly password: {
95
+ /** Sends the reset link; finish with confirmReset(token, newPassword). */
96
+ requestReset: (email: string) => Promise<void>;
97
+ confirmReset: (token: string, newPassword: string) => Promise<void>;
98
+ change: (body: authModule.PasswordChangeRequest) => Promise<authModule.SuccessResponse>;
99
+ set: (body: authModule.PasswordSetRequest) => Promise<authModule.SuccessResponse>;
100
+ };
101
+ readonly email: {
102
+ /** The token from the verification link, or the code with the address. */
103
+ verify: (body: authModule.VerifyEmailRequest) => Promise<void>;
104
+ resendVerification: (email: string) => Promise<authModule.ResendResult>;
105
+ };
106
+ readonly sessions: {
107
+ list: () => Promise<authModule.SessionList>;
108
+ revoke: (id: string) => Promise<void>;
109
+ revokeAll: () => Promise<void>;
110
+ };
111
+ constructor(transport: Transport, keyOnly: Transport, session: SessionManager, credentials?: CredentialsLike);
112
+ /** Listens to sign-in, refresh and sign-out; returns the unsubscribe. */
113
+ onAuthStateChange(listener: (event: AuthEvent, session: Session | null) => void): () => void;
114
+ /** The current session, or null when no one is signed in. */
115
+ currentSession(): Promise<Session | null>;
116
+ /** Creates the account. When the project signs new users in at once, the session starts too. */
117
+ signUp(body: SignupRequest): Promise<authModule.SignupResult>;
118
+ /** Email and password. A second factor, when the account has one, is asked for in mfaRequired. */
119
+ signIn(email: string, password: string): Promise<SignInResult>;
120
+ /** Ends the session here and on the server (a failed call still signs out locally). */
121
+ signOut(): Promise<void>;
122
+ /** The signed-in user's profile, read fresh. */
123
+ getUser(): Promise<authModule.MeProfile>;
124
+ /** Refreshes now rather than when the token is about to lapse. */
125
+ refreshSession(): Promise<Session>;
126
+ /**
127
+ * Deletes the account and ends the session. An account with a password
128
+ * gives it (`has_password` on getUser() says whether it has one): auth asks
129
+ * for it again, and answers 401 reauth_required when it is missing or
130
+ * wrong — the session is kept then. One without a password is deleted on
131
+ * the strength of its session.
132
+ */
133
+ deleteAccount(password?: string): Promise<void>;
134
+ private signInResult;
135
+ /**
136
+ * Starts the session from a token pair the app has itself — a flow it runs
137
+ * step by step (a passkey ceremony of its own, its begin and its finish), or
138
+ * a pair another of its pages was handed. The session held before is gone,
139
+ * and the user is read once.
140
+ */
141
+ setSession(pair: {
142
+ access_token: string;
143
+ refresh_token: string;
144
+ expires_in: number;
145
+ }): Promise<User>;
146
+ /** Starts the session from a token pair; the user is the answer's, or read once when it carries none. */
147
+ private signedIn;
148
+ private authenticator;
149
+ }
150
+ type Json = Record<string, unknown>;
151
+ export declare function creationOptions(pk: Json): PublicKeyCredentialCreationOptions;
152
+ export declare function requestOptions(pk: Json): PublicKeyCredentialRequestOptions;
153
+ export declare function attestationJSON(c: unknown): authModule.WebAuthnAttestationResponse;
154
+ export declare function assertionJSON(c: unknown): authModule.WebAuthnAssertionResponse;
155
+ export {};
@@ -0,0 +1,38 @@
1
+ import { PalbaseError } from '../../internal/src/transport.js';
2
+ export interface FieldError {
3
+ field: string;
4
+ message: string;
5
+ }
6
+ export type BackendErrorKind = 'validation' | 'unauthorized' | 'rateLimited' | 'server' | 'network' | 'decode';
7
+ interface BackendErrorParams {
8
+ code: string;
9
+ message: string;
10
+ status?: number;
11
+ requestId?: string;
12
+ fields?: FieldError[];
13
+ retryAfter?: number;
14
+ data?: unknown;
15
+ cause?: unknown;
16
+ }
17
+ export declare class BackendError extends Error {
18
+ readonly kind: BackendErrorKind;
19
+ readonly code: string;
20
+ readonly status: number;
21
+ readonly requestId?: string;
22
+ readonly fields?: FieldError[];
23
+ readonly retryAfter?: number;
24
+ readonly data?: unknown;
25
+ constructor(kind: BackendErrorKind, params: BackendErrorParams);
26
+ }
27
+ /**
28
+ * A BackendError, by class or by shape: two copies of the package loaded side
29
+ * by side make two classes, and `instanceof` fails for "the other" copy's.
30
+ */
31
+ export declare function isBackendError(e: unknown): e is BackendError;
32
+ /**
33
+ * The transport's refusal as a BackendError. Its body is the envelope as sent:
34
+ * `{ error, error_description, status, request_id, retry_after?, data? }`.
35
+ */
36
+ export declare function fromPalbaseError(err: PalbaseError): BackendError;
37
+ export declare function isPalbaseError(e: unknown): e is PalbaseError;
38
+ export {};
@@ -0,0 +1,25 @@
1
+ /** A name TypeScript takes bare: a property key unquoted, the client's member. */
2
+ export declare const IDENTIFIER: RegExp;
3
+ /**
4
+ * Names a backend cannot take on the client: what the client and the other
5
+ * platforms' clients already have, and what breaks an object. One list for
6
+ * every platform, so a name is refused at apps:sync and not on one of them.
7
+ */
8
+ export declare const RESERVED_CLIENT_NAMES: readonly string[];
9
+ /**
10
+ * A key no namespace tree holds, at any level: `then` (a thenable namespace
11
+ * makes `await` hang), `toJSON` (JSON.stringify calls it: logging
12
+ * `JSON.stringify(pb.api)` would make the call, with the user's token) and
13
+ * Object.prototype's members.
14
+ */
15
+ export declare function isReservedKey(key: string): boolean;
16
+ /** A key a backend's tree does not hold at its first level: `call` and `join`, the members every backend has (BackendBase), and the keys no level holds. */
17
+ export declare function isReservedTopLevelKey(key: string): boolean;
18
+ /** A backend's name on the client: an identifier, kept by neither the client, any platform's client nor Object.prototype. */
19
+ export declare function isClientName(name: unknown): name is string;
20
+ /** A machine's name on the platform, as storage takes it in an upload's address (/storage/upload/<machine>/…). */
21
+ export declare function isMachineName(name: unknown): name is string;
22
+ /** The machine's address, held to the transport's rule — https (http only on loopback), the origin alone; a TypeError says what is wrong. */
23
+ export declare function checkMachineUrl(url: string): void;
24
+ /** The origin of the project's address a client is made with (ClientOptions.url), held to the same rule. */
25
+ export declare function projectOrigin(url: string): string;
@@ -0,0 +1,84 @@
1
+ import type { Transport } from '../../internal/src/transport.js';
2
+ import { BackendError } from './backend-error.js';
3
+ import { type RoomDescriptor, type RoomEvent, type RoomHandle } from './rooms.js';
4
+ import type { RealtimeSocket } from './socket.js';
5
+ export interface CallOptions {
6
+ headers?: Record<string, string>;
7
+ signal?: AbortSignal;
8
+ }
9
+ /** How much of an upload's request has gone: `loaded` of its `total` bytes. */
10
+ export interface UploadProgress {
11
+ loaded: number;
12
+ total: number;
13
+ }
14
+ /**
15
+ * An upload's options. No headers: the call goes to storage, which hands the
16
+ * route the file and the input, never the request's headers.
17
+ */
18
+ export interface UploadOptions {
19
+ signal?: AbortSignal;
20
+ /** Told as the bytes go out — through XMLHttpRequest where the page has one — and once more when the answer is in. */
21
+ onProgress?: (progress: UploadProgress) => void;
22
+ }
23
+ export interface EndpointDescriptor {
24
+ method: string;
25
+ path: string;
26
+ /** `{name}` segments consumed as leading call args, in order. */
27
+ pathParams?: string[];
28
+ /**
29
+ * 'body' (default) | 'query' (the input serialized into the URL) | 'none'
30
+ * (no input arg — options come straight after the path params).
31
+ */
32
+ input?: 'body' | 'query' | 'none';
33
+ /** wire error code → lift fn; the call rethrows the lifted error. */
34
+ errors?: Record<string, (e: BackendError) => Error>;
35
+ /** The route answers `text/event-stream`: the call returns the frames, not one body. */
36
+ sse?: boolean;
37
+ /**
38
+ * The backend keeps the call's Idempotency-Key (its contract declares the
39
+ * header): sent again with the same key when the answer is "not now" or
40
+ * never came.
41
+ */
42
+ idempotent?: boolean;
43
+ /**
44
+ * The route is an `@Upload`: its call takes a file after its input, and
45
+ * goes to the project's storage, which has the machine authorize it, keeps
46
+ * the file and answers what the route's completion answered.
47
+ */
48
+ upload?: boolean;
49
+ }
50
+ export type NamespaceTree = {
51
+ [key: string]: NamespaceTree | EndpointDescriptor;
52
+ };
53
+ /** What every backend has besides its routes; a generated module's `Backend` extends it. */
54
+ export interface BackendBase {
55
+ /**
56
+ * POSTs `input` as the body to the route at `name` ('todos/create' or
57
+ * '/todos/create') and answers its JSON — v2's `pb.call`, for a route the
58
+ * generated types do not have.
59
+ */
60
+ call<O = unknown>(name: string, input?: unknown, options?: CallOptions): Promise<O>;
61
+ /**
62
+ * Joins a room the backend declared with `@Room` — `room` is the generated
63
+ * module's, `params` its pattern's `{param}` values: resolves once the
64
+ * backend let the user in, with the handle its events arrive on and its
65
+ * messages go through; rejects with a RoomError (its reason) when it did not.
66
+ *
67
+ * const room = await pb.api.join(AiSessionIdRoom, { sessionId });
68
+ * room.on('token', (t) => transcript.push(t.text));
69
+ * await room.send('say', { text: 'hello' });
70
+ * room.leave();
71
+ */
72
+ join<E extends RoomEvent, S, P extends readonly string[]>(room: RoomDescriptor<E, S, P>, params: Record<P[number], string>): Promise<RoomHandle<E, S>>;
73
+ }
74
+ /** What every client createClient makes inherits: one getter per registered backend name. */
75
+ export declare const clientBase: object;
76
+ /** Gives a client made by createClient its project's origin, its way to the machines, its project's transport (an upload's way, through storage) and its socket (a room's). */
77
+ export declare function bindMachines(client: object, project: string, machine: (url: string) => Transport, storage: Transport, socket: RealtimeSocket): void;
78
+ /**
79
+ * Registers a backend under `client` for the clients of `project` (the
80
+ * address createClient is given) — the machine `machine`, at `url` — called
81
+ * by its generated module, once, as it loads. The tree is checked whole here,
82
+ * so a broken one fails as the module loads, not at a call.
83
+ */
84
+ export declare function __registerBackend(project: string, client: string, machine: string, url: string, tree: NamespaceTree): void;
@@ -0,0 +1,51 @@
1
+ import { type Fetch, type Transport } from '../../internal/src/transport.js';
2
+ import { Auth, type CredentialsLike } from './auth.js';
3
+ import { type RetryPolicy } from '../../internal/src/retry.js';
4
+ import { type SessionStore } from './session.js';
5
+ import { RealtimeSocket } from './socket.js';
6
+ import { Analytics, type AnalyticsOptions } from './analytics.js';
7
+ import { Purchases } from './purchases.js';
8
+ export interface ClientOptions {
9
+ /** The project's address: https://<ref>.<domain> */
10
+ url: string;
11
+ /** The project's publishable key (pb_<ref>_c…). A secret key is refused: it never goes into an app. */
12
+ publishableKey: string;
13
+ /** Where the session is kept: localStorage in a browser, memory elsewhere, unless the app gives its own. */
14
+ storage?: SessionStore;
15
+ fetch?: Fetch;
16
+ webSocket?: typeof WebSocket;
17
+ /**
18
+ * What an upload that follows its progress is sent with (fetch tells
19
+ * nothing of a body as it goes): the page's XMLHttpRequest, unless the app
20
+ * gave its own fetch — then every request is that fetch's unless this is
21
+ * given too.
22
+ */
23
+ xmlHttpRequest?: typeof XMLHttpRequest;
24
+ /** Passkeys' authenticator: navigator.credentials in a browser. */
25
+ webauthn?: CredentialsLike;
26
+ retry?: RetryPolicy;
27
+ /**
28
+ * Starts what the browser tells of itself, as the app's analytics: page
29
+ * views, web vitals and request timings (true for all of them, or say which).
30
+ * Off unless given; events the app captures (`analytics.capture`) go either way.
31
+ */
32
+ analytics?: boolean | AnalyticsOptions;
33
+ }
34
+ /**
35
+ * The app's client. A backend's generated Palbe module (palbase apps:sync)
36
+ * gives every client of its project, made before or after the module loads,
37
+ * the backend under its name — `pb.api.rooms.create(…)`; its type is the
38
+ * module's `Backend`, and the app's file types its client `PalbaseClient & { api: Backend }`.
39
+ */
40
+ export interface PalbaseClient {
41
+ auth: Auth;
42
+ /** The project's realtime socket, opened on first use. */
43
+ socket: RealtimeSocket;
44
+ /** The transport every module call goes through (credentials, refresh, retries). */
45
+ transport: Transport;
46
+ /** The app's analytics: events it captures, page views, and — once started — the browser's own measures. */
47
+ analytics: Analytics;
48
+ /** What the signed-in customer has (live, onCustomerInfo), the paywall's catalog, and buying on the web. */
49
+ purchases: Purchases;
50
+ }
51
+ export declare function createClient(opts: ClientOptions): PalbaseClient;
@@ -0,0 +1,11 @@
1
+ export { createClient, type ClientOptions, type PalbaseClient } from './client.js';
2
+ export type { Auth, CredentialsLike, SignInResult, User } from './auth.js';
3
+ export { localStore, memoryStore, SessionChangedError, type AuthEvent, type Session, type SessionStore } from './session.js';
4
+ export { Channel, RealtimeSocket, type PushReply } from './socket.js';
5
+ export { Analytics, type AnalyticsOptions } from './analytics.js';
6
+ export { Purchases, type CustomerInfo, type CustomerInfoListener, type MergeGrant, type Offerings } from './purchases.js';
7
+ export { PalbaseError } from '../../internal/src/transport.js';
8
+ export { BackendError, isBackendError, type BackendErrorKind, type FieldError } from './backend-error.js';
9
+ export type { BackendBase, CallOptions, UploadOptions, UploadProgress } from './backends.js';
10
+ export { RoomError, type RoomDescriptor, type RoomEvent, type RoomHandle, type RoomSubscription } from './rooms.js';
11
+ export type { RetryPolicy } from '../../internal/src/retry.js';
@@ -0,0 +1,2 @@
1
+ export { __registerBackend, type EndpointDescriptor, type NamespaceTree } from './backends.js';
2
+ export { isClientName, RESERVED_CLIENT_NAMES } from './backend-names.js';
@@ -0,0 +1,73 @@
1
+ import { purchases as purchasesModule } from '../../internal/src/modules/index.js';
2
+ import type { Transport } from '../../internal/src/transport.js';
3
+ import type { SessionManager } from './session.js';
4
+ import type { RealtimeSocket } from './socket.js';
5
+ type P = typeof purchasesModule;
6
+ export type CustomerInfo = purchasesModule.CustomerInfo;
7
+ export type Offerings = purchasesModule.Offerings;
8
+ export type MergeGrant = purchasesModule.MergeGrant;
9
+ /** Told what the customer has: on the first read, then each time it changes; null while no one is signed in. */
10
+ export type CustomerInfoListener = (info: CustomerInfo | null) => void;
11
+ export declare class Purchases {
12
+ /** Every client operation of the purchases contract, through the session's transport. */
13
+ readonly api: ReturnType<P['client']>;
14
+ private session;
15
+ private socket;
16
+ private ref;
17
+ private listeners;
18
+ private watch;
19
+ private following;
20
+ /** What listeners were last told: undefined until the first read (or sign-out) is known. */
21
+ private last;
22
+ constructor(transport: Transport, session: SessionManager, socket: RealtimeSocket, ref: string);
23
+ /** Everything the signed-in customer has, read now (the first read makes the user a customer). */
24
+ me(): Promise<CustomerInfo>;
25
+ /** The paywall's catalog: each product's text and price on a channel — the web's unless another is named. */
26
+ offerings(channel?: 'ios' | 'android' | 'web'): Promise<Offerings>;
27
+ /**
28
+ * Opens Stripe Checkout for the signed-in customer (not a guest) to buy a
29
+ * product's plan sold on the web: the URL to send them to. Stripe sends
30
+ * them back to successUrl or cancelUrl — one of the project's registered
31
+ * apps. The purchase is theirs once Stripe reports it: onCustomerInfo hears it.
32
+ */
33
+ checkout(opts: {
34
+ product: string;
35
+ plan: string;
36
+ successUrl: string;
37
+ cancelUrl: string;
38
+ }): Promise<string>;
39
+ /** Stripe's customer portal for what the customer bought on the web: the URL to send them to, and back to returnUrl. */
40
+ portal(returnUrl: string): Promise<string>;
41
+ /**
42
+ * Asked with a guest's session, before it signs in to an existing account:
43
+ * the proof — good for 5 minutes — that whoever signs in held the guest.
44
+ * Spent with merge() once signed in. Refused for a session that is not a
45
+ * guest's (403 guest_required).
46
+ */
47
+ mergeGrant(): Promise<MergeGrant>;
48
+ /**
49
+ * With the account's session: everything the guest bought goes to the
50
+ * account, for good; what the account has now. Asked again with the same
51
+ * grant, it is the same merge.
52
+ */
53
+ merge(grant: MergeGrant | string): Promise<CustomerInfo>;
54
+ /**
55
+ * Listens to what the signed-in customer has: told it once read, then each
56
+ * time it changes — a purchase in any store, a renewal or its end, a
57
+ * refund, credits spent — and null while no one is signed in; a new user
58
+ * signing in is read anew. The first listener joins the customer's realtime
59
+ * channel, the last one's unsubscribe leaves it. A read that fails is left
60
+ * until the next change.
61
+ */
62
+ onCustomerInfo(listener: CustomerInfoListener): () => void;
63
+ private start;
64
+ private stop;
65
+ private follow;
66
+ private unfollow;
67
+ private changed;
68
+ private held;
69
+ private read;
70
+ private offer;
71
+ private tell;
72
+ }
73
+ export {};
@@ -0,0 +1,61 @@
1
+ import type { RealtimeSocket } from './socket.js';
2
+ /** One event of a room, as a generated module's union has it. */
3
+ export type RoomEvent = {
4
+ event: string;
5
+ payload: unknown;
6
+ };
7
+ /**
8
+ * A room, as the generator writes one. `send` and `event` carry types alone —
9
+ * their values are never read: `room.send('say', { … })` is checked against
10
+ * the message's shape, and a handler's event against the room's events.
11
+ */
12
+ export interface RoomDescriptor<E extends RoomEvent, S, P extends readonly string[] = readonly string[]> {
13
+ /** The channel pattern the backend declared: `ai:{sessionId}`. */
14
+ pattern: string;
15
+ /** The `{param}` names, in the pattern's order: a join takes exactly these. */
16
+ params: P;
17
+ /** The declared event names: what a handle listens to. */
18
+ events: readonly string[];
19
+ /** The declared message names: what a handle sends. */
20
+ messages: readonly string[];
21
+ send: S;
22
+ event: E;
23
+ }
24
+ /** What `on` returns: called, it stops that callback (`off` does the same). */
25
+ export type RoomSubscription = () => void;
26
+ /** A live room. Hold it for as long as the view is on screen, and `leave` it when the view goes: an occupied room is one its backend is still running for. */
27
+ export interface RoomHandle<E extends RoomEvent, S> {
28
+ /** The channel this handle is on: `ai:42`. */
29
+ readonly topic: string;
30
+ /** Every event the room declares, as a union to switch over. */
31
+ on(handler: (event: E) => void): RoomSubscription;
32
+ /** One event by its name, its payload typed. */
33
+ on<K extends E['event']>(event: K, handler: (payload: Extract<E, {
34
+ event: K;
35
+ }>['payload']) => void): RoomSubscription;
36
+ /** Stops one callback. */
37
+ off(subscription: RoomSubscription): void;
38
+ /**
39
+ * Sends one declared message and waits for the room's backend to take it;
40
+ * rejects with a RoomError — its reason — when it did not.
41
+ */
42
+ send<K extends keyof S & string>(message: K, payload: S[K]): Promise<void>;
43
+ /** Drops every callback of this handle and lets go of the room; the channel is left with its last handle. */
44
+ leave(): void;
45
+ }
46
+ /**
47
+ * Why a room did not do what was asked. `reason` is the server's: for a join,
48
+ * `unauthorized` (the backend did not let the user in) or `unavailable` (it
49
+ * could not be asked: try again); for a message, the reason the backend gave,
50
+ * `forbidden` when it gave none or the user may only listen, `rate_limited`,
51
+ * `unavailable`. When no answer came it is `no reply` or `socket closed` —
52
+ * the message may have been taken — and `not joined` when nothing was sent:
53
+ * the handle left, its socket closed (a sign-out, another user's sign-in), or
54
+ * the socket dropped and is being opened again.
55
+ */
56
+ export declare class RoomError extends Error {
57
+ readonly reason: string;
58
+ constructor(message: string, reason: string);
59
+ }
60
+ /** Joins `room` on `socket`: resolves once the backend let the user in, rejects with a RoomError when it did not. */
61
+ export declare function joinRoom<E extends RoomEvent, S, P extends readonly string[]>(socket: RealtimeSocket, room: RoomDescriptor<E, S, P>, params: Record<P[number], string>): Promise<RoomHandle<E, S>>;
@@ -0,0 +1,91 @@
1
+ export interface Session {
2
+ accessToken: string;
3
+ refreshToken: string;
4
+ /** Epoch milliseconds the access token stops being accepted. */
5
+ expiresAt: number;
6
+ /** The user the session is (the access token's sub). */
7
+ userId: string;
8
+ user?: unknown;
9
+ }
10
+ export interface SessionStore {
11
+ load(): Session | null | Promise<Session | null>;
12
+ save(session: Session): void | Promise<void>;
13
+ clear(): void | Promise<void>;
14
+ /** Shared with other tabs (localStorage): refreshes take a lock across them. */
15
+ readonly shared?: boolean;
16
+ /** Calls back when another tab changed the stored session; returns the unsubscribe. */
17
+ watch?(onChange: () => void): () => void;
18
+ }
19
+ export type AuthEvent = 'SIGNED_IN' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'SIGNED_OUT' | 'SESSION_EXPIRED';
20
+ export interface TokenPair {
21
+ access_token: string;
22
+ refresh_token: string;
23
+ expires_in: number;
24
+ }
25
+ /** The session changed under a call (another tab signed out, or in as someone else): the call is not made again. */
26
+ export declare class SessionChangedError extends Error {
27
+ readonly code = "session_changed";
28
+ constructor(message: string);
29
+ }
30
+ export declare function memoryStore(): SessionStore;
31
+ /**
32
+ * localStorage under one key. Storage that cannot be read or written (full,
33
+ * blocked, a private mode) keeps the session in memory for this page rather
34
+ * than losing it.
35
+ */
36
+ export declare function localStore(key: string): SessionStore;
37
+ /** The user an access token is: its sub claim (read, not verified — the server verifies). */
38
+ export declare function subjectOf(accessToken: string): string;
39
+ /** A refusal of the refresh token itself (rotated away, revoked, expired): the session is over. */
40
+ export type IsRejected = (e: unknown) => boolean;
41
+ export interface SessionManagerOptions {
42
+ now?: () => number;
43
+ lockName?: string;
44
+ refreshTimeoutMs?: number;
45
+ }
46
+ export declare class SessionManager {
47
+ private current;
48
+ private loaded;
49
+ private inflight;
50
+ private listeners;
51
+ private store;
52
+ private refresher;
53
+ private isRejected;
54
+ private now;
55
+ private lockName;
56
+ private refreshTimeoutMs;
57
+ constructor(store: SessionStore, refresher: (refreshToken: string) => Promise<TokenPair>, isRejected: IsRejected, opts?: SessionManagerOptions);
58
+ session(): Promise<Session | null>;
59
+ /** The session as it is now, without waiting (null before the store has loaded). */
60
+ snapshot(): Session | null;
61
+ onChange(listener: (event: AuthEvent, session: Session | null) => void): () => void;
62
+ /** A new session (SIGNED_IN: this user's alone, never the previous one's profile) or a refreshed one. */
63
+ set(pair: TokenPair, event: 'SIGNED_IN' | 'TOKEN_REFRESHED', user?: {
64
+ id?: string;
65
+ }): Promise<Session>;
66
+ /** The profile of the user it was read for; dropped when someone else is signed in by now. */
67
+ setUser(user: unknown, readFor: Session | null): Promise<void>;
68
+ /**
69
+ * Ends this tab's session. The store is cleared when it holds this user's
70
+ * session — the one this tab holds or the same user's, refreshed by another
71
+ * tab: a sign-out signs the user out of every tab. It is left alone only
72
+ * when it holds another user's (someone else signed in since).
73
+ */
74
+ clear(event: 'SIGNED_OUT' | 'SESSION_EXPIRED'): Promise<void>;
75
+ /** The access token to send now; undefined when no one is signed in. */
76
+ accessToken(): Promise<string | undefined>;
77
+ /** One refresh at a time — in this tab, and across tabs when the store is shared. */
78
+ refresh(): Promise<Session>;
79
+ /**
80
+ * What another tab did to the stored session: signed out (this tab signs
81
+ * out), signed in as someone else (this tab says SIGNED_OUT then
82
+ * SIGNED_IN, and the caller must not repeat its call), or refreshed it for
83
+ * the same user (taken as it is). Undefined when the store still holds this
84
+ * tab's session.
85
+ */
86
+ private adoptStored;
87
+ /** Another tab wrote the store: follow it now rather than at the next refresh. */
88
+ private reconcile;
89
+ private withTimeout;
90
+ private emit;
91
+ }