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.
- package/LICENSE +27 -0
- package/README.md +2 -2
- package/dist/ai/index.js +17 -0
- package/dist/backend/engine.js +2 -0
- package/dist/backend/env.js +1 -0
- package/dist/backend/index.js +12 -0
- package/dist/backend/stack.js +1 -0
- package/dist/backend/test.js +2 -0
- package/dist/chunks/5W6XJM46.js +13 -0
- package/dist/chunks/6OPBXB7Z.js +2 -0
- package/dist/chunks/7LOMYBLN.js +20 -0
- package/dist/chunks/AN5LCF5K.js +55 -0
- package/dist/chunks/ECUYQ73P.js +2 -0
- package/dist/chunks/HDX6YUAB.js +2 -0
- package/dist/chunks/HVC2C35F.js +2 -0
- package/dist/chunks/I3JNX6MZ.js +4 -0
- package/dist/chunks/JEJ5CFTK.js +2 -0
- package/dist/chunks/K3C2IEDS.js +2 -0
- package/dist/chunks/KRBVYYME.js +2 -0
- package/dist/chunks/NIJHTHNF.js +2 -0
- package/dist/chunks/TL4H6V3P.js +2 -0
- package/dist/chunks/UTRQWCMD.js +6 -0
- package/dist/chunks/ZFPDYRRY.js +2 -0
- package/dist/cli.js +815 -0
- package/dist/refuse/ai.js +2 -0
- package/dist/refuse/backend.js +2 -0
- package/dist/refuse/server.js +2 -0
- package/dist/server/index.js +2 -0
- package/dist/server/testing.js +2 -0
- package/dist/web/index.js +2 -0
- package/dist/web/palbe.js +2 -0
- package/dist/web/paywall.js +2 -0
- package/dist/web/vite.js +3 -0
- package/package.json +111 -4
- package/types/ai/src/ai.d.ts +178 -0
- package/types/ai/src/index.d.ts +5 -0
- package/types/ai/src/partial.d.ts +1 -0
- package/types/ai/src/schema.d.ts +47 -0
- package/types/backend/src/bootstrap.d.ts +72 -0
- package/types/backend/src/cache.d.ts +32 -0
- package/types/backend/src/container.d.ts +49 -0
- package/types/backend/src/database.d.ts +148 -0
- package/types/backend/src/db-env.d.ts +4 -0
- package/types/backend/src/decorators.d.ts +293 -0
- package/types/backend/src/engine-db.d.ts +24 -0
- package/types/backend/src/engine-exports.d.ts +3 -0
- package/types/backend/src/engine.d.ts +124 -0
- package/types/backend/src/env.d.ts +27 -0
- package/types/backend/src/errors.d.ts +70 -0
- package/types/backend/src/i18n.d.ts +21 -0
- package/types/backend/src/index.d.ts +23 -0
- package/types/backend/src/openapi.d.ts +73 -0
- package/types/backend/src/platform-calls.d.ts +41 -0
- package/types/backend/src/requests.d.ts +81 -0
- package/types/backend/src/rooms.d.ts +168 -0
- package/types/backend/src/router.d.ts +22 -0
- package/types/backend/src/runtime.d.ts +56 -0
- package/types/backend/src/services.d.ts +262 -0
- package/types/backend/src/stack.d.ts +20 -0
- package/types/backend/src/strings-files.d.ts +3 -0
- package/types/backend/src/testing/index.d.ts +3 -0
- package/types/backend/src/testing/unit.d.ts +66 -0
- package/types/backend/src/upload.d.ts +125 -0
- package/types/backend/src/webhooks.d.ts +113 -0
- package/types/internal/src/event.d.ts +9 -0
- package/types/internal/src/modules/ai.d.ts +678 -0
- package/types/internal/src/modules/analytics.d.ts +1414 -0
- package/types/internal/src/modules/auth.d.ts +5660 -0
- package/types/internal/src/modules/cache.d.ts +158 -0
- package/types/internal/src/modules/events.d.ts +19 -0
- package/types/internal/src/modules/flags.d.ts +957 -0
- package/types/internal/src/modules/index.d.ts +13 -0
- package/types/internal/src/modules/machines.d.ts +1584 -0
- package/types/internal/src/modules/notify.d.ts +1450 -0
- package/types/internal/src/modules/purchases.d.ts +4113 -0
- package/types/internal/src/modules/realtime.d.ts +326 -0
- package/types/internal/src/modules/secrets.d.ts +275 -0
- package/types/internal/src/modules/storage.d.ts +691 -0
- package/types/internal/src/modules/stores.d.ts +1516 -0
- package/types/internal/src/retry.d.ts +14 -0
- package/types/internal/src/transport.d.ts +106 -0
- package/types/server/src/auth/dpop.d.ts +47 -0
- package/types/server/src/auth/jwt.d.ts +115 -0
- package/types/server/src/index.d.ts +9 -0
- package/types/server/src/pages.d.ts +4 -0
- package/types/server/src/purchases.d.ts +51 -0
- package/types/server/src/secrets-env.d.ts +22 -0
- package/types/server/src/server.d.ts +40 -0
- package/types/server/src/testing.d.ts +91 -0
- package/types/server/src/webhooks.d.ts +72 -0
- package/types/web/src/analytics.d.ts +87 -0
- package/types/web/src/auth.d.ts +155 -0
- package/types/web/src/backend-error.d.ts +38 -0
- package/types/web/src/backend-names.d.ts +25 -0
- package/types/web/src/backends.d.ts +84 -0
- package/types/web/src/client.d.ts +51 -0
- package/types/web/src/index.d.ts +13 -0
- package/types/web/src/palbe.d.ts +2 -0
- package/types/web/src/paywall/icons.d.ts +5 -0
- package/types/web/src/paywall/index.d.ts +5 -0
- package/types/web/src/paywall/placement.d.ts +85 -0
- package/types/web/src/paywall/prices.d.ts +40 -0
- package/types/web/src/paywall/render.d.ts +54 -0
- package/types/web/src/paywall/resolve.d.ts +149 -0
- package/types/web/src/purchases.d.ts +86 -0
- package/types/web/src/rooms.d.ts +61 -0
- package/types/web/src/session.d.ts +91 -0
- package/types/web/src/socket.d.ts +87 -0
- package/types/web/src/vite.d.ts +49 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { purchases } from '../../../internal/src/modules/index.js';
|
|
2
|
+
export type PaywallDefinition = purchases.PaywallDefinition;
|
|
3
|
+
/** The newest schema of definition this renderer draws: what the SDK asks the server for (X-Palbase-Paywall-Schema). */
|
|
4
|
+
export declare const PAYWALL_SCHEMA = 1;
|
|
5
|
+
export type Appearance = 'light' | 'dark';
|
|
6
|
+
/** What the store gave a package, as the variables of its texts: `{{price}}` … */
|
|
7
|
+
export interface PackageValues {
|
|
8
|
+
price?: string;
|
|
9
|
+
price_per_month?: string;
|
|
10
|
+
period?: string;
|
|
11
|
+
trial?: string;
|
|
12
|
+
savings?: string;
|
|
13
|
+
product_name?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolveOptions {
|
|
16
|
+
/** The app's language (`tr-TR`): the definition's own, else its language without the region, else its default. */
|
|
17
|
+
locale: string;
|
|
18
|
+
appearance: Appearance;
|
|
19
|
+
/** The store's values of each package, by `<product>.<plan>`. */
|
|
20
|
+
packages: Record<string, PackageValues>;
|
|
21
|
+
/**
|
|
22
|
+
* The package selected now (`<product>.<plan>`), once the person picked
|
|
23
|
+
* one; the one the definition marks, else the first, when absent or not
|
|
24
|
+
* one of its packages.
|
|
25
|
+
*/
|
|
26
|
+
selected?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ResolvedTheme {
|
|
29
|
+
background: string;
|
|
30
|
+
surface: string;
|
|
31
|
+
text: string;
|
|
32
|
+
muted: string;
|
|
33
|
+
accent: string;
|
|
34
|
+
on_accent: string;
|
|
35
|
+
badge: string;
|
|
36
|
+
radius: number;
|
|
37
|
+
font: 'system' | 'rounded' | 'serif';
|
|
38
|
+
}
|
|
39
|
+
type Base = {
|
|
40
|
+
id?: string;
|
|
41
|
+
};
|
|
42
|
+
export type Align = 'start' | 'center' | 'end' | 'stretch';
|
|
43
|
+
export type ResolvedStack = Base & {
|
|
44
|
+
type: 'stack';
|
|
45
|
+
axis: 'vertical' | 'horizontal' | 'z';
|
|
46
|
+
spacing: number;
|
|
47
|
+
align: Align;
|
|
48
|
+
padding: number;
|
|
49
|
+
corner: number;
|
|
50
|
+
background?: string;
|
|
51
|
+
children: ResolvedComponent[];
|
|
52
|
+
};
|
|
53
|
+
export type TextStyle = 'large_title' | 'title' | 'headline' | 'body' | 'callout' | 'caption' | 'footnote';
|
|
54
|
+
export type ResolvedText = Base & {
|
|
55
|
+
type: 'text';
|
|
56
|
+
text: string;
|
|
57
|
+
style: TextStyle;
|
|
58
|
+
color: string;
|
|
59
|
+
align: 'start' | 'center' | 'end';
|
|
60
|
+
weight?: 'regular' | 'medium' | 'semibold' | 'bold';
|
|
61
|
+
};
|
|
62
|
+
export type ResolvedImage = Base & {
|
|
63
|
+
type: 'image';
|
|
64
|
+
src: string;
|
|
65
|
+
fit: 'fit' | 'fill';
|
|
66
|
+
corner: number;
|
|
67
|
+
aspect?: number;
|
|
68
|
+
height?: number;
|
|
69
|
+
alt?: string;
|
|
70
|
+
};
|
|
71
|
+
export type ResolvedIcon = Base & {
|
|
72
|
+
type: 'icon';
|
|
73
|
+
name: string;
|
|
74
|
+
color: string;
|
|
75
|
+
size: number;
|
|
76
|
+
};
|
|
77
|
+
export type ResolvedFeature = {
|
|
78
|
+
icon: string;
|
|
79
|
+
text: string;
|
|
80
|
+
};
|
|
81
|
+
export type ResolvedFeatures = Base & {
|
|
82
|
+
type: 'features';
|
|
83
|
+
items: ResolvedFeature[];
|
|
84
|
+
icon_color: string;
|
|
85
|
+
color: string;
|
|
86
|
+
spacing: number;
|
|
87
|
+
};
|
|
88
|
+
export type ResolvedPackage = {
|
|
89
|
+
product: string;
|
|
90
|
+
plan: string;
|
|
91
|
+
selected: boolean;
|
|
92
|
+
title?: string;
|
|
93
|
+
detail?: string;
|
|
94
|
+
badge?: string;
|
|
95
|
+
};
|
|
96
|
+
export type ResolvedPackages = Base & {
|
|
97
|
+
type: 'packages';
|
|
98
|
+
layout: 'list' | 'cards' | 'segmented';
|
|
99
|
+
items: ResolvedPackage[];
|
|
100
|
+
};
|
|
101
|
+
export type ResolvedPurchaseButton = Base & {
|
|
102
|
+
type: 'purchase_button';
|
|
103
|
+
text: string;
|
|
104
|
+
sub_text?: string;
|
|
105
|
+
};
|
|
106
|
+
export type ResolvedRestoreButton = Base & {
|
|
107
|
+
type: 'restore_button';
|
|
108
|
+
text?: string;
|
|
109
|
+
};
|
|
110
|
+
export type ResolvedLink = Base & {
|
|
111
|
+
type: 'link';
|
|
112
|
+
text: string;
|
|
113
|
+
url: string;
|
|
114
|
+
};
|
|
115
|
+
export type ResolvedCloseButton = Base & {
|
|
116
|
+
type: 'close_button';
|
|
117
|
+
color: string;
|
|
118
|
+
};
|
|
119
|
+
export type ResolvedSpacer = Base & {
|
|
120
|
+
type: 'spacer';
|
|
121
|
+
size?: number;
|
|
122
|
+
};
|
|
123
|
+
export type ResolvedDivider = Base & {
|
|
124
|
+
type: 'divider';
|
|
125
|
+
color: string;
|
|
126
|
+
};
|
|
127
|
+
export type ResolvedComponent = ResolvedStack | ResolvedText | ResolvedImage | ResolvedIcon | ResolvedFeatures | ResolvedPackages | ResolvedPurchaseButton | ResolvedRestoreButton | ResolvedLink | ResolvedCloseButton | ResolvedSpacer | ResolvedDivider;
|
|
128
|
+
/** The definition as drawn: the palette of the appearance and the tree, nothing left to look up. */
|
|
129
|
+
export interface ResolvedPaywall {
|
|
130
|
+
theme: ResolvedTheme;
|
|
131
|
+
root: ResolvedComponent;
|
|
132
|
+
}
|
|
133
|
+
/** Each component of the schema, by type: what a definition may hold and this renderer draws. */
|
|
134
|
+
export declare const PAYWALL_COMPONENTS: readonly string[];
|
|
135
|
+
/** The package a definition selects at first: the one it marks, else the first of its packages component. */
|
|
136
|
+
export declare function initialPackage(root: unknown): string;
|
|
137
|
+
/** Every package a definition sells, `<product>.<plan>`, in its order. */
|
|
138
|
+
export declare function packagesOf(root: unknown): string[];
|
|
139
|
+
/** The language a definition is drawn in for an app's: its own, else without the region, else the default. */
|
|
140
|
+
export declare function paywallLanguage(definition: PaywallDefinition, locale: string): string;
|
|
141
|
+
/** A definition as drawn (contracts/paywall/README.md). */
|
|
142
|
+
export declare function resolve(definition: PaywallDefinition, opts: ResolveOptions): ResolvedPaywall;
|
|
143
|
+
/**
|
|
144
|
+
* The same, with a component that is not drawn left in its place as null:
|
|
145
|
+
* two resolutions of one definition line up child by child, so the renderer
|
|
146
|
+
* changes only what a new selection changed.
|
|
147
|
+
*/
|
|
148
|
+
export declare function resolveAligned(definition: PaywallDefinition, opts: ResolveOptions): ResolvedPaywall;
|
|
149
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
import { type Paywall } from './paywall/placement.js';
|
|
6
|
+
type P = typeof purchasesModule;
|
|
7
|
+
export type CustomerInfo = purchasesModule.CustomerInfo;
|
|
8
|
+
export type Offerings = purchasesModule.Offerings;
|
|
9
|
+
export type MergeGrant = purchasesModule.MergeGrant;
|
|
10
|
+
/** Told what the customer has: on the first read, then each time it changes; null while no one is signed in. */
|
|
11
|
+
export type CustomerInfoListener = (info: CustomerInfo | null) => void;
|
|
12
|
+
export declare class Purchases {
|
|
13
|
+
/** Every client operation of the purchases contract, through the session's transport. */
|
|
14
|
+
readonly api: ReturnType<P['client']>;
|
|
15
|
+
private session;
|
|
16
|
+
private socket;
|
|
17
|
+
private ref;
|
|
18
|
+
private listeners;
|
|
19
|
+
private watch;
|
|
20
|
+
private following;
|
|
21
|
+
/** What listeners were last told: undefined until the first read (or sign-out) is known. */
|
|
22
|
+
private last;
|
|
23
|
+
constructor(transport: Transport, session: SessionManager, socket: RealtimeSocket, ref: string);
|
|
24
|
+
/** Everything the signed-in customer has, read now (the first read makes the user a customer). */
|
|
25
|
+
me(): Promise<CustomerInfo>;
|
|
26
|
+
/** The paywall's catalog: each product's text and price on a channel — the web's unless another is named. */
|
|
27
|
+
offerings(channel?: 'ios' | 'android' | 'web'): Promise<Offerings>;
|
|
28
|
+
/**
|
|
29
|
+
* The paywall the project shows at a placement — the place in the app it
|
|
30
|
+
* names (`onboarding`, `settings`) — to draw with render(). Its version
|
|
31
|
+
* is the newest published in a schema this SDK draws (an experiment's
|
|
32
|
+
* variant while one runs there, the same for this person every time); the
|
|
33
|
+
* browser keeps it, to ask again with its ETag and to show offline. With
|
|
34
|
+
* nothing published there it is the SDK's own, of every plan the web
|
|
35
|
+
* sells; null when the web sells nothing.
|
|
36
|
+
*/
|
|
37
|
+
paywall(placement: string, opts?: {
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}): Promise<Paywall | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Opens Stripe Checkout for the signed-in customer (not a guest) to buy a
|
|
42
|
+
* product's plan sold on the web: the URL to send them to. Stripe sends
|
|
43
|
+
* them back to successUrl or cancelUrl — one of the project's registered
|
|
44
|
+
* apps. The purchase is theirs once Stripe reports it: onCustomerInfo hears it.
|
|
45
|
+
*/
|
|
46
|
+
checkout(opts: {
|
|
47
|
+
product: string;
|
|
48
|
+
plan: string;
|
|
49
|
+
successUrl: string;
|
|
50
|
+
cancelUrl: string;
|
|
51
|
+
}): Promise<string>;
|
|
52
|
+
/** Stripe's customer portal for what the customer bought on the web: the URL to send them to, and back to returnUrl. */
|
|
53
|
+
portal(returnUrl: string): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Asked with a guest's session, before it signs in to an existing account:
|
|
56
|
+
* the proof — good for 5 minutes — that whoever signs in held the guest.
|
|
57
|
+
* Spent with merge() once signed in. Refused for a session that is not a
|
|
58
|
+
* guest's (403 guest_required).
|
|
59
|
+
*/
|
|
60
|
+
mergeGrant(): Promise<MergeGrant>;
|
|
61
|
+
/**
|
|
62
|
+
* With the account's session: everything the guest bought goes to the
|
|
63
|
+
* account, for good; what the account has now. Asked again with the same
|
|
64
|
+
* grant, it is the same merge.
|
|
65
|
+
*/
|
|
66
|
+
merge(grant: MergeGrant | string): Promise<CustomerInfo>;
|
|
67
|
+
/**
|
|
68
|
+
* Listens to what the signed-in customer has: told it once read, then each
|
|
69
|
+
* time it changes — a purchase in any store, a renewal or its end, a
|
|
70
|
+
* refund, credits spent — and null while no one is signed in; a new user
|
|
71
|
+
* signing in is read anew. The first listener joins the customer's realtime
|
|
72
|
+
* channel, the last one's unsubscribe leaves it. A read that fails is left
|
|
73
|
+
* until the next change.
|
|
74
|
+
*/
|
|
75
|
+
onCustomerInfo(listener: CustomerInfoListener): () => void;
|
|
76
|
+
private start;
|
|
77
|
+
private stop;
|
|
78
|
+
private follow;
|
|
79
|
+
private unfollow;
|
|
80
|
+
private changed;
|
|
81
|
+
private held;
|
|
82
|
+
private read;
|
|
83
|
+
private offer;
|
|
84
|
+
private tell;
|
|
85
|
+
}
|
|
86
|
+
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
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export type Frame = [joinRef: string | null, ref: string | null, topic: string, event: string, payload: Record<string, unknown>];
|
|
2
|
+
export interface SocketOptions {
|
|
3
|
+
/** https://<ref>.<domain> */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
publishableKey: string;
|
|
6
|
+
accessToken: () => Promise<string | undefined>;
|
|
7
|
+
/** The WebSocket constructor (globalThis.WebSocket by default). */
|
|
8
|
+
webSocket?: typeof WebSocket;
|
|
9
|
+
/** Pauses before each reconnection attempt, in ms; the last repeats. Jittered ±20%. */
|
|
10
|
+
backoff?: number[];
|
|
11
|
+
heartbeatMs?: number;
|
|
12
|
+
replyTimeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export type ChannelState = 'closed' | 'joining' | 'joined' | 'refused';
|
|
15
|
+
/**
|
|
16
|
+
* What became of a push: the server's answer — `ok` with its response, or
|
|
17
|
+
* `error` with `{ reason }` — or, when none came, `timeout` (`no reply`: the
|
|
18
|
+
* server may have taken it) or `error` with `socket closed` or `not joined`.
|
|
19
|
+
*/
|
|
20
|
+
export interface PushReply {
|
|
21
|
+
status: string;
|
|
22
|
+
response: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
type Listener = (payload: Record<string, unknown>) => void;
|
|
25
|
+
export declare class Channel {
|
|
26
|
+
state: ChannelState;
|
|
27
|
+
/** Why the server refused the last join, when it did. */
|
|
28
|
+
refusal?: Record<string, unknown>;
|
|
29
|
+
joinRef: string | null;
|
|
30
|
+
private listeners;
|
|
31
|
+
private closers;
|
|
32
|
+
private joinWaiters;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
private socket;
|
|
35
|
+
constructor(name: string, socket: RealtimeSocket);
|
|
36
|
+
get topic(): string;
|
|
37
|
+
/** Resolves once joined; rejects with the server's reason when refused. */
|
|
38
|
+
join(): Promise<void>;
|
|
39
|
+
leave(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Sends one event on the joined channel and resolves with what became of it
|
|
42
|
+
* (PushReply) — never rejects for an answer, whatever it says. A channel
|
|
43
|
+
* that is not joined sends nothing. The server answers what it takes or
|
|
44
|
+
* refuses of a room's message and what it refuses of any other; a broadcast
|
|
45
|
+
* it took on a channel that is no room is answered by no one.
|
|
46
|
+
*/
|
|
47
|
+
push(event: string, payload: Record<string, unknown>): Promise<PushReply>;
|
|
48
|
+
/** Listens to an event on this channel (a broadcast is the event "broadcast"); returns the unsubscribe. */
|
|
49
|
+
on(event: string, listener: Listener): () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Listens for the end of what was joined here as the socket's user: the
|
|
52
|
+
* socket closed for good (the user signed out, or someone signed in) — not a
|
|
53
|
+
* drop, after which the channel is joined again. Returns the unsubscribe.
|
|
54
|
+
*/
|
|
55
|
+
onClosed(listener: () => void): () => void;
|
|
56
|
+
}
|
|
57
|
+
export declare class RealtimeSocket {
|
|
58
|
+
private ws;
|
|
59
|
+
private opening;
|
|
60
|
+
private ref;
|
|
61
|
+
private channels;
|
|
62
|
+
private replies;
|
|
63
|
+
private heartbeat;
|
|
64
|
+
private heartbeatRef;
|
|
65
|
+
private reconnectTimer;
|
|
66
|
+
private attempts;
|
|
67
|
+
private closedByUs;
|
|
68
|
+
/** Counted for the app's own diagnostics: how many times the socket was opened again after a drop. */
|
|
69
|
+
reconnects: number;
|
|
70
|
+
private opts;
|
|
71
|
+
constructor(opts: SocketOptions);
|
|
72
|
+
channel(name: string): Channel;
|
|
73
|
+
/** Closes the socket; every channel is closed (the app joins again — as the next user, if one signs in). */
|
|
74
|
+
disconnect(): void;
|
|
75
|
+
/** A new token for every joined channel (the session was refreshed). */
|
|
76
|
+
tokenChanged(): Promise<void>;
|
|
77
|
+
private open;
|
|
78
|
+
private dropped;
|
|
79
|
+
private sendJoin;
|
|
80
|
+
private awaitReply;
|
|
81
|
+
private receive;
|
|
82
|
+
private push;
|
|
83
|
+
private nextRef;
|
|
84
|
+
private startHeartbeat;
|
|
85
|
+
private stopHeartbeat;
|
|
86
|
+
}
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface PalbaseViteOptions {
|
|
2
|
+
/** The app's slug (palbase apps:list): its file in palbase/. */
|
|
3
|
+
app: string;
|
|
4
|
+
/** Each mode the app is built in (vite --mode), and the environment it is built against. */
|
|
5
|
+
environments: Record<string, string>;
|
|
6
|
+
/** Where palbase apps:sync writes; palbase by default. */
|
|
7
|
+
dir?: string;
|
|
8
|
+
/** The app's tsconfig, from its root: tsconfig.app.json where there is one, else tsconfig.json. */
|
|
9
|
+
tsconfig?: string;
|
|
10
|
+
/** Type-check the app against its environment before a build (true). */
|
|
11
|
+
typecheck?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface Warning {
|
|
14
|
+
code?: string;
|
|
15
|
+
exporter?: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
type Warn = (w: Warning) => void;
|
|
20
|
+
/** What a hook may ask of the bundler: to resolve an import as from another file. */
|
|
21
|
+
interface ResolveContext {
|
|
22
|
+
resolve(source: string, importer: string, options: {
|
|
23
|
+
skipSelf: boolean;
|
|
24
|
+
}): Promise<unknown>;
|
|
25
|
+
}
|
|
26
|
+
/** What the plugin is to Vite: its hooks, typed here so the package needs no Vite of its own. */
|
|
27
|
+
export interface PalbaseVitePlugin {
|
|
28
|
+
name: string;
|
|
29
|
+
enforce: 'pre';
|
|
30
|
+
config(user: {
|
|
31
|
+
build?: {
|
|
32
|
+
rollupOptions?: {
|
|
33
|
+
onwarn?: (w: Warning, warn: Warn) => void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}, env: {
|
|
37
|
+
mode: string;
|
|
38
|
+
command?: string;
|
|
39
|
+
}): unknown;
|
|
40
|
+
configResolved(config: {
|
|
41
|
+
root: string;
|
|
42
|
+
mode: string;
|
|
43
|
+
command?: string;
|
|
44
|
+
}): void;
|
|
45
|
+
buildStart(): Promise<void>;
|
|
46
|
+
resolveId(this: ResolveContext, source: string, importer?: string): string | null | Promise<unknown>;
|
|
47
|
+
}
|
|
48
|
+
export declare function palbase(options: PalbaseViteOptions): PalbaseVitePlugin;
|
|
49
|
+
export {};
|