@civitai/blocks-react 0.53.1 → 0.55.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 +326 -6
- package/dist/hooks/useAppStorage.d.ts +39 -8
- package/dist/hooks/useAppStorage.d.ts.map +1 -1
- package/dist/hooks/useAppStorage.js +14 -1
- package/dist/hooks/useAppStorage.js.map +1 -1
- package/dist/hooks/useBlockContext.d.ts +5 -1
- package/dist/hooks/useBlockContext.d.ts.map +1 -1
- package/dist/hooks/useBlockContext.js +5 -1
- package/dist/hooks/useBlockContext.js.map +1 -1
- package/dist/internal/liveHost.d.ts +3 -1
- package/dist/internal/liveHost.d.ts.map +1 -1
- package/dist/internal/liveHost.js +22 -21
- package/dist/internal/liveHost.js.map +1 -1
- package/dist/internal/mockHost.d.ts +37 -5
- package/dist/internal/mockHost.d.ts.map +1 -1
- package/dist/internal/mockHost.js +80 -34
- package/dist/internal/mockHost.js.map +1 -1
- package/dist/internal/transport.d.ts +4 -3
- package/dist/internal/transport.d.ts.map +1 -1
- package/dist/internal/transport.js.map +1 -1
- package/dist/internal/validate.d.ts.map +1 -1
- package/dist/internal/validate.js +22 -17
- package/dist/internal/validate.js.map +1 -1
- package/dist/live.d.ts +24 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +24 -0
- package/dist/live.js.map +1 -0
- package/dist/testing.d.ts +16 -22
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +15 -23
- package/dist/testing.js.map +1 -1
- package/package.json +75 -40
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ your block app and the SDK share a single React tree.
|
|
|
27
27
|
import { useRef } from 'react';
|
|
28
28
|
import { useBlockContext, useBlockResize, useBuzzWorkflow } from '@civitai/blocks-react';
|
|
29
29
|
import { Button } from '@civitai/blocks-react/ui';
|
|
30
|
-
import { isModelSlotContext } from '@civitai/app-sdk/blocks';
|
|
30
|
+
import { isModelSlotContext, isSignedIn } from '@civitai/app-sdk/blocks';
|
|
31
31
|
|
|
32
32
|
export function App() {
|
|
33
33
|
const { ready, context, viewer, theme } = useBlockContext();
|
|
@@ -45,7 +45,8 @@ export function App() {
|
|
|
45
45
|
// GOTCHA #60: set data-theme on YOUR OWN root — the host can't reach into
|
|
46
46
|
// the iframe to set it. Without this any [data-theme="dark"] CSS is dormant.
|
|
47
47
|
<div ref={rootRef} data-theme={theme}>
|
|
48
|
-
|
|
48
|
+
{/* Sign-in gate: call `isSignedIn`, never an identity read. */}
|
|
49
|
+
<p>Block for model {context.modelName} ({isSignedIn(viewer) ? 'signed in' : 'anon'})</p>
|
|
49
50
|
{/* `/ui` Button — themed by the data-theme above; `loading` disables + shows a spinner */}
|
|
50
51
|
<Button
|
|
51
52
|
loading={status === 'submitting' || status === 'polling'}
|
|
@@ -113,7 +114,14 @@ const { ready, context, viewer, theme, settings, blockId, blockInstanceId, appId
|
|
|
113
114
|
|
|
114
115
|
- `context` — `BlockContext` (`{ slotId, … }`); narrow to `ModelSlotContext` for
|
|
115
116
|
model-page slots.
|
|
116
|
-
- `viewer` — `ViewerInfo | null` (`null` = anonymous).
|
|
117
|
+
- `viewer` — `ViewerInfo | null` (`null` = anonymous). **Gate sign-in with
|
|
118
|
+
`isSignedIn(viewer)`** (from `@civitai/app-sdk/blocks`), never on
|
|
119
|
+
`viewer.id`/`viewer.username` (both `@deprecated`). Don't open-code the gate:
|
|
120
|
+
the SDK owns which spelling is correct — `signedIn` is optional on the wire
|
|
121
|
+
and is the one viewer field the init validator deliberately does not reject
|
|
122
|
+
when malformed, so `isSignedIn` answers from presence instead. Hover it for
|
|
123
|
+
the full reasoning. Need the identity itself? Use
|
|
124
|
+
[`useViewer()`](#useviewer) — scope-gated and audited per call.
|
|
117
125
|
- `theme` — `'light' | 'dark'`. **Set `data-theme={theme}` on your root** (gotcha #60).
|
|
118
126
|
LIVE: it starts at the `BLOCK_INIT` value and then tracks the host's
|
|
119
127
|
`THEME_CHANGE` push when the viewer toggles dark mode mid-session — see
|
|
@@ -684,18 +692,72 @@ async function onCancel(id: string) {
|
|
|
684
692
|
|
|
685
693
|
### `useAppStorage()`
|
|
686
694
|
|
|
687
|
-
|
|
688
|
-
|
|
695
|
+
KV datastore, host-mediated. Keys are **namespaced** per (block instance,
|
|
696
|
+
viewer); the byte and row **budgets** are enforced per (**app**, viewer), so
|
|
697
|
+
every instance of one app shares one budget for that viewer.
|
|
689
698
|
|
|
690
699
|
```tsx
|
|
700
|
+
import {
|
|
701
|
+
APP_STORAGE_MAX_VALUE_BYTES, // largest single value, in wire bytes
|
|
702
|
+
APP_STORAGE_MAX_BYTES, // total stored bytes per (app, viewer)
|
|
703
|
+
APP_STORAGE_MAX_ROWS, // total rows per (app, viewer)
|
|
704
|
+
} from '@civitai/app-sdk/blocks';
|
|
705
|
+
|
|
691
706
|
const storage = useAppStorage();
|
|
692
|
-
await storage.set('key', { any: 'json' }); //
|
|
707
|
+
await storage.set('key', { any: 'json' }); // rejects over ANY of the three ceilings
|
|
693
708
|
const v = await storage.get<MyShape>('key'); // null if unset / anon
|
|
694
709
|
await storage.delete('key'); // idempotent
|
|
695
710
|
const { keys } = await storage.list({ prefix: 'note-' });
|
|
696
711
|
const quota = await storage.getQuota(); // { usedBytes, rowCount, limitBytes, limitRows }
|
|
697
712
|
```
|
|
698
713
|
|
|
714
|
+
🔴 **`getQuota()` is the authority; the constants are a snapshot.** These three
|
|
715
|
+
are the ceilings **as of the version of `@civitai/app-sdk` you installed** —
|
|
716
|
+
compiled-in figures, which is the same frozen-number failure mode this page
|
|
717
|
+
used to demonstrate, just with one copy instead of nine. The host can move a
|
|
718
|
+
ceiling without your lockfile changing. So:
|
|
719
|
+
|
|
720
|
+
- **Render `getQuota()`'s reply**, never a constant, anywhere a viewer sees a
|
|
721
|
+
number or a code path decides whether a write will fit.
|
|
722
|
+
- **Reach for the constants only where no quota reply is available** — a
|
|
723
|
+
build-time sanity check, a test fixture, a rough design-time estimate — and
|
|
724
|
+
treat the answer as "roughly, at install time".
|
|
725
|
+
- **Re-check after any SDK bump**, and expect movement: the per-viewer clamp
|
|
726
|
+
was sized against a measured distribution and the host says to expect a
|
|
727
|
+
re-measure. `appStorageLimits.ts` in `@civitai/app-sdk` carries the
|
|
728
|
+
provenance and a one-liner that re-derives the current values from the host.
|
|
729
|
+
|
|
730
|
+
Never hard-code a figure of your own: the docs here used to quote the app-wide
|
|
731
|
+
umbrella instead of the per-viewer clamp and were **25x** out on bytes and
|
|
732
|
+
**1000x** out on rows.
|
|
733
|
+
|
|
734
|
+
🔴 **The ROW ceiling is usually the binding one, and a byte-based "x of y used"
|
|
735
|
+
readout will not see it coming.** A block caching one modest record per item a
|
|
736
|
+
viewer touches exhausts `limitRows` while still holding a small fraction of
|
|
737
|
+
`limitBytes`. Show rows too.
|
|
738
|
+
|
|
739
|
+
`createMockHost()` defaults to these same ceilings and enforces the per-value
|
|
740
|
+
cap, the byte budget and — since it was added — the **row** budget on write, so
|
|
741
|
+
a row-limit overrun now fails under `dev:mock` where it previously passed and
|
|
742
|
+
failed only in production. Pass `storage: { quotaBytes, limitRows }` to
|
|
743
|
+
simulate something smaller.
|
|
744
|
+
|
|
745
|
+
⚠️ The mock is **not** gate-for-gate identical to the host. Three known
|
|
746
|
+
divergences:
|
|
747
|
+
|
|
748
|
+
- the error string a rejection carries
|
|
749
|
+
([#343](https://github.com/civitai/civitai-app-starters/issues/343));
|
|
750
|
+
- the byte gate refusing a shrinking overwrite that the host admits
|
|
751
|
+
([#345](https://github.com/civitai/civitai-app-starters/issues/345));
|
|
752
|
+
- 🔴 the byte gate counting **wire** bytes where the host counts **stored**
|
|
753
|
+
bytes — `octet_length(value::jsonb::text)`, larger for every container, up to
|
|
754
|
+
~1.5x ([#347](https://github.com/civitai/civitai-app-starters/issues/347)).
|
|
755
|
+
|
|
756
|
+
Passing under `dev:mock` is evidence, not proof — and note the third one is
|
|
757
|
+
**permissive**: unlike the other two, it lets a write pass locally that
|
|
758
|
+
production will reject. Size your fixtures against `getQuota()`, not against
|
|
759
|
+
what the mock accepted.
|
|
760
|
+
|
|
699
761
|
### `useSharedStorage()`
|
|
700
762
|
|
|
701
763
|
App-scoped, append-only, community-votable SHARED datastore (every viewer sees
|
|
@@ -1134,6 +1196,264 @@ For non-React or advanced use, the transport primitives are exported too:
|
|
|
1134
1196
|
`readAllowedOriginsFromEnv`, `getTransport`, and `sendTypedRequest`. Hooks are the
|
|
1135
1197
|
recommended surface; reach for these only when a hook doesn't fit.
|
|
1136
1198
|
|
|
1199
|
+
## The `/testing` subexport
|
|
1200
|
+
|
|
1201
|
+
`@civitai/blocks-react/testing` is the **host-simulation** entry point: it stands
|
|
1202
|
+
in for civitai.com so your block can run in `vitest`/`happy-dom` and in a local
|
|
1203
|
+
dev harness. It is a normal, published subpath of a `0.x` package — see
|
|
1204
|
+
[Stability](#stability-of-testing) below for exactly what that does and does not
|
|
1205
|
+
promise.
|
|
1206
|
+
|
|
1207
|
+
**Everything on this subpath is a mock.** No network, no Buzz, no real backend.
|
|
1208
|
+
Until `0.55.0` that was not true: `createLiveHost`, which talks to the real
|
|
1209
|
+
Civitai backend and spends the token holder's own Buzz, was exported from here
|
|
1210
|
+
too, one autocomplete entry from `createMockHost`. It now lives on its own
|
|
1211
|
+
subpath — see [The `/live` subexport](#the-live-subexport) below.
|
|
1212
|
+
|
|
1213
|
+
### The whole surface
|
|
1214
|
+
|
|
1215
|
+
This section is **the one place the surface is written down**, and it is not
|
|
1216
|
+
prose: `test/subpathSurfaces.test.ts` parses the two marked regions below and
|
|
1217
|
+
fails if they disagree with what `src/testing.tsx` actually exports — in either
|
|
1218
|
+
direction. Every other mention of this subpath (module docblock, `AGENTS.md`)
|
|
1219
|
+
points here rather than repeating the list, because a second copy is exactly
|
|
1220
|
+
what went stale in [#334](https://github.com/civitai/civitai-app-starters/issues/334).
|
|
1221
|
+
|
|
1222
|
+
**Values**
|
|
1223
|
+
|
|
1224
|
+
<!-- TESTING-SURFACE:VALUES:BEGIN -->
|
|
1225
|
+
|
|
1226
|
+
| Export | What it is |
|
|
1227
|
+
|---|---|
|
|
1228
|
+
| `resetTransport` | Drops the cached singleton transport. Call it in `beforeEach` so each test starts clean. |
|
|
1229
|
+
| `createMockHost` | A framework-agnostic fake of the embedding host — answers every `*_RESULT` message, with knobs for generation cost/latency/failure, Buzz balance, app + shared storage, consent, maturity. Returns a `MockHost`; call `.install()` and keep the returned teardown. **No network, no Buzz.** |
|
|
1230
|
+
| `readMockHostUrlOptions` | Reads the harness URL toggles (`?viewer` `?consent` `?fail` `?theme` `?pick` `?balance` `?latency` `?seed` …) into a `Partial<MockHostOptions>`. `Harness` applies it for you; call it directly only in a hand-rolled harness. |
|
|
1231
|
+
| `Harness` | The React wrapper: installs a `createMockHost` on mount, tears it down on unmount, and renders an optional on-screen outbound-message log. Takes every `MockHostOptions` field plus `applyUrlToggles` and `showLog`. |
|
|
1232
|
+
|
|
1233
|
+
<!-- TESTING-SURFACE:VALUES:END -->
|
|
1234
|
+
|
|
1235
|
+
**Types** — the transitive closure that makes those values nameable: each is the
|
|
1236
|
+
declared type of an option, of a `MockHost` member, or of a property of one of
|
|
1237
|
+
those, so you can hoist a sub-object out of an options literal and give it a
|
|
1238
|
+
type.
|
|
1239
|
+
|
|
1240
|
+
<!-- TESTING-SURFACE:TYPES:BEGIN -->
|
|
1241
|
+
|
|
1242
|
+
```text
|
|
1243
|
+
CannedPick
|
|
1244
|
+
CostSpec
|
|
1245
|
+
HarnessProps
|
|
1246
|
+
ImageSpec
|
|
1247
|
+
MockBuzzBalance
|
|
1248
|
+
MockBuzzHandle
|
|
1249
|
+
MockBuzzScenario
|
|
1250
|
+
MockCannedImageScan
|
|
1251
|
+
MockGenerationScenario
|
|
1252
|
+
MockHost
|
|
1253
|
+
MockHostFailMode
|
|
1254
|
+
MockHostOptions
|
|
1255
|
+
MockHostScenarioPatch
|
|
1256
|
+
MockSharedScenario
|
|
1257
|
+
MockSharedSeed
|
|
1258
|
+
MockStorageScenario
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
<!-- TESTING-SURFACE:TYPES:END -->
|
|
1262
|
+
|
|
1263
|
+
That is the complete list. Nothing else is exported.
|
|
1264
|
+
|
|
1265
|
+
### In a test
|
|
1266
|
+
|
|
1267
|
+
```ts
|
|
1268
|
+
import {
|
|
1269
|
+
createMockHost,
|
|
1270
|
+
resetTransport,
|
|
1271
|
+
type MockHostOptions,
|
|
1272
|
+
type MockGenerationScenario,
|
|
1273
|
+
} from '@civitai/blocks-react/testing';
|
|
1274
|
+
|
|
1275
|
+
resetTransport();
|
|
1276
|
+
|
|
1277
|
+
// Hoisting a sub-object out of the options literal is why the scenario types
|
|
1278
|
+
// are exported.
|
|
1279
|
+
const generation: MockGenerationScenario = { costPerGen: 12, latencyMs: 0 };
|
|
1280
|
+
const options: MockHostOptions = { viewer: null, failMode: 'some', generation };
|
|
1281
|
+
|
|
1282
|
+
const host = createMockHost(options);
|
|
1283
|
+
const uninstall = host.install();
|
|
1284
|
+
host.setScenario({ failMode: 'none' }); // live-tune mid-test
|
|
1285
|
+
uninstall();
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
### In a dev harness
|
|
1289
|
+
|
|
1290
|
+
```tsx
|
|
1291
|
+
import { Harness } from '@civitai/blocks-react/testing';
|
|
1292
|
+
|
|
1293
|
+
export function DevRoot() {
|
|
1294
|
+
return (
|
|
1295
|
+
<Harness failMode="some" showLog>
|
|
1296
|
+
<App />
|
|
1297
|
+
</Harness>
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
`<Harness>` fires host messages from `window.location.origin`, and the transport
|
|
1303
|
+
drops inbound messages from origins outside its allowlist — so a dev harness
|
|
1304
|
+
must include its own origin, e.g. `VITE_BLOCK_ALLOWED_PARENT_ORIGINS=http://localhost:5173`.
|
|
1305
|
+
Otherwise `BLOCK_INIT` never lands.
|
|
1306
|
+
|
|
1307
|
+
### Stability of `/testing`
|
|
1308
|
+
|
|
1309
|
+
[#334](https://github.com/civitai/civitai-app-starters/issues/334) offered a
|
|
1310
|
+
fork: *document the undocumented surface*, **or** *mark the subpath explicitly
|
|
1311
|
+
unstable*. This package took the **first** branch, and only the first. The
|
|
1312
|
+
section above is that documentation.
|
|
1313
|
+
|
|
1314
|
+
Concretely, and with no guarantee beyond what is actually enforced:
|
|
1315
|
+
|
|
1316
|
+
- **It is a normal subpath of a `0.x` package**, on the same footing as `.`,
|
|
1317
|
+
`./ui` and `./live` — no stronger, no weaker. Under semver `0.x`, a **minor
|
|
1318
|
+
may break it**. It is not `@internal`, and it is not "unsupported": fleet
|
|
1319
|
+
blocks import it from their dev harnesses and from their test suites.
|
|
1320
|
+
- **What is enforced** is that a change to the exported *symbol set* cannot ship
|
|
1321
|
+
silently. `test/subpathSurfaces.test.ts` fails on growth and on shrinkage, and
|
|
1322
|
+
it fails again unless the README section above is updated to match — so any
|
|
1323
|
+
such change is a deliberate edit that a reviewer sees and a changeset names.
|
|
1324
|
+
- **What is *not* promised** is the *shape* of the mock-host option and result
|
|
1325
|
+
types. They describe a fake host whose fidelity tracks the real one; a
|
|
1326
|
+
property may be added, tightened or renamed in a minor. The ledger asserts
|
|
1327
|
+
names, not shapes, and deliberately so.
|
|
1328
|
+
|
|
1329
|
+
What is *not* listed above is genuinely internal and carries no guarantee. Until
|
|
1330
|
+
`0.55.0` this subpath also re-exported 25 symbols with no documentation — the
|
|
1331
|
+
catalog client (`fetchCatalog`, `buildCatalogUrl`, `edgeThumb`, `modelToCard`,
|
|
1332
|
+
`DEFAULT_LIMIT`, …), the in-harness picker overlay (`openPickerOverlay`),
|
|
1333
|
+
`decodeBlockTokenPayload`, `disallowedAccountError`, `mockParentMessage`, and
|
|
1334
|
+
the `MockHostProvider` alias — plus `createLiveHost` / `LiveHostOptions`, which
|
|
1335
|
+
moved to `./live` rather than disappearing. See the `0.55.0` changelog entry for
|
|
1336
|
+
the full list and for the three removals that had a measured fleet consumer. If
|
|
1337
|
+
you were importing one of the internal ones, it lives at a path this package
|
|
1338
|
+
does not publish — open an issue rather than reaching into `dist/internal/`.
|
|
1339
|
+
|
|
1340
|
+
## The `/live` subexport
|
|
1341
|
+
|
|
1342
|
+
> ### 🔴 `@civitai/blocks-react/live` spends real Buzz
|
|
1343
|
+
>
|
|
1344
|
+
> `createLiveHost` forwards the App-Block postMessage protocol to the **real
|
|
1345
|
+
> Civitai backend** over a pasted short-lived dev block token —
|
|
1346
|
+
> `blocks.submitWorkflow` included — and a successful generation **debits the
|
|
1347
|
+
> token holder's own Buzz**. There is no dry-run mode and no confirmation. It
|
|
1348
|
+
> exists for one caller: a `pnpm dev:live` harness. **It must never appear in a
|
|
1349
|
+
> test suite.** The free one is `createMockHost`, on `./testing`.
|
|
1350
|
+
|
|
1351
|
+
### Why it has its own subpath
|
|
1352
|
+
|
|
1353
|
+
Until `0.55.0` this code was exported from `./testing`. The argument for moving
|
|
1354
|
+
it, in full, is that **a client which spends the caller's money should not be
|
|
1355
|
+
reachable through an import path named `testing`** — the import line is the one
|
|
1356
|
+
piece of context that travels with every call site, and `…/testing` actively
|
|
1357
|
+
asserts the opposite of what this module does. That is
|
|
1358
|
+
[#334](https://github.com/civitai/civitai-app-starters/issues/334)'s literal
|
|
1359
|
+
closing condition.
|
|
1360
|
+
|
|
1361
|
+
Two arguments that were made for this change and **do not hold** — recorded so
|
|
1362
|
+
they are not made again:
|
|
1363
|
+
|
|
1364
|
+
- **It does not shrink the install.** Measured: **+4,447 B**. See
|
|
1365
|
+
[What the host-simulation subpaths cost you](#what-the-host-simulation-subpaths-cost-you).
|
|
1366
|
+
- **It does not close a wrong-autocomplete hazard**, because there was none to
|
|
1367
|
+
close. `createMockHost(options: MockHostOptions = {})` is callable bare;
|
|
1368
|
+
`createLiveHost(options: LiveHostOptions)` takes a **required** argument whose
|
|
1369
|
+
`blockToken` is a **required** short-lived RS256 JWT that a human mints and
|
|
1370
|
+
pastes by hand. `createLiveHost()` and `createLiveHost({})` do not compile, so
|
|
1371
|
+
nobody reaches this module by picking the wrong completion. Earlier revisions
|
|
1372
|
+
of this file, of the changeset, and of #334 called the two signatures
|
|
1373
|
+
"near-identical"; none of them had read the signatures.
|
|
1374
|
+
|
|
1375
|
+
### The whole surface
|
|
1376
|
+
|
|
1377
|
+
One value and one type. `test/subpathSurfaces.test.ts` pins the runtime export
|
|
1378
|
+
set, failing on growth and on shrinkage.
|
|
1379
|
+
|
|
1380
|
+
| Export | What it is |
|
|
1381
|
+
|---|---|
|
|
1382
|
+
| `createLiveHost` | 🔴 **Real backend, real Buzz.** Installs a host that proxies the block's `postMessage` traffic to civitai.com using a dev block token. Returns a handle; call `.install()` and keep the teardown, exactly like `createMockHost`. |
|
|
1383
|
+
| `LiveHostOptions` *(type)* | Options for the above. `blockToken` is required; everything else (`backendBaseUrl`, `viewer`, `theme`, `context`, `onOutbound`, …) has a default. |
|
|
1384
|
+
|
|
1385
|
+
### In a `dev:live` harness
|
|
1386
|
+
|
|
1387
|
+
```ts
|
|
1388
|
+
import { createLiveHost, type LiveHostOptions } from '@civitai/blocks-react/live';
|
|
1389
|
+
|
|
1390
|
+
// The token is a SHORT-LIVED dev block token pasted into the harness env, never
|
|
1391
|
+
// an API key: `POST /api/v1/blocks/dev-token`, ~4h, re-minted by hand.
|
|
1392
|
+
const options: LiveHostOptions = {
|
|
1393
|
+
blockToken: devBlockToken,
|
|
1394
|
+
theme: 'dark',
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
const host = createLiveHost(options);
|
|
1398
|
+
const uninstall = host.install();
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
### Stability of `/live`
|
|
1402
|
+
|
|
1403
|
+
The same terms as `./testing`: a normal subpath of a `0.x` package where a minor
|
|
1404
|
+
may break it, with the runtime symbol set pinned by
|
|
1405
|
+
`test/subpathSurfaces.test.ts` so it cannot change silently.
|
|
1406
|
+
|
|
1407
|
+
🔴 One cost worth stating plainly: publishing and documenting this subpath makes
|
|
1408
|
+
[#334](https://github.com/civitai/civitai-app-starters/issues/334) **item 3** —
|
|
1409
|
+
getting the live-host code out of the tarball entirely — *harder*, not easier.
|
|
1410
|
+
`./live` is now a named public entry point, so removing it later is a breaking
|
|
1411
|
+
change on a surface consumers pin against, where before it was one export among
|
|
1412
|
+
many on a subpath nobody was told to rely on.
|
|
1413
|
+
|
|
1414
|
+
## What the host-simulation subpaths cost you
|
|
1415
|
+
|
|
1416
|
+
Seven `dist/` modules — 266,790 B of JavaScript plus 77,338 B of `.d.ts` — are
|
|
1417
|
+
reachable only from `./testing` and `./live`, and from nothing under `.` or
|
|
1418
|
+
`./ui`. Measured by walking the built module graph:
|
|
1419
|
+
|
|
1420
|
+
```text
|
|
1421
|
+
118,590 dist/internal/mockHost.js ← ./testing (createMockHost)
|
|
1422
|
+
86,688 dist/internal/liveHost.js ← ./live (createLiveHost)
|
|
1423
|
+
29,508 dist/internal/pickerOverlay.js ← ./live (via liveHost)
|
|
1424
|
+
15,351 dist/internal/catalog.js ← ./live (via pickerOverlay)
|
|
1425
|
+
9,141 dist/testing.js
|
|
1426
|
+
4,697 dist/internal/consent.js ← BOTH hosts import it
|
|
1427
|
+
2,815 dist/live.js
|
|
1428
|
+
```
|
|
1429
|
+
|
|
1430
|
+
**They ship in every install**, production dependency trees included. They are
|
|
1431
|
+
tree-shaken out of application *bundles* — no block ships a mock host to a
|
|
1432
|
+
browser — so this is `node_modules` weight, not bundle weight.
|
|
1433
|
+
|
|
1434
|
+
🔴 **Splitting `createLiveHost` onto its own subpath removed none of this — it
|
|
1435
|
+
ADDS 4,447 B of code, and trimming the export list moved nothing either.**
|
|
1436
|
+
Measured with `pnpm pack` on both sides of the split, in a detached worktree so
|
|
1437
|
+
neither pack is contaminated by the other change in this release: **319 → 323
|
|
1438
|
+
entries, 1,590,099 B → 1,597,161 B uncompressed.** The only files that differ are
|
|
1439
|
+
|
|
1440
|
+
```text
|
|
1441
|
+
+6,048 dist/live.* (new: .js 2,815, .d.ts 2,839, + maps)
|
|
1442
|
+
-1,692 dist/testing.* (the re-export and its docblock leaving)
|
|
1443
|
+
+91 package.json (the new exports-map key)
|
|
1444
|
+
+2,615 README.md (this section)
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
`liveHost.js`, `pickerOverlay.js` and `catalog.js` do not appear in that diff at
|
|
1448
|
+
all — they are byte-identical and still in the tarball. `files` is
|
|
1449
|
+
`["dist", "README.md"]` and `tsconfig` compiles all of `src/**/*`, so the
|
|
1450
|
+
`exports` map has no bearing whatsoever on tarball contents; it decides only what
|
|
1451
|
+
a consumer can *name*.
|
|
1452
|
+
**The `/live` split buys safety, not size.** Moving these bytes needs the code
|
|
1453
|
+
deleted or published as a second artifact; that is
|
|
1454
|
+
[#334](https://github.com/civitai/civitai-app-starters/issues/334) item 3, and it
|
|
1455
|
+
is not done here.
|
|
1456
|
+
|
|
1137
1457
|
## Examples
|
|
1138
1458
|
|
|
1139
1459
|
Runnable, minimal blocks — one per feature, each with its own README:
|
|
@@ -14,9 +14,23 @@ export interface AppStorageListResult {
|
|
|
14
14
|
export interface AppStorageQuota {
|
|
15
15
|
usedBytes: number;
|
|
16
16
|
rowCount: number;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Host-enforced byte ceiling for this (app, viewer). **Render THIS, never a
|
|
19
|
+
* hard-coded figure** — the ceiling moves, and a UI built on a literal goes
|
|
20
|
+
* quietly wrong rather than loudly wrong. Matches
|
|
21
|
+
* `APP_STORAGE_MAX_BYTES` from `@civitai/app-sdk/blocks` against the current
|
|
22
|
+
* host.
|
|
23
|
+
*/
|
|
18
24
|
limitBytes: number;
|
|
19
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Host-enforced row ceiling for this (app, viewer). Same rule: render it.
|
|
27
|
+
* Matches `APP_STORAGE_MAX_ROWS`.
|
|
28
|
+
*
|
|
29
|
+
* 🔴 THIS IS USUALLY THE BINDING ONE. Rows are small; a block caching one
|
|
30
|
+
* modest record per item a viewer touches exhausts the row ceiling while
|
|
31
|
+
* still using a small fraction of `limitBytes`, so a byte-only "x of y used"
|
|
32
|
+
* readout will show plenty of headroom right up to the rejection.
|
|
33
|
+
*/
|
|
20
34
|
limitRows: number;
|
|
21
35
|
}
|
|
22
36
|
export interface UseAppStorage {
|
|
@@ -27,9 +41,11 @@ export interface UseAppStorage {
|
|
|
27
41
|
*/
|
|
28
42
|
get<T = unknown>(key: string): Promise<T | null>;
|
|
29
43
|
/**
|
|
30
|
-
* Upsert a value. Resolves on host ack. Rejects with the host's
|
|
31
|
-
*
|
|
32
|
-
*
|
|
44
|
+
* Upsert a value. Resolves on host ack. Rejects with the host's `error`
|
|
45
|
+
* string when the value exceeds `APP_STORAGE_MAX_VALUE_BYTES`, when the
|
|
46
|
+
* per-(app, viewer) byte or ROW ceiling would be crossed
|
|
47
|
+
* (`APP_STORAGE_MAX_BYTES` / `APP_STORAGE_MAX_ROWS`), or when the viewer is
|
|
48
|
+
* anonymous. All from `@civitai/app-sdk/blocks`.
|
|
33
49
|
*/
|
|
34
50
|
set<T = unknown>(key: string, value: T): Promise<{
|
|
35
51
|
ok: true;
|
|
@@ -54,8 +70,10 @@ export interface UseAppStorage {
|
|
|
54
70
|
cursor?: string;
|
|
55
71
|
}): Promise<AppStorageListResult>;
|
|
56
72
|
/**
|
|
57
|
-
* Diagnostic: current usage + the
|
|
58
|
-
* settings widget against this
|
|
73
|
+
* Diagnostic: current usage + the host's ceilings. Build an "X of Y used"
|
|
74
|
+
* settings widget against this — taking **both** numbers from the reply, and
|
|
75
|
+
* showing ROWS as well as bytes (see {@link AppStorageQuota.limitRows} for
|
|
76
|
+
* why bytes alone mislead).
|
|
59
77
|
*/
|
|
60
78
|
getQuota(): Promise<AppStorageQuota>;
|
|
61
79
|
}
|
|
@@ -71,7 +89,20 @@ export interface UseAppStorage {
|
|
|
71
89
|
* once the transport singleton is created, so it's safe to put in
|
|
72
90
|
* dependency arrays of `useEffect` / `useMemo`.
|
|
73
91
|
*
|
|
74
|
-
*
|
|
92
|
+
* 🔴 THE NAMESPACE AND THE BUDGET ARE SCOPED DIFFERENTLY. Keys are namespaced
|
|
93
|
+
* per (block instance, viewer) — the tuple above. The BYTE and ROW budgets
|
|
94
|
+
* (`APP_STORAGE_MAX_BYTES` / `APP_STORAGE_MAX_ROWS`, plus
|
|
95
|
+
* `APP_STORAGE_MAX_VALUE_BYTES` per value, all from `@civitai/app-sdk/blocks`)
|
|
96
|
+
* are enforced per (APP, viewer): every instance of the same app draws on ONE
|
|
97
|
+
* budget for that viewer.
|
|
98
|
+
*
|
|
99
|
+
* 🔴 THE CONSTANTS ARE A SNAPSHOT; `getQuota()` IS THE AUTHORITY. They are the
|
|
100
|
+
* ceilings as of the `@civitai/app-sdk` version you installed — a figure
|
|
101
|
+
* compiled into a published package is still a frozen figure, and the host can
|
|
102
|
+
* move a ceiling without your lockfile changing. Render `getQuota()`'s reply
|
|
103
|
+
* anywhere a viewer sees a number or a code path decides whether a write will
|
|
104
|
+
* fit; reach for a constant only where no reply is available (a test fixture, a
|
|
105
|
+
* design-time estimate), and re-check after an SDK bump.
|
|
75
106
|
*
|
|
76
107
|
* @example
|
|
77
108
|
* const storage = useAppStorage();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAppStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB
|
|
1
|
+
{"version":3,"file":"useAppStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC7D;;;OAGG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,IAAI,aAAa,CAiF7C"}
|
|
@@ -14,7 +14,20 @@ import { sendTypedRequest } from '../internal/transport.js';
|
|
|
14
14
|
* once the transport singleton is created, so it's safe to put in
|
|
15
15
|
* dependency arrays of `useEffect` / `useMemo`.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* 🔴 THE NAMESPACE AND THE BUDGET ARE SCOPED DIFFERENTLY. Keys are namespaced
|
|
18
|
+
* per (block instance, viewer) — the tuple above. The BYTE and ROW budgets
|
|
19
|
+
* (`APP_STORAGE_MAX_BYTES` / `APP_STORAGE_MAX_ROWS`, plus
|
|
20
|
+
* `APP_STORAGE_MAX_VALUE_BYTES` per value, all from `@civitai/app-sdk/blocks`)
|
|
21
|
+
* are enforced per (APP, viewer): every instance of the same app draws on ONE
|
|
22
|
+
* budget for that viewer.
|
|
23
|
+
*
|
|
24
|
+
* 🔴 THE CONSTANTS ARE A SNAPSHOT; `getQuota()` IS THE AUTHORITY. They are the
|
|
25
|
+
* ceilings as of the `@civitai/app-sdk` version you installed — a figure
|
|
26
|
+
* compiled into a published package is still a frozen figure, and the host can
|
|
27
|
+
* move a ceiling without your lockfile changing. Render `getQuota()`'s reply
|
|
28
|
+
* anywhere a viewer sees a number or a code path decides whether a write will
|
|
29
|
+
* fit; reach for a constant only where no reply is available (a test fixture, a
|
|
30
|
+
* design-time estimate), and re-check after an SDK bump.
|
|
18
31
|
*
|
|
19
32
|
* @example
|
|
20
33
|
* const storage = useAppStorage();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAppStorage.js","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useAppStorage.js","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,10 +25,14 @@ declare function useTransportSnapshot(): BlockSnapshot;
|
|
|
25
25
|
* moves (today's behaviour).
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
|
+
* import { isSignedIn } from '@civitai/app-sdk/blocks';
|
|
28
29
|
* const { ready, context, viewer, theme, settings } = useBlockContext();
|
|
29
30
|
* if (!ready) return <div>Loading…</div>;
|
|
30
31
|
* // Set data-theme on YOUR root — the host can't reach into the iframe (gotcha #60).
|
|
31
|
-
*
|
|
32
|
+
* // Sign-in gate: call `isSignedIn`. NOT `viewer?.username` — an identity read
|
|
33
|
+
* // standing in for a presence check, on a field that is `@deprecated` and
|
|
34
|
+
* // scheduled for removal. This snippet used to do exactly that.
|
|
35
|
+
* return <div data-theme={theme}>{isSignedIn(viewer) ? 'Hi there' : 'Hi anon'}</div>;
|
|
32
36
|
*/
|
|
33
37
|
export declare function useBlockContext(): Pick<BlockSnapshot, 'ready' | 'renderMode' | 'context' | 'token' | 'settings' | 'viewer' | 'theme' | 'blockId' | 'blockInstanceId' | 'appId'>;
|
|
34
38
|
/** Re-exported so other hooks in this package can share the subscription. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBlockContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;GAGG;AACH,iBAAS,oBAAoB,IAAI,aAAa,CAS7C;AAED
|
|
1
|
+
{"version":3,"file":"useBlockContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;GAGG;AACH,iBAAS,oBAAoB,IAAI,aAAa,CAS7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,IAAI,IAAI,CACrC,aAAa,EACX,OAAO,GACP,YAAY,GACZ,SAAS,GACT,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,GACP,SAAS,GACT,iBAAiB,GACjB,OAAO,CACV,CAcA;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -32,10 +32,14 @@ function useTransportSnapshot() {
|
|
|
32
32
|
* moves (today's behaviour).
|
|
33
33
|
*
|
|
34
34
|
* @example
|
|
35
|
+
* import { isSignedIn } from '@civitai/app-sdk/blocks';
|
|
35
36
|
* const { ready, context, viewer, theme, settings } = useBlockContext();
|
|
36
37
|
* if (!ready) return <div>Loading…</div>;
|
|
37
38
|
* // Set data-theme on YOUR root — the host can't reach into the iframe (gotcha #60).
|
|
38
|
-
*
|
|
39
|
+
* // Sign-in gate: call `isSignedIn`. NOT `viewer?.username` — an identity read
|
|
40
|
+
* // standing in for a presence check, on a field that is `@deprecated` and
|
|
41
|
+
* // scheduled for removal. This snippet used to do exactly that.
|
|
42
|
+
* return <div data-theme={theme}>{isSignedIn(viewer) ? 'Hi there' : 'Hi anon'}</div>;
|
|
39
43
|
*/
|
|
40
44
|
export function useBlockContext() {
|
|
41
45
|
const snap = useTransportSnapshot();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBlockContext.js","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD;;;GAGG;AACH,SAAS,oBAAoB;IAC3B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,OAAO,oBAAoB,CACzB,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE;IAC7B,iEAAiE;IACjE,mEAAmE;IACnE,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"useBlockContext.js","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD;;;GAGG;AACH,SAAS,oBAAoB;IAC3B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,OAAO,oBAAoB,CACzB,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE;IAC7B,iEAAiE;IACjE,mEAAmE;IACnE,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,eAAe;IAa7B,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -47,7 +47,9 @@
|
|
|
47
47
|
* tRPC procedures (publicProcedure + verifyBlockToken, FLAT `{ blockToken, … }`
|
|
48
48
|
* input). Reads need the `apps:storage:read` scope, writes `apps:storage:write`
|
|
49
49
|
* — the dev token already carries whatever the local manifest declared, and the
|
|
50
|
-
* server enforces. Real per
|
|
50
|
+
* server enforces. Real KV — keys namespaced per (block_instance, user), byte
|
|
51
|
+
* and row budgets enforced per (app, user) — with the REAL ceilings, so a
|
|
52
|
+
* write this host accepts is one production would accept.
|
|
51
53
|
*
|
|
52
54
|
* SET_USER_CHECKPOINT (Phase 4): FORWARDED (faithful) to the block-token
|
|
53
55
|
* `blocks.updateUserSettings` mutation — never fabricated. The default page
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"liveHost.d.ts","sourceRoot":"","sources":["../../src/internal/liveHost.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"liveHost.d.ts","sourceRoot":"","sources":["../../src/internal/liveHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAEH,OAAO,EACL,KAAK,YAAY,EAGjB,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,UAAU,EAGhB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,QAAQ,EAAyC,MAAM,eAAe,CAAC;AACrF,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAK5B;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,UAAU,CAAC;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACvD;AAQD;;;;;GAKG;AACH,UAAU,wBAAwB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,CAgB/E;AAyHD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAw9CjE"}
|
|
@@ -47,7 +47,9 @@
|
|
|
47
47
|
* tRPC procedures (publicProcedure + verifyBlockToken, FLAT `{ blockToken, … }`
|
|
48
48
|
* input). Reads need the `apps:storage:read` scope, writes `apps:storage:write`
|
|
49
49
|
* — the dev token already carries whatever the local manifest declared, and the
|
|
50
|
-
* server enforces. Real per
|
|
50
|
+
* server enforces. Real KV — keys namespaced per (block_instance, user), byte
|
|
51
|
+
* and row budgets enforced per (app, user) — with the REAL ceilings, so a
|
|
52
|
+
* write this host accepts is one production would accept.
|
|
51
53
|
*
|
|
52
54
|
* SET_USER_CHECKPOINT (Phase 4): FORWARDED (faithful) to the block-token
|
|
53
55
|
* `blocks.updateUserSettings` mutation — never fabricated. The default page
|
|
@@ -396,10 +398,9 @@ export function createLiveHost(options) {
|
|
|
396
398
|
return anonFallbackViewer();
|
|
397
399
|
// `/api/v1/blocks/me` is the AUTHORITATIVE self-read and does carry
|
|
398
400
|
// `status` — but this builds `BLOCK_INIT.viewer`, which the real host
|
|
399
|
-
// projects down to `{ id, username }`
|
|
400
|
-
// `
|
|
401
|
-
//
|
|
402
|
-
// in either version (civitai #2521). Forwarding `me.status` here would
|
|
401
|
+
// projects down to exactly `{ id, username, signedIn }` (civitai/civitai
|
|
402
|
+
// `main`'s `withSignedInFlag`). Never moderation state (civitai #2521).
|
|
403
|
+
// Forwarding `me.status` here would
|
|
403
404
|
// make the live dev host more generous than production. A block that
|
|
404
405
|
// wants `status` must ask for it via `GET_VIEWER` / `useViewer()`, which
|
|
405
406
|
// is exactly the scope-gated path this release steers authors to.
|
|
@@ -1012,7 +1013,9 @@ export function createLiveHost(options) {
|
|
|
1012
1013
|
}
|
|
1013
1014
|
case 'APP_STORAGE_SET': {
|
|
1014
1015
|
// Mutation apps.storage.set {blockToken, key, value} (POST). The
|
|
1015
|
-
// server enforces apps:storage:write + the
|
|
1016
|
+
// server enforces apps:storage:write + the per-value cap and both
|
|
1017
|
+
// per-(app, viewer) budgets — no simulation here, so nothing to
|
|
1018
|
+
// keep in sync.
|
|
1016
1019
|
const key = typed.payload?.key ?? '';
|
|
1017
1020
|
const value = typed.payload?.value;
|
|
1018
1021
|
void callTrpcData('apps.storage.set', { blockToken: rawToken, key, value }, 'POST').then((r) => {
|
|
@@ -1461,23 +1464,21 @@ export function createLiveHost(options) {
|
|
|
1461
1464
|
/**
|
|
1462
1465
|
* A minimal anon-ish viewer used when `/api/v1/blocks/me` can't be reached.
|
|
1463
1466
|
*
|
|
1464
|
-
* Carries EXACTLY `{ id, username, signedIn }` — the key set the
|
|
1465
|
-
*
|
|
1467
|
+
* Carries EXACTLY `{ id, username, signedIn }` — byte-for-byte the key set the
|
|
1468
|
+
* real host puts on the wire:
|
|
1466
1469
|
*
|
|
1467
|
-
* - NO `status
|
|
1468
|
-
*
|
|
1469
|
-
*
|
|
1470
|
-
*
|
|
1471
|
-
*
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
* `
|
|
1475
|
-
* (OPEN, unmerged), which also moves the host's pinned key set to
|
|
1476
|
-
* `['id', 'signedIn', 'username']`. Emitted here so the field is exercisable
|
|
1477
|
-
* locally ahead of the host — `viewer !== null` is still the gate to SHIP.
|
|
1470
|
+
* - NO `status`. The platform withholds the viewer's moderation state from
|
|
1471
|
+
* third-party iframes (civitai #2521), so a dev host that sent it would
|
|
1472
|
+
* invite a block to read a field production never provides.
|
|
1473
|
+
* - WITH `signedIn: true`. civitai/civitai `main`'s `withSignedInFlag`
|
|
1474
|
+
* (`src/components/AppBlocks/projectBlockInit.ts`) stamps it on every
|
|
1475
|
+
* present viewer from BOTH host surfaces, and that repo's
|
|
1476
|
+
* `__tests__/projectBlockInit.test.ts` pins the viewer key set as exactly
|
|
1477
|
+
* `['id', 'signedIn', 'username']`.
|
|
1478
1478
|
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1479
|
+
* 🔴 THE PROPERTY THIS FENCE HOLDS: the dev hosts must not be more generous
|
|
1480
|
+
* than the host they imitate — this default MATCHES the host, it does not run
|
|
1481
|
+
* ahead of it. See `DEFAULT_VIEWER` in `mockHost` for the same note.
|
|
1481
1482
|
*/
|
|
1482
1483
|
function anonFallbackViewer() {
|
|
1483
1484
|
return { id: 0, username: 'dev-live', signedIn: true };
|