@civitai/sdk 0.10.2 → 0.11.1
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/BREAKING.md +17 -8
- package/README.md +55 -0
- package/dist/core/transports/iframe-transport.js +4 -1
- package/dist/host/index.d.ts +14 -0
- package/dist/host/index.js +4 -0
- package/dist/host/protocol.d.ts +4 -0
- package/dist/version.generated.d.ts +2 -0
- package/dist/version.generated.js +3 -0
- package/package.json +3 -2
package/BREAKING.md
CHANGED
|
@@ -25,13 +25,22 @@ messages have no destination yet.
|
|
|
25
25
|
| `CREATE_POST_FROM_APP` | — | No v1 route, **and deliberately staying on the bridge** — see below |
|
|
26
26
|
| `PUBLISH_GENERATION_OUTPUTS` | `app.host.publishGenerationOutputs` | Same reasoning: it raises host UI, so it stays on the bridge — **carried** rather than replaced |
|
|
27
27
|
| `SET_COLLECTION_FOLLOW` | `POST /api/v1/blocks/collections/{id}/follow` | **Route exists** (scope `collections:write:self`). This row previously said "No v1 route"; that was wrong |
|
|
28
|
-
| `GET_DAILY_COMPENSATION`, `GET_WILDCARD_PACK
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`
|
|
32
|
-
no
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
| `GET_DAILY_COMPENSATION`, `GET_WILDCARD_PACK` | — | Not carried |
|
|
29
|
+
| `TRACK_EVENT` | `app.host.track(eventName, properties)` | **Carried**, same wire as `useBlockAnalytics().track`. 🔴 Only events and properties DECLARED under the manifest's `analytics.events` are recorded — see below |
|
|
30
|
+
|
|
31
|
+
⚠ **`TRACK_EVENT` used to be listed here as *not carried*, because no host handled it — every
|
|
32
|
+
`useBlockAnalytics` call was a no-op.** Both hosts now forward it, so the calls an app already makes start
|
|
33
|
+
counting — but only against the declaration. What is recorded:
|
|
34
|
+
|
|
35
|
+
- **Declared events only.** An event name the approved manifest does not declare under `analytics.events` is
|
|
36
|
+
counted as a drop; its name and properties are not stored. An app that never declared its events records
|
|
37
|
+
nothing but that count.
|
|
38
|
+
- **Declared properties of the declared type only.** Types are `enum` (one of the declared strings), `number`
|
|
39
|
+
and `boolean`. There is no free-text string, so an id, a prompt or a username sent as a string is dropped.
|
|
40
|
+
An undeclared property, or a value of the wrong type, is dropped and the rest of the event kept.
|
|
41
|
+
- **Budgets.** Each mount may send 10 events a second and 500 in all, in bodies of at most 32 KiB; an event
|
|
42
|
+
over a limit is dropped in the browser and counted nowhere. Signed-out viewers are counted, never identified.
|
|
43
|
+
- **Fire-and-forget.** No reply, no acknowledgement. Owners see aggregates in the app's analytics panel.
|
|
35
44
|
|
|
36
45
|
## Scope binding is per-route
|
|
37
46
|
|
|
@@ -299,7 +308,7 @@ iframe, and nothing about the call site looks different.
|
|
|
299
308
|
On `app.host`: `requestSignIn`, `download` (was `SAVE_IMAGE`),
|
|
300
309
|
`openResourcePicker`, `openBuzzPurchase`, `openImageUpload`,
|
|
301
310
|
`publishGenerationOutputs`, `resize`, **`autoResize`**, `reportError`, `navigate`
|
|
302
|
-
and `onVisibilityChange
|
|
311
|
+
and `onVisibilityChange` — plus `track`, which raises no UI (see `TRACK_EVENT` above). Reach for `autoResize(element)` over bare `resize` —
|
|
303
312
|
it wires the `ResizeObserver` for you and returns a stop function. Consent is `app.requestGrants` (was `requestConsent`).
|
|
304
313
|
|
|
305
314
|
`publishGenerationOutputs` keeps `usePublishGenerationOutputs`'s wire exactly —
|
package/README.md
CHANGED
|
@@ -294,6 +294,28 @@ The `400` body is `{ error: 'Invalid query', details }` and the SDK reads `error
|
|
|
294
294
|
first, so `ApiError.message` says only "Invalid query" — the field-level reason is
|
|
295
295
|
in `ApiError.body.details`.
|
|
296
296
|
|
|
297
|
+
**Writes are rate-limited per (viewer, app)**; the limits are listed once, in
|
|
298
|
+
[the `@civitai/blocks-react` README](https://github.com/civitai/civitai-app-starters/blob/main/packages/civitai-blocks-react/README.md#shared-storage-write-limits). A write refused for a limit is an `ApiError` with
|
|
299
|
+
`status === 429`, a `Retry-After` header the client does not expose, and a body
|
|
300
|
+
of `{ code: 'TOO_MANY_REQUESTS', message, error, rateLimit: { retryAfterSeconds,
|
|
301
|
+
retryAt } }`. This client does not validate `body.rateLimit`, so check it before
|
|
302
|
+
you render it:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
import { ApiError } from '@civitai/sdk';
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
await app.sharedStorage.append({ title: 'My deck' });
|
|
309
|
+
} catch (error) {
|
|
310
|
+
const rateLimit =
|
|
311
|
+
error instanceof ApiError && error.status === 429
|
|
312
|
+
? (error.body as { rateLimit?: { retryAt?: unknown } } | null)?.rateLimit
|
|
313
|
+
: undefined;
|
|
314
|
+
if (typeof rateLimit?.retryAt === 'string') showRetryAt(new Date(rateLimit.retryAt));
|
|
315
|
+
else throw error;
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
297
319
|
If you are porting from `@civitai/blocks-react`'s `useSharedStorage`, the bridge
|
|
298
320
|
*host* normalised all of this for you and nothing normalises it here;
|
|
299
321
|
[`BREAKING.md`](./BREAKING.md) covers what the port does and does not cost.
|
|
@@ -353,6 +375,7 @@ wider scope — and refuses without one.
|
|
|
353
375
|
| `navigate(path, { scope, target })` | Deep-links within this app's own sub-paths; `scope: 'site'` asks to leave the app for a civitai.com page instead. `scope` defaults to `'app'` |
|
|
354
376
|
| `onVisibilityChange(handler)` | Reports the page hiding and returning |
|
|
355
377
|
| `requestSignIn({ returnUrl })` | Starts sign-in; the block re-initialises signed in |
|
|
378
|
+
| `track(eventName, properties)` | Sends a custom event to your app's analytics, fire-and-forget. Only events and properties declared under `analytics.events` in the manifest are recorded — see *Custom events* below |
|
|
356
379
|
| `download({ url, filename })` | Saves to the viewer's device — a sandboxed frame cannot |
|
|
357
380
|
| `openResourcePicker({ resourceType })` | civitai's own picker; resolves the one pick, or `null` |
|
|
358
381
|
| `openBuzzPurchase({ suggestedAmount })` | The purchase flow; resolves `{ purchased }` |
|
|
@@ -456,6 +479,38 @@ than failing the call, so `imageIds` can be **shorter** than what you selected
|
|
|
456
479
|
and nothing says which index dropped. Compare lengths; do not pair ids to
|
|
457
480
|
indexes.
|
|
458
481
|
|
|
482
|
+
### Custom events
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
app.host.track('generate_clicked', { mode: 'txt2img', batch: 4 });
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
🔴 **Only what the manifest declares is recorded.** Declare each event, and each
|
|
489
|
+
property it carries, under `analytics.events` in `block.manifest.json`; events
|
|
490
|
+
are accepted against the approved manifest:
|
|
491
|
+
|
|
492
|
+
```json
|
|
493
|
+
"analytics": {
|
|
494
|
+
"events": {
|
|
495
|
+
"generate_clicked": {
|
|
496
|
+
"properties": {
|
|
497
|
+
"mode": { "type": "enum", "values": ["txt2img", "img2img"] },
|
|
498
|
+
"batch": { "type": "number" }
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
- An undeclared event is counted as dropped; its name and properties are not stored.
|
|
506
|
+
- A declared event keeps only its declared properties, each only when the value
|
|
507
|
+
has the declared type. Types are `enum`, `number` and `boolean`; there is no
|
|
508
|
+
free-text string, so a string is kept only when it is one of the enum's values.
|
|
509
|
+
- Each mount may send 10 events a second and 500 in all, in bodies of at most
|
|
510
|
+
32 KiB. An event over a limit is dropped in the browser and counted nowhere.
|
|
511
|
+
- Fire-and-forget: there is no reply. Owners see aggregates in the app's
|
|
512
|
+
analytics panel, never individual events.
|
|
513
|
+
|
|
459
514
|
## Parent origins
|
|
460
515
|
|
|
461
516
|
Messages are accepted only from an allowlisted parent origin. The allowlist is
|
|
@@ -2,6 +2,7 @@ import { isHostMessage, parseBlockInitFragment, stripBlockInitFragment, } from '
|
|
|
2
2
|
import { EMPTY_SNAPSHOT, snapshotFromInit, tokenFromWrapped, } from '../transport.js';
|
|
3
3
|
import { BridgeError, classifyHostError } from '../errors.js';
|
|
4
4
|
import { OriginMatcher } from '../origin-matcher.js';
|
|
5
|
+
import { VERSION } from '../../version.generated.js';
|
|
5
6
|
/**
|
|
6
7
|
* The framing the host had before this package owned the protocol: fields
|
|
7
8
|
* straight in the payload, and failure as the server's own sentence. Converting
|
|
@@ -271,7 +272,9 @@ export class IframeTransport {
|
|
|
271
272
|
this.#emit();
|
|
272
273
|
this.#flushOutbound();
|
|
273
274
|
// height 0 is a placeholder; the real measurement follows once the app mounts.
|
|
274
|
-
|
|
275
|
+
// `sdkName`/`sdkVersion` are informational: the host counts which SDK
|
|
276
|
+
// versions are in the wild. Nothing is negotiated on them.
|
|
277
|
+
this.#dispatch('BLOCK_READY', { height: 0, sdkName: 'sdk', sdkVersion: VERSION });
|
|
275
278
|
}
|
|
276
279
|
return;
|
|
277
280
|
}
|
package/dist/host/index.d.ts
CHANGED
|
@@ -50,6 +50,20 @@ export interface Host {
|
|
|
50
50
|
requestSignIn(args?: {
|
|
51
51
|
returnUrl?: string;
|
|
52
52
|
}): void;
|
|
53
|
+
/**
|
|
54
|
+
* Sends one custom event to the app's analytics. Fire-and-forget: no reply
|
|
55
|
+
* and no acknowledgement. The wire is `useBlockAnalytics().track`'s.
|
|
56
|
+
*
|
|
57
|
+
* 🔴 Only what the manifest DECLARES under `analytics.events` is recorded,
|
|
58
|
+
* against the approved manifest. An undeclared event is counted as dropped and
|
|
59
|
+
* its name is not stored. A declared event keeps only its declared
|
|
60
|
+
* properties, each only when the value has the declared type — `enum` (one of
|
|
61
|
+
* the declared strings), `number` or `boolean`; there is no free-text string.
|
|
62
|
+
* Each mount may send 10 events a second and 500 in all; an event over a
|
|
63
|
+
* limit is dropped in the browser and counted nowhere. Owners see aggregates
|
|
64
|
+
* in the app's analytics panel, never individual events.
|
|
65
|
+
*/
|
|
66
|
+
track(eventName: string, properties?: Record<string, unknown>): void;
|
|
53
67
|
/**
|
|
54
68
|
* Saves to the viewer's device through the host, because a sandboxed frame
|
|
55
69
|
* cannot. The host fetches only from origins it allowlists.
|
package/dist/host/index.js
CHANGED
|
@@ -40,6 +40,10 @@ export function createHost(transport) {
|
|
|
40
40
|
};
|
|
41
41
|
},
|
|
42
42
|
requestSignIn: (args = {}) => notify('REQUEST_SIGN_IN', args, { transport }),
|
|
43
|
+
// `properties` is OMITTED when not given, so the payload is byte-identical
|
|
44
|
+
// to what `useBlockAnalytics().track(name)` sends after JSON drops the
|
|
45
|
+
// `undefined`.
|
|
46
|
+
track: (eventName, properties) => notify('TRACK_EVENT', { eventName, ...(properties !== undefined ? { properties } : {}) }, { transport }),
|
|
43
47
|
async download(request, opts = {}) {
|
|
44
48
|
await call('SAVE_IMAGE', request, { ...opts, transport });
|
|
45
49
|
},
|
package/dist/host/protocol.d.ts
CHANGED
|
@@ -175,6 +175,10 @@ export type HostNotifications = {
|
|
|
175
175
|
REQUEST_CONSENT: {
|
|
176
176
|
scopes?: string[];
|
|
177
177
|
};
|
|
178
|
+
TRACK_EVENT: {
|
|
179
|
+
eventName: string;
|
|
180
|
+
properties?: Record<string, unknown>;
|
|
181
|
+
};
|
|
178
182
|
};
|
|
179
183
|
export type HostPushes = {
|
|
180
184
|
/** The page is hidden; stop timers and polling until `RESUME`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civitai/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "The Civitai SDK: an app holds a token and calls the Civitai API and the orchestrator; a block asks its host only to show host UI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"@civitai/orchestration-client": "0.2.0-beta.101"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
|
-
"
|
|
63
|
+
"generate": "node ../../scripts/gen-version.mjs",
|
|
64
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && node ../../scripts/gen-version.mjs && tsc -p tsconfig.json",
|
|
64
65
|
"api": "node scripts/api-report.mjs",
|
|
65
66
|
"api:check": "node scripts/api-report.mjs --check",
|
|
66
67
|
"check:layering": "node scripts/check-layering.mjs",
|