@omen.dog/sdk 1.0.0 → 1.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.
package/README.md CHANGED
@@ -110,6 +110,78 @@ console.log(webhook.secret); // save this!
110
110
  const valid = await omen.webhooks.verify(rawBody, signature, secret);
111
111
  ```
112
112
 
113
+ ### Sparks (partner awards)
114
+
115
+ Award money-backed Sparks and catalog items to your users from a prepaid pool
116
+ (buy/manage it in the Developer Portal → Sparks Pool).
117
+
118
+ > **Approved Partner Apps only.** Awarding mints real Sparks, so `award`,
119
+ > `awardBatch`, and `items.awardCatalog` require the app to be an approved
120
+ > Partner App (Developer Portal → Partner Program). Unapproved apps get
121
+ > `403 partner_required`. Funding a pool does not require approval.
122
+
123
+ ```ts
124
+ // Award Sparks (pass idempotencyKey to make retries safe)
125
+ await omen.sparks.award({
126
+ userId, amount: 250, reason: 'Beat level 10',
127
+ idempotencyKey: `level10:${userId}`,
128
+ });
129
+
130
+ // Batch (≤100, best-effort per item)
131
+ await omen.sparks.awardBatch([
132
+ { userId: 'u1', amount: 100 },
133
+ { userId: 'u2', amount: 100 },
134
+ ]);
135
+
136
+ // Award an open-shop item or avatar trait — paid from your pool at list price
137
+ await omen.items.awardCatalog({ userId, catalogType: 'store', catalogItemId: 'theme_lava' });
138
+
139
+ // Pool status
140
+ const pool = await omen.sparks.pool();
141
+
142
+ // Mint a short-lived display token for the UI kit (backend only — needs your
143
+ // app's OAuth client secret). Pass it to <omen-sparks-balance>/<omen-inventory>.
144
+ const token = omen.sparks.displayToken({ userId, secret: process.env.OMEN_CLIENT_SECRET! });
145
+ ```
146
+
147
+ #### UI kit (embeddable widgets)
148
+
149
+ ```html
150
+ <script src="https://sdk.omen.dog/v1/ui-kit.js"></script>
151
+ <omen-sparks-balance token="DISPLAY_TOKEN"></omen-sparks-balance>
152
+ <omen-inventory token="DISPLAY_TOKEN" limit="24"></omen-inventory>
153
+ <script>OmenUI.toast({ amount: 250, message: 'Level cleared!' })</script>
154
+ ```
155
+
156
+ Restyle via CSS variables (`--omen-bg`, `--omen-accent`, `--omen-spark`, …),
157
+ `::part()` selectors, or `theme="bare"` to ship your own CSS entirely.
158
+
159
+ ### Shared avatar (Embedded Avatar Kit)
160
+
161
+ Put the shared Omen avatar inside your app — display it, let users edit it
162
+ in-place, browse the trait catalog, and gift traits from your Sparks pool.
163
+
164
+ ```ts
165
+ // Public render URL (pass version from avatar:saved / user.avatar_updated to bust caches)
166
+ const url = omen.avatar.renderUrl(userId, { version });
167
+
168
+ // Mint a short-lived editor token for <omen-avatar-editor> (backend only)
169
+ const token = omen.avatar.editorToken({ userId, secret: process.env.OMEN_CLIENT_SECRET! });
170
+
171
+ // Enumerate the shared catalog — trait ids, names, rarities, ✦ prices (public, no auth).
172
+ // Pass { editorToken } and each trait's `locked` reflects that user's ownership.
173
+ const { catalog, rarityPricing } = await omen.avatar.catalog();
174
+
175
+ // Gift a trait app-funded — your pool pays list price, the user pays nothing
176
+ await omen.items.awardCatalog({
177
+ userId, catalogType: 'avatar_trait', catalogItemId: 'hair/export-01.svg',
178
+ });
179
+ ```
180
+
181
+ Trait ids are `category/file.svg` paths and double as public preview SVGs at
182
+ `https://omen.dog/avatar/{traitId}`. Common traits are free for everyone and
183
+ can't be awarded.
184
+
113
185
  ## Creation Runtime Types
114
186
 
115
187
  For creation developers — get autocomplete for the `omen.*` global:
@@ -121,6 +193,42 @@ const save = await omen.load();
121
193
  omen.score(100);
122
194
  ```
123
195
 
196
+ ## Child Re-Login (`ChildLogin`)
197
+
198
+ A safe, thin wrapper over the child device-grant flow for Tier-3 apps that sign
199
+ in Omen child accounts (F263). It's a separate export — it uses your OAuth
200
+ `clientId`/`clientSecret`, not a `dev_` token — and is also available from the
201
+ server-only subpath `@omen.dog/sdk/child-login`. `scope:'child'` is always sent,
202
+ RFC 8628 errors map to a calm status union instead of throwing, and scope is
203
+ preserved across refresh rotation. Tokens never leave your server.
204
+
205
+ ```ts
206
+ import { ChildLogin } from '@omen.dog/sdk/child-login';
207
+
208
+ const child = new ChildLogin({
209
+ clientId: process.env.OMEN_CLIENT_ID!,
210
+ clientSecret: process.env.OMEN_CLIENT_SECRET!, // omit for public/PKCE clients
211
+ webhookSecret: process.env.OMEN_WEBHOOK_SECRET!,
212
+ });
213
+
214
+ const grant = await child.deviceStart({ deviceName: "Astrid's iPad" });
215
+ await child.notify({ device_code: grant.device_code, username: 'astrid' }); // cold start
216
+ const res = await child.pollUntil(grant.device_code); // honors interval + slow_down
217
+ if (res.status === 'approved') {
218
+ const session = child.seal(res.tokens!); // store in your httpOnly session; res.rebind = card data
219
+ }
220
+
221
+ // flip the child's screen the instant a parent approves:
222
+ if (child.verifyWebhook(rawBody, req.headers['x-omen-signature'])) { /* notify the device */ }
223
+ ```
224
+
225
+ Pair it with the in-context `<omen-child-login>` web component (zero-dep, themeable,
226
+ talks only to your own routes). See [omen.dog/docs#child-relogin-kit](https://omen.dog/docs).
227
+
228
+ The canonical child-side state machine is exported too (`deriveInitialState`,
229
+ `reduce`, `displayGroup`, `CHILD_LOGIN_STATES`) for apps building custom UI on the
230
+ same kindness.
231
+
124
232
  ## Error Handling
125
233
 
126
234
  ```ts
@@ -0,0 +1,230 @@
1
+ type ChildPollStatus =
2
+ | 'approved'
3
+ | 'pending'
4
+ | 'slow_down'
5
+ | 'denied'
6
+ | 'expired'
7
+ | 'error';
8
+
9
+ interface ChildTokens {
10
+ access_token: string;
11
+ refresh_token?: string;
12
+ token_type: string;
13
+ expires_in?: number;
14
+ scope?: string;
15
+ }
16
+
17
+ interface ChildRebind {
18
+ child_id: string;
19
+ display_name: string;
20
+ avatar_url: string | null;
21
+ suggested_device_name: string;
22
+ }
23
+
24
+ interface PollResult {
25
+ status: ChildPollStatus;
26
+ tokens?: ChildTokens;
27
+ rebind?: ChildRebind | null;
28
+ deviceToken?: string | null;
29
+ error?: string;
30
+ }
31
+
32
+ interface SealedSession {
33
+ accessToken: string;
34
+ refreshToken: string | null;
35
+ scope: string;
36
+ accessExpiresAt: number;
37
+ deviceToken?: string;
38
+ }
39
+
40
+ interface ChildLoginApprovedPayload {
41
+ child_id: string;
42
+ device_id: string | null;
43
+ device_name: string | null;
44
+ approved_at: string;
45
+ }
46
+
47
+ type ChildLoginState =
48
+ | 'trusted'
49
+ | 'known'
50
+ | 'cold'
51
+ | 'pending'
52
+ | 'approved'
53
+ | 'denied'
54
+ | 'blocked'
55
+ | 'paused'
56
+ | 'expired'
57
+ | 'offline';
58
+
59
+ type ChildLoginSignal =
60
+ | 'ask'
61
+ | 'pending'
62
+ | 'slow_down'
63
+ | 'approved'
64
+ | 'denied'
65
+ | 'blocked'
66
+ | 'paused'
67
+ | 'expired'
68
+ | 'offline'
69
+ | 'online'
70
+ | 'reset';
71
+
72
+ type ChildLoginDisplayGroup =
73
+ | 'ready'
74
+ | 'waiting'
75
+ | 'approved'
76
+ | 'ask-grown-up'
77
+ | 'expired'
78
+ | 'offline';
79
+
80
+ interface ChildLoginContext {
81
+ online?: boolean;
82
+ trustedDevice?: boolean;
83
+ identity?: unknown | null;
84
+ }
85
+
86
+ declare const CHILD_LOGIN_STATES: ChildLoginState[];
87
+ declare const ASK_GROWN_UP_STATES: ChildLoginState[];
88
+ declare const ENTRY_STATES: ChildLoginState[];
89
+
90
+ declare function deriveInitialState(ctx?: ChildLoginContext): ChildLoginState;
91
+ declare function reduce(state: ChildLoginState, signal: ChildLoginSignal, ctx?: ChildLoginContext): ChildLoginState;
92
+ declare function displayGroup(state: ChildLoginState): ChildLoginDisplayGroup;
93
+
94
+ interface ChildLoginOptions {
95
+ /** Your OAuth client_id (from the Developer Portal). Required. */
96
+ clientId: string;
97
+ /** Your client_secret. Omit for public (PKCE) clients. */
98
+ clientSecret?: string;
99
+ /** Base URL for the Omen API. Defaults to `https://omen.dog`. */
100
+ baseUrl?: string;
101
+ /** The endpoint secret for your `child.login.approved` webhook (enables `verifyWebhook`). */
102
+ webhookSecret?: string;
103
+ }
104
+ interface DeviceStartOptions {
105
+ /** The child's Omen userId from a previous session. Omit for a cold start (parent picks). */
106
+ loginHint?: string;
107
+ /** Stable per-device id you generate and persist (enables trusted-device auto-approval). */
108
+ deviceId?: string;
109
+ /** Human-readable device name shown to the parent ("Astrid's iPad"). */
110
+ deviceName?: string;
111
+ /** A trusted-device token from a prior approval — auto-approves on first poll. */
112
+ deviceToken?: string;
113
+ }
114
+ interface DeviceStartResult {
115
+ device_code: string;
116
+ user_code: string;
117
+ verification_uri: string;
118
+ verification_uri_complete: string;
119
+ expires_in: number;
120
+ interval: number;
121
+ }
122
+ interface ChildIdentity {
123
+ child: {
124
+ display_name: string;
125
+ avatar_url: string | null;
126
+ };
127
+ guardians: Array<{
128
+ display_name: string;
129
+ avatar_url: string | null;
130
+ }>;
131
+ }
132
+ interface PollUntilOptions {
133
+ /** Abort the wait (e.g. the child navigated away, or your webhook already flipped the screen). */
134
+ signal?: AbortSignal;
135
+ /** Called after every non-terminal poll with the current status — useful for logging. */
136
+ onStatus?: (status: PollResult['status']) => void;
137
+ /** Override the starting interval (seconds). Defaults to the grant's `interval`, or 5. */
138
+ intervalSeconds?: number;
139
+ }
140
+
141
+ /**
142
+ * F263 Child Re-Login — the official server-side primitive for third-party apps
143
+ * that sign in child accounts.
144
+ *
145
+ * It is a thin, safe wrapper over the Phase-1 device-grant API with the right
146
+ * defaults made non-optional: `scope:'child'` is always sent, RFC 8628 errors
147
+ * are mapped to a calm status union instead of thrown, and `scope` is preserved
148
+ * across refresh. Token custody stays on YOUR server — nothing here ever returns
149
+ * a token to the browser.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * import { ChildLogin } from '@omen.dog/sdk';
154
+ *
155
+ * const child = new ChildLogin({
156
+ * clientId: process.env.OMEN_CLIENT_ID!,
157
+ * clientSecret: process.env.OMEN_CLIENT_SECRET!,
158
+ * webhookSecret: process.env.OMEN_WEBHOOK_SECRET!,
159
+ * });
160
+ *
161
+ * // 1. start a grant on a fresh device
162
+ * const grant = await child.deviceStart({ deviceName: "Astrid's iPad" });
163
+ * // 2. she types her username → ask the family
164
+ * await child.notify({ device_code: grant.device_code, username: 'astrid' });
165
+ * // 3. poll until a grown-up approves (calm status union, never throws on pending)
166
+ * const result = await child.pollUntil(grant.device_code);
167
+ * if (result.status === 'approved') {
168
+ * const session = child.seal(result.tokens!); // store in your httpOnly session
169
+ * }
170
+ * ```
171
+ */
172
+ declare class ChildLogin {
173
+ private readonly clientId;
174
+ private readonly clientSecret?;
175
+ private readonly webhookSecret?;
176
+ readonly baseUrl: string;
177
+ constructor(options: ChildLoginOptions);
178
+ /** Start a child device-authorization grant. `scope:'child'` is always sent. */
179
+ deviceStart(options?: DeviceStartOptions): Promise<DeviceStartResult>;
180
+ /**
181
+ * Tell Omen which child is logging in. With `username` this is the cold-start
182
+ * path (no prior `login_hint`); without it, it re-pings the family for a known
183
+ * child. Always resolves `{ ok: true }` (enumeration-safe) on the cold path.
184
+ */
185
+ notify(args: {
186
+ device_code: string;
187
+ username?: string;
188
+ }): Promise<{
189
+ ok: boolean;
190
+ }>;
191
+ /**
192
+ * Poll the token endpoint once. Returns a calm status union — `pending`,
193
+ * `slow_down`, `denied`, `expired`, `error`, or `approved` (with tokens,
194
+ * rebind blob and one-time device_token). Never throws on a normal RFC 8628
195
+ * polling response; only network failures reject.
196
+ */
197
+ poll(deviceCode: string): Promise<PollResult>;
198
+ /**
199
+ * Poll until the grant reaches a terminal state, honouring `interval` and
200
+ * widening it by 5s on every `slow_down` (RFC 8628 §3.5). Prefer driving the
201
+ * flip from the `child.login.approved` webhook; use this as the fallback.
202
+ */
203
+ pollUntil(deviceCode: string, options?: PollUntilOptions): Promise<PollResult>;
204
+ /**
205
+ * Refresh a child access token. `scope:'child'` is preserved across rotation,
206
+ * and the old refresh token is single-use (Omen rotates it).
207
+ */
208
+ refresh(refreshToken: string): Promise<ChildTokens>;
209
+ /** Fetch the consent-gated identity bundle with the child's access token. */
210
+ childIdentity(accessToken: string): Promise<ChildIdentity>;
211
+ /** Normalise a token grant into a record for your httpOnly session store. */
212
+ seal(tokens: ChildTokens & {
213
+ device_token?: string;
214
+ }, opts?: {
215
+ now?: number;
216
+ deviceToken?: string | null;
217
+ }): SealedSession;
218
+ /** Whether a sealed session's access token needs a refresh before use. */
219
+ accessExpired(record: SealedSession, opts?: {
220
+ now?: number;
221
+ skewMs?: number;
222
+ }): boolean;
223
+ /** Verify a `child.login.approved` webhook signature (uses your `webhookSecret`). */
224
+ verifyWebhook(payload: string, signature: string, secret?: string | undefined): boolean;
225
+ /** Verify + parse a `child.login.approved` webhook. Returns null if the signature is invalid. */
226
+ parseApproval(payload: string, signature: string, secret?: string | undefined): ChildLoginApprovedPayload | null;
227
+ private post;
228
+ }
229
+
230
+ export { ASK_GROWN_UP_STATES, CHILD_LOGIN_STATES, type ChildIdentity, ChildLogin, type ChildLoginApprovedPayload, type ChildLoginContext, type ChildLoginDisplayGroup, type ChildLoginOptions, type ChildLoginSignal, type ChildLoginState, type ChildRebind, type ChildTokens, type DeviceStartOptions, type DeviceStartResult, ENTRY_STATES, type PollResult, type PollUntilOptions, type SealedSession, deriveInitialState, displayGroup, reduce };
@@ -0,0 +1,230 @@
1
+ type ChildPollStatus =
2
+ | 'approved'
3
+ | 'pending'
4
+ | 'slow_down'
5
+ | 'denied'
6
+ | 'expired'
7
+ | 'error';
8
+
9
+ interface ChildTokens {
10
+ access_token: string;
11
+ refresh_token?: string;
12
+ token_type: string;
13
+ expires_in?: number;
14
+ scope?: string;
15
+ }
16
+
17
+ interface ChildRebind {
18
+ child_id: string;
19
+ display_name: string;
20
+ avatar_url: string | null;
21
+ suggested_device_name: string;
22
+ }
23
+
24
+ interface PollResult {
25
+ status: ChildPollStatus;
26
+ tokens?: ChildTokens;
27
+ rebind?: ChildRebind | null;
28
+ deviceToken?: string | null;
29
+ error?: string;
30
+ }
31
+
32
+ interface SealedSession {
33
+ accessToken: string;
34
+ refreshToken: string | null;
35
+ scope: string;
36
+ accessExpiresAt: number;
37
+ deviceToken?: string;
38
+ }
39
+
40
+ interface ChildLoginApprovedPayload {
41
+ child_id: string;
42
+ device_id: string | null;
43
+ device_name: string | null;
44
+ approved_at: string;
45
+ }
46
+
47
+ type ChildLoginState =
48
+ | 'trusted'
49
+ | 'known'
50
+ | 'cold'
51
+ | 'pending'
52
+ | 'approved'
53
+ | 'denied'
54
+ | 'blocked'
55
+ | 'paused'
56
+ | 'expired'
57
+ | 'offline';
58
+
59
+ type ChildLoginSignal =
60
+ | 'ask'
61
+ | 'pending'
62
+ | 'slow_down'
63
+ | 'approved'
64
+ | 'denied'
65
+ | 'blocked'
66
+ | 'paused'
67
+ | 'expired'
68
+ | 'offline'
69
+ | 'online'
70
+ | 'reset';
71
+
72
+ type ChildLoginDisplayGroup =
73
+ | 'ready'
74
+ | 'waiting'
75
+ | 'approved'
76
+ | 'ask-grown-up'
77
+ | 'expired'
78
+ | 'offline';
79
+
80
+ interface ChildLoginContext {
81
+ online?: boolean;
82
+ trustedDevice?: boolean;
83
+ identity?: unknown | null;
84
+ }
85
+
86
+ declare const CHILD_LOGIN_STATES: ChildLoginState[];
87
+ declare const ASK_GROWN_UP_STATES: ChildLoginState[];
88
+ declare const ENTRY_STATES: ChildLoginState[];
89
+
90
+ declare function deriveInitialState(ctx?: ChildLoginContext): ChildLoginState;
91
+ declare function reduce(state: ChildLoginState, signal: ChildLoginSignal, ctx?: ChildLoginContext): ChildLoginState;
92
+ declare function displayGroup(state: ChildLoginState): ChildLoginDisplayGroup;
93
+
94
+ interface ChildLoginOptions {
95
+ /** Your OAuth client_id (from the Developer Portal). Required. */
96
+ clientId: string;
97
+ /** Your client_secret. Omit for public (PKCE) clients. */
98
+ clientSecret?: string;
99
+ /** Base URL for the Omen API. Defaults to `https://omen.dog`. */
100
+ baseUrl?: string;
101
+ /** The endpoint secret for your `child.login.approved` webhook (enables `verifyWebhook`). */
102
+ webhookSecret?: string;
103
+ }
104
+ interface DeviceStartOptions {
105
+ /** The child's Omen userId from a previous session. Omit for a cold start (parent picks). */
106
+ loginHint?: string;
107
+ /** Stable per-device id you generate and persist (enables trusted-device auto-approval). */
108
+ deviceId?: string;
109
+ /** Human-readable device name shown to the parent ("Astrid's iPad"). */
110
+ deviceName?: string;
111
+ /** A trusted-device token from a prior approval — auto-approves on first poll. */
112
+ deviceToken?: string;
113
+ }
114
+ interface DeviceStartResult {
115
+ device_code: string;
116
+ user_code: string;
117
+ verification_uri: string;
118
+ verification_uri_complete: string;
119
+ expires_in: number;
120
+ interval: number;
121
+ }
122
+ interface ChildIdentity {
123
+ child: {
124
+ display_name: string;
125
+ avatar_url: string | null;
126
+ };
127
+ guardians: Array<{
128
+ display_name: string;
129
+ avatar_url: string | null;
130
+ }>;
131
+ }
132
+ interface PollUntilOptions {
133
+ /** Abort the wait (e.g. the child navigated away, or your webhook already flipped the screen). */
134
+ signal?: AbortSignal;
135
+ /** Called after every non-terminal poll with the current status — useful for logging. */
136
+ onStatus?: (status: PollResult['status']) => void;
137
+ /** Override the starting interval (seconds). Defaults to the grant's `interval`, or 5. */
138
+ intervalSeconds?: number;
139
+ }
140
+
141
+ /**
142
+ * F263 Child Re-Login — the official server-side primitive for third-party apps
143
+ * that sign in child accounts.
144
+ *
145
+ * It is a thin, safe wrapper over the Phase-1 device-grant API with the right
146
+ * defaults made non-optional: `scope:'child'` is always sent, RFC 8628 errors
147
+ * are mapped to a calm status union instead of thrown, and `scope` is preserved
148
+ * across refresh. Token custody stays on YOUR server — nothing here ever returns
149
+ * a token to the browser.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * import { ChildLogin } from '@omen.dog/sdk';
154
+ *
155
+ * const child = new ChildLogin({
156
+ * clientId: process.env.OMEN_CLIENT_ID!,
157
+ * clientSecret: process.env.OMEN_CLIENT_SECRET!,
158
+ * webhookSecret: process.env.OMEN_WEBHOOK_SECRET!,
159
+ * });
160
+ *
161
+ * // 1. start a grant on a fresh device
162
+ * const grant = await child.deviceStart({ deviceName: "Astrid's iPad" });
163
+ * // 2. she types her username → ask the family
164
+ * await child.notify({ device_code: grant.device_code, username: 'astrid' });
165
+ * // 3. poll until a grown-up approves (calm status union, never throws on pending)
166
+ * const result = await child.pollUntil(grant.device_code);
167
+ * if (result.status === 'approved') {
168
+ * const session = child.seal(result.tokens!); // store in your httpOnly session
169
+ * }
170
+ * ```
171
+ */
172
+ declare class ChildLogin {
173
+ private readonly clientId;
174
+ private readonly clientSecret?;
175
+ private readonly webhookSecret?;
176
+ readonly baseUrl: string;
177
+ constructor(options: ChildLoginOptions);
178
+ /** Start a child device-authorization grant. `scope:'child'` is always sent. */
179
+ deviceStart(options?: DeviceStartOptions): Promise<DeviceStartResult>;
180
+ /**
181
+ * Tell Omen which child is logging in. With `username` this is the cold-start
182
+ * path (no prior `login_hint`); without it, it re-pings the family for a known
183
+ * child. Always resolves `{ ok: true }` (enumeration-safe) on the cold path.
184
+ */
185
+ notify(args: {
186
+ device_code: string;
187
+ username?: string;
188
+ }): Promise<{
189
+ ok: boolean;
190
+ }>;
191
+ /**
192
+ * Poll the token endpoint once. Returns a calm status union — `pending`,
193
+ * `slow_down`, `denied`, `expired`, `error`, or `approved` (with tokens,
194
+ * rebind blob and one-time device_token). Never throws on a normal RFC 8628
195
+ * polling response; only network failures reject.
196
+ */
197
+ poll(deviceCode: string): Promise<PollResult>;
198
+ /**
199
+ * Poll until the grant reaches a terminal state, honouring `interval` and
200
+ * widening it by 5s on every `slow_down` (RFC 8628 §3.5). Prefer driving the
201
+ * flip from the `child.login.approved` webhook; use this as the fallback.
202
+ */
203
+ pollUntil(deviceCode: string, options?: PollUntilOptions): Promise<PollResult>;
204
+ /**
205
+ * Refresh a child access token. `scope:'child'` is preserved across rotation,
206
+ * and the old refresh token is single-use (Omen rotates it).
207
+ */
208
+ refresh(refreshToken: string): Promise<ChildTokens>;
209
+ /** Fetch the consent-gated identity bundle with the child's access token. */
210
+ childIdentity(accessToken: string): Promise<ChildIdentity>;
211
+ /** Normalise a token grant into a record for your httpOnly session store. */
212
+ seal(tokens: ChildTokens & {
213
+ device_token?: string;
214
+ }, opts?: {
215
+ now?: number;
216
+ deviceToken?: string | null;
217
+ }): SealedSession;
218
+ /** Whether a sealed session's access token needs a refresh before use. */
219
+ accessExpired(record: SealedSession, opts?: {
220
+ now?: number;
221
+ skewMs?: number;
222
+ }): boolean;
223
+ /** Verify a `child.login.approved` webhook signature (uses your `webhookSecret`). */
224
+ verifyWebhook(payload: string, signature: string, secret?: string | undefined): boolean;
225
+ /** Verify + parse a `child.login.approved` webhook. Returns null if the signature is invalid. */
226
+ parseApproval(payload: string, signature: string, secret?: string | undefined): ChildLoginApprovedPayload | null;
227
+ private post;
228
+ }
229
+
230
+ export { ASK_GROWN_UP_STATES, CHILD_LOGIN_STATES, type ChildIdentity, ChildLogin, type ChildLoginApprovedPayload, type ChildLoginContext, type ChildLoginDisplayGroup, type ChildLoginOptions, type ChildLoginSignal, type ChildLoginState, type ChildRebind, type ChildTokens, type DeviceStartOptions, type DeviceStartResult, ENTRY_STATES, type PollResult, type PollUntilOptions, type SealedSession, deriveInitialState, displayGroup, reduce };