@roughapp/feature 0.2.1 → 0.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,66 @@ All notable changes to `@roughapp/feature`.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
  This package is pre-1.0, so a minor version bump can contain breaking changes.
7
7
 
8
+ ## 0.3.0 - 2026-08-10
9
+
10
+ **This release has breaking API changes.** Rough now uses an explicit client for
11
+ each project instead of global initialization. Apps can safely show features
12
+ from multiple Rough projects at the same time, and each client has a clear,
13
+ awaitable lifecycle.
14
+
15
+ ### Added
16
+
17
+ - **`createRoughClient({ projectId, baseUrl?, fetchUserToken })`** creates and
18
+ starts a client for one project. Keep the client for as long as your app needs
19
+ that project, then call `await client.destroy()`.
20
+ - **`whenRoughClientReady({ client })`** lets you wait for startup or handle a
21
+ startup error.
22
+ - **`openRoughCreate({ target })`** lets you attach the create modal inside the
23
+ element that scopes your Rough theme. It defaults to `document.body`.
24
+
25
+ ### Changed
26
+
27
+ - **All stateful functions and components now take a client.** Pass `client` and
28
+ `surface` to `getRoughFeatures()`, `openRoughCreate()`, `<rough-surface>`,
29
+ `<rough-feature>`, `<rough-edit-button>` and the modal elements.
30
+ - **Cleanup methods are asynchronous and safe to call more than once.** Await
31
+ `client.destroy()`, `subscription.unsubscribe()` and `modal.close()` when you
32
+ need to know that cleanup has finished.
33
+ - **Create one client and share it for each signed-in person and project.** A
34
+ second client for the same `baseUrl`, `projectId` and person fails with
35
+ `RoughReplicacheIdentityConflictError`. Clients for different projects or
36
+ different signed-in people can run together.
37
+ - **`openRoughCreate()` no longer requires a mounted Rough component.** You can
38
+ open it directly with a client and surface.
39
+
40
+ ### Removed
41
+
42
+ - **`initRough()` has been removed.** There is no default or global client.
43
+ - **`defineSurface()` has been replaced by `defineRoughSurface()`.** Rename
44
+ `toolList` to `tools` when updating your surface definitions.
45
+ - **`registerSurfaceEntry` has been removed.** You no longer need to register a
46
+ surface before using it.
47
+
48
+ ### Migrating
49
+
50
+ ```ts
51
+ // Before
52
+ initRough({ projectId, fetchUserToken })
53
+ const surface = defineSurface({ key, name, description, toolList })
54
+ const unsubscribe = getRoughFeatures(surface, onFeatures)
55
+ openRoughCreate(surface, { projectId })
56
+
57
+ // After
58
+ const client = createRoughClient({ projectId, fetchUserToken })
59
+ const surface = defineRoughSurface({ key, name, description, tools })
60
+ const subscription = getRoughFeatures({ client, surface, onFeatures })
61
+ const modal = await openRoughCreate({ client, surface })
62
+
63
+ await modal.close()
64
+ await subscription.unsubscribe()
65
+ await client.destroy()
66
+ ```
67
+
8
68
  ## 0.2.1 - 2026-08-05
9
69
 
10
70
  Add `CHANGELOG.md` to the published package.
package/README.md CHANGED
@@ -10,16 +10,14 @@ of custom elements (including `<rough-surface>`).
10
10
  ## Install
11
11
 
12
12
  ```bash
13
- pnpm add @roughapp/feature
13
+ pnpm add @roughapp/feature zod
14
14
  ```
15
15
 
16
16
  This package targets **npm + a modern bundler** (Vite, Webpack, Rspack, …). It
17
17
  is ESM-only and does not support direct `<script>`/CDN usage in this release.
18
18
 
19
- `svelte`, `zod`, `capnweb`, `@andypf/json-viewer`, and
20
- `@stayradiated/error-boundary` are installed automatically as runtime
21
- dependencies — you do not need to add them yourself, though you can import `zod`
22
- directly (see below).
19
+ Install `zod` directly because your tool definitions import it. The package
20
+ installs its other runtime dependencies automatically.
23
21
 
24
22
  ## Import the stylesheet
25
23
 
@@ -40,14 +38,19 @@ A surface describes a place in your product where Rough features appear, plus
40
38
  the tools the feature can call. Import `z` from `zod` directly for tool schemas:
41
39
 
42
40
  ```ts
43
- import { defineSurface, Query, Mutation, Subscription } from '@roughapp/feature'
41
+ import {
42
+ defineRoughSurface,
43
+ Query,
44
+ Mutation,
45
+ Subscription,
46
+ } from '@roughapp/feature'
44
47
  import { z } from 'zod'
45
48
 
46
- export const inboxSurface = defineSurface({
49
+ export const inboxSurface = defineRoughSurface({
47
50
  key: 'inbox',
48
51
  name: 'Inbox',
49
52
  description: 'The main message inbox.',
50
- toolList: [
53
+ tools: [
51
54
  new Query({
52
55
  id: 'listMessages',
53
56
  name: 'List Messages',
@@ -60,7 +63,8 @@ export const inboxSurface = defineSurface({
60
63
  return []
61
64
  },
62
65
  }),
63
- // Mutation and Subscription take the same options shape.
66
+ // Mutation uses the same fields. A Subscription implementation receives
67
+ // a callback before the input.
64
68
  ],
65
69
  })
66
70
  ```
@@ -68,65 +72,137 @@ export const inboxSurface = defineSurface({
68
72
  `Query`, `Mutation`, and `Subscription` are re-exported from
69
73
  `@roughapp/bridge` for convenience.
70
74
 
71
- ### Initialize the SDK
75
+ `defineRoughSurface()` validates and freezes the surface and its tools array.
76
+ The definition holds no client, project id or connection state, so you can hand
77
+ the same value to clients for two different projects at once.
78
+
79
+ ### Create a client
80
+
81
+ `createRoughClient()` returns synchronously and immediately starts that
82
+ project's authentication, local database and first sync. You own the client
83
+ until you destroy it.
72
84
 
73
85
  ```ts
74
- import { initRough } from '@roughapp/feature'
86
+ import { createRoughClient } from '@roughapp/feature'
75
87
 
76
- initRough({
88
+ const client = createRoughClient({
77
89
  projectId: 'proj_123',
78
90
  fetchUserToken: async () => myAppSession.getRoughToken(),
79
91
  // baseUrl is optional; defaults to the Rough production API.
80
92
  })
81
93
  ```
82
94
 
95
+ Create the client at whatever owns the project in your app: a route, a provider
96
+ component, a page controller. Share that one client with everything below it
97
+ rather than creating a second. Within one JavaScript realm, create only one live
98
+ client for each `<baseUrl, projectId, personId>` identity. If you create another,
99
+ its startup fails with `RoughReplicacheIdentityConflictError`.
100
+
101
+ ### Destroy it when you are done
102
+
103
+ ```ts
104
+ await client.destroy()
105
+ ```
106
+
107
+ `destroy()` is the only lifecycle method, and it works everywhere. It is
108
+ idempotent, returns the same promise every time, closes every subscription and
109
+ modal the client still owns, and does not resolve until all of that has
110
+ finished. A framework unmount hook may use `void client.destroy()`; tests and
111
+ controlled route transitions should await it.
112
+
113
+ The client implements no disposal protocol. Teardown here is genuinely
114
+ asynchronous, and there is one way to ask for it.
115
+
116
+ Startup runs in the background. If you want to observe it:
117
+
118
+ ```ts
119
+ import { whenRoughClientReady } from '@roughapp/feature'
120
+
121
+ await whenRoughClientReady({ client })
122
+ ```
123
+
124
+ A client that fails startup keeps that error and replays it from every later
125
+ operation. It is not restarted; destroy it and create a new one.
126
+
83
127
  ### Render a surface
84
128
 
85
129
  Use the `<rough-surface>` custom element, or the `RoughSurface` component
86
- export. Set the `surface` property to a `defineSurface(...)` result:
130
+ export. Set both the client and the surface:
87
131
 
88
132
  ```ts
89
133
  import '@roughapp/feature'
90
134
 
91
135
  const el = document.createElement('rough-surface')
136
+ el.client = client
92
137
  el.surface = inboxSurface
93
138
  document.querySelector('#rough-slot')?.append(el)
94
139
  ```
95
140
 
96
141
  ### List features and open the create flow
97
142
 
143
+ Operations are direct exports and take the client in their options. Neither
144
+ requires anything to have mounted first.
145
+
98
146
  ```ts
99
147
  import { getRoughFeatures, openRoughCreate } from '@roughapp/feature'
100
148
 
101
149
  // Subscribe to the published features for a surface.
102
- const unsubscribe = getRoughFeatures(inboxSurface, (features) => {
103
- console.log(features)
150
+ const subscription = getRoughFeatures({
151
+ client,
152
+ surface: inboxSurface,
153
+ onFeatures: (features) => {
154
+ console.log(features)
155
+ },
156
+ onError: (error) => {
157
+ console.error(error)
158
+ },
104
159
  })
105
160
 
106
- // Open the "create a feature" modal for a surface.
107
- openRoughCreate(inboxSurface, { projectId: 'proj_123' })
161
+ // Optional: wait for the first feature batch.
162
+ await subscription.ready
163
+
164
+ // Open the "create a feature" modal.
165
+ const modal = await openRoughCreate({
166
+ client,
167
+ surface: inboxSurface,
168
+ // Where the modal attaches. Defaults to document.body. Pass the element that
169
+ // scopes your Rough theme if you scope it to a subtree.
170
+ target: document.querySelector('#rough-root') ?? undefined,
171
+ })
172
+
173
+ await modal.close()
174
+ await subscription.unsubscribe()
108
175
  ```
109
176
 
177
+ Every cleanup handle returns a promise, is safe to call twice, and returns the
178
+ same promise on the second call.
179
+
110
180
  ## Exports
111
181
 
112
182
  Recommended, stable-ish customer API:
113
183
 
114
- - `initRough`, `defineSurface` (+ `SurfaceDefinition` type)
115
- - `getRoughFeatures`, `openRoughCreate`
184
+ - `createRoughClient`, `whenRoughClientReady` (+ `RoughClient`,
185
+ `RoughClientOptions` types)
186
+ - `defineRoughSurface` (+ `RoughSurfaceDefinition` type)
187
+ - `getRoughFeatures` (+ `GetRoughFeaturesOptions`,
188
+ `RoughFeatureSubscription` types)
189
+ - `openRoughCreate` (+ `OpenRoughCreateOptions`, `RoughModalHandle` types)
116
190
  - `RoughSurface` / `<rough-surface>`
117
191
  - `Query`, `Mutation`, `Subscription` (re-exported from `@roughapp/bridge`)
192
+ - Errors worth branching on: `RoughClientDestroyedError`,
193
+ `RoughReplicacheIdentityConflictError`, `RoughSurfaceContractConflictError`,
194
+ `RoughClientDestroyError`, `RoughInvalidClientError`
118
195
 
119
- Advanced / incidental exports — these exist but are **not** intended as stable
120
- customer dependencies during the pre-1.0 series:
196
+ Advanced / incidental exports are **not** intended as stable customer
197
+ dependencies during the pre-1.0 series:
121
198
 
122
199
  - Custom-element components: `RoughCreateModal` (`<rough-create-modal>`),
123
200
  `RoughEditButton` (`<rough-edit-button>`), `RoughFeature` (`<rough-feature>`)
124
201
  (the `<rough-edit-modal>` element registers transitively).
125
202
  - UI building blocks: `PrimaryButton`, `SecondaryButton`, `ResizeHandle`,
126
203
  `SpriteBuildMenu`, `SpriteFrame`.
127
- - `registerSurfaceEntry`.
128
- - Types: `Sprite`, `SpriteBuild`, `JsonValue`, `SpriteFrameDatastore`,
129
- `SpriteFrameDatastoreContext`.
204
+ - Types: `Sprite`, `SpriteBuild`, `JsonValue`, `RoughCleanup`,
205
+ `FetchUserTokenFn`, `SpriteFrameDatastore`, `SpriteFrameDatastoreContext`.
130
206
 
131
207
  ## License
132
208
 
package/index.d.ts CHANGED
@@ -307,35 +307,167 @@ type Sprite = {
307
307
  publishedSpriteBuildId: SpriteBuildId | null;
308
308
  };
309
309
 
310
- type SurfaceDefinition = {
311
- key: string;
310
+ /**
311
+ * An immutable, project-independent surface contract.
312
+ *
313
+ * A definition holds no client, project id, server surface id, registration
314
+ * state, subscription, or lifecycle hook. That is what lets the same value be
315
+ * handed to two clients for two different projects at once: each resolves it
316
+ * against its own project and caches the canonical id privately.
317
+ */
318
+ type RoughSurfaceDefinition<TKey extends string = string, TTools extends readonly AnyTool[] = readonly AnyTool[]> = Readonly<{
319
+ key: TKey;
312
320
  name: string;
313
321
  description: string;
314
- toolList: readonly AnyTool[];
322
+ tools: TTools;
323
+ }>;
324
+ type DefineRoughSurfaceOptions<TKey extends string, TTools extends readonly AnyTool[]> = {
325
+ key: TKey;
326
+ name: string;
327
+ description: string;
328
+ tools: TTools;
315
329
  };
316
- declare const defineSurface: (config: SurfaceDefinition) => SurfaceDefinition;
317
-
318
- declare const getRoughFeatures: (surface: SurfaceDefinition, callback: (features: Sprite[]) => void) => (() => void);
330
+ declare const defineRoughSurface: <const TKey extends string, const TTools extends readonly AnyTool[]>(options: DefineRoughSurfaceOptions<TKey, TTools>) => RoughSurfaceDefinition<TKey, TTools>;
319
331
 
320
332
  type FetchUserTokenFn = () => string | Promise<string>;
321
333
 
322
- type SurfaceEntry = {
323
- surfaceId: SurfaceId;
324
- toolList: readonly AnyTool[];
325
- };
326
- declare const registerSurfaceEntry: (key: string, entry: SurfaceEntry) => (() => void);
327
-
328
- type InitRoughOptions = {
329
- baseUrl?: string;
334
+ type RoughClientOptions = {
330
335
  projectId: string;
336
+ baseUrl?: string;
331
337
  fetchUserToken: FetchUserTokenFn;
332
338
  };
333
- declare const initRough: (options: InitRoughOptions) => void;
339
+ /**
340
+ * A handle to one project's live Rough runtime.
341
+ *
342
+ * The client is an opaque capability: it exposes identity and lifecycle, not
343
+ * behavior. Behavior lives in the package's function exports, which take the
344
+ * client in their options.
345
+ */
346
+ type RoughClient = {
347
+ readonly projectId: string;
348
+ destroy: () => Promise<void>;
349
+ };
350
+ /**
351
+ * Creates and immediately starts a client for one Rough project.
352
+ *
353
+ * Returns synchronously so a framework provider can own the value in the same
354
+ * tick it renders, and so a client whose auth or Replicache startup is still
355
+ * pending can still be destroyed. Startup runs in the background; observe it
356
+ * with `whenRoughClientReady({ client })` if you need to.
357
+ *
358
+ * The host owns the result until it calls `client.destroy()`. There is no
359
+ * release, restart, or reuse: a failed client is destroyed and replaced.
360
+ */
361
+ declare const createRoughClient: (options: RoughClientOptions) => RoughClient;
362
+ /**
363
+ * Awaits the client's eager startup.
364
+ *
365
+ * Rejects with the retained startup error if it failed. Operations do this
366
+ * internally, so hosts only need it when they want to show startup state or
367
+ * when a test needs a deterministic point to await.
368
+ */
369
+ declare const whenRoughClientReady: (options: {
370
+ client: RoughClient;
371
+ }) => Promise<void>;
372
+
373
+ /**
374
+ * Errors that hosts are expected to branch on.
375
+ *
376
+ * These are exported from the package root so a host can distinguish "you used
377
+ * a client you already destroyed" (a programming error) from "another client
378
+ * already owns this local database" (a coordination error the host fixes by
379
+ * sharing one client).
380
+ */
381
+ /** Thrown when an operation is started on a destroying or destroyed client. */
382
+ declare class RoughClientDestroyedError extends Error {
383
+ readonly name = "RoughClientDestroyedError";
384
+ constructor(message?: string);
385
+ }
386
+ /**
387
+ * Thrown during startup when another live client in this JavaScript realm
388
+ * already owns the `<baseUrl, projectId, personId>` Replicache identity.
389
+ */
390
+ declare class RoughReplicacheIdentityConflictError extends Error {
391
+ readonly name = "RoughReplicacheIdentityConflictError";
392
+ readonly replicacheName: string;
393
+ constructor(options: {
394
+ replicacheName: string;
395
+ });
396
+ }
397
+ /**
398
+ * Thrown when two concurrent operations disagree about the serialized contract
399
+ * of one surface key on the same client.
400
+ */
401
+ declare class RoughSurfaceContractConflictError extends Error {
402
+ readonly name = "RoughSurfaceContractConflictError";
403
+ readonly surfaceKey: string;
404
+ constructor(options: {
405
+ surfaceKey: string;
406
+ });
407
+ }
408
+ /**
409
+ * Rejection value of `client.destroy()` when at least one cleanup stage failed.
410
+ *
411
+ * `destroy()` always attempts every stage, so this is reported only after all
412
+ * of them have settled. `errors` holds one entry per failed stage.
413
+ */
414
+ declare class RoughClientDestroyError extends Error {
415
+ readonly name = "RoughClientDestroyError";
416
+ readonly errors: readonly Error[];
417
+ constructor(options: {
418
+ errors: readonly Error[];
419
+ });
420
+ }
421
+ /** Thrown when a value that is not a Rough client is passed as `client`. */
422
+ declare class RoughInvalidClientError extends Error {
423
+ readonly name = "RoughInvalidClientError";
424
+ constructor(message?: string);
425
+ }
426
+
427
+ /**
428
+ * Every cleanup handle in the public API has this shape.
429
+ *
430
+ * It is a promise rather than `void` on purpose. Un-awaitable teardown is what
431
+ * lets Replicache closes, store watches, and mounted modals outlive the thing
432
+ * that owned them, which shows up as work still running during a test
433
+ * environment teardown long after the assertions passed. Calling a cleanup
434
+ * twice is always safe: the second call returns the first call's promise.
435
+ */
436
+ type RoughCleanup = () => Promise<void>;
437
+
438
+ type GetRoughFeaturesOptions = {
439
+ client: RoughClient;
440
+ surface: RoughSurfaceDefinition;
441
+ onFeatures: (features: Sprite[]) => void;
442
+ onError?: (error: Error) => void;
443
+ signal?: AbortSignal;
444
+ };
445
+ type RoughFeatureSubscription = {
446
+ /**
447
+ * Resolves after the first feature batch is published. Rejects if startup or
448
+ * surface resolution failed.
449
+ */
450
+ readonly ready: Promise<void>;
451
+ unsubscribe: RoughCleanup;
452
+ };
453
+ /** Subscribes to published features for a surface. */
454
+ declare const getRoughFeatures: (options: GetRoughFeaturesOptions) => RoughFeatureSubscription;
334
455
 
335
456
  type OpenRoughCreateOptions = {
336
- projectId?: string;
457
+ client: RoughClient;
458
+ surface: RoughSurfaceDefinition;
459
+ /** Portal target. Use when Rough theme variables are scoped below document.body. */
460
+ target?: HTMLElement;
461
+ signal?: AbortSignal;
337
462
  };
338
- declare const openRoughCreate: (surface: SurfaceDefinition, options?: OpenRoughCreateOptions) => void;
463
+ type RoughModalHandle = {
464
+ close: RoughCleanup;
465
+ };
466
+ /**
467
+ * Opens the Feature Builder for a surface. Resolves the surface itself, so no
468
+ * `<RoughSurface>` need be mounted first.
469
+ */
470
+ declare const openRoughCreate: (options: OpenRoughCreateOptions) => Promise<RoughModalHandle>;
339
471
 
340
472
  type Props$9 = {
341
473
  children?: Snippet;
@@ -377,18 +509,21 @@ declare const ResizeHandle: svelte.Component<Props$8, {}, "">;
377
509
  type ResizeHandle = ReturnType<typeof ResizeHandle>;
378
510
 
379
511
  type Props$7 = {
380
- toolList: readonly AnyTool[];
512
+ client: RoughClient;
513
+ surface: RoughSurfaceDefinition;
381
514
  surfaceId: SurfaceId;
382
- projectId?: string;
515
+ /** Portal target; use when Rough theme variables are scoped to a subtree. */
516
+ portalTarget?: HTMLElement;
383
517
  onclose?: () => void;
384
518
  };
385
519
  declare const RoughCreateModal: svelte.Component<Props$7, {}, "">;
386
520
  type RoughCreateModal = ReturnType<typeof RoughCreateModal>;
387
521
 
388
522
  type Props$6 = {
389
- toolList: readonly AnyTool[];
523
+ client: RoughClient;
524
+ surface: RoughSurfaceDefinition;
525
+ spriteId: SpriteId;
390
526
  label?: string;
391
- spriteId: string;
392
527
  };
393
528
  declare const RoughEditButton: svelte.Component<Props$6, {}, "">;
394
529
  type RoughEditButton = ReturnType<typeof RoughEditButton>;
@@ -407,16 +542,18 @@ type SpriteFrameDatastoreContext = {
407
542
  };
408
543
 
409
544
  type Props$5 = {
545
+ client: RoughClient;
546
+ surface: RoughSurfaceDefinition;
410
547
  featureId: SpriteId;
411
548
  buildId: SpriteBuildId;
412
- surfaceKey: string;
413
549
  datastore?: SpriteFrameDatastore;
414
550
  };
415
551
  declare const RoughFeature: svelte.Component<Props$5, {}, "">;
416
552
  type RoughFeature = ReturnType<typeof RoughFeature>;
417
553
 
418
554
  type Props$4 = {
419
- surface: SurfaceDefinition;
555
+ client: RoughClient;
556
+ surface: RoughSurfaceDefinition;
420
557
  getDatastore?: (context: SpriteFrameDatastoreContext) => SpriteFrameDatastore | undefined;
421
558
  };
422
559
  declare const RoughSurface: svelte.Component<Props$4, {}, "">;
@@ -440,16 +577,16 @@ declare const SecondaryButton: svelte.Component<Props$3, {}, "">;
440
577
  type SecondaryButton = ReturnType<typeof SecondaryButton>;
441
578
 
442
579
  type Props$2 = {
580
+ client: RoughClient;
443
581
  surfaceId: SurfaceId;
444
582
  onselect?: (sprite: Sprite, spriteBuild: SpriteBuild) => Promise<void> | void;
445
583
  };
446
584
  declare const SpriteBuildMenu: svelte.Component<Props$2, {}, "">;
447
585
  type SpriteBuildMenu = ReturnType<typeof SpriteBuildMenu>;
448
586
 
449
- type Props$1 = {
587
+ type BaseProps = {
450
588
  spriteId: string;
451
589
  spriteBuildId: SpriteBuildId;
452
- artifactUrl?: string;
453
590
  toolList: readonly AnyTool[];
454
591
  datastore?: SpriteFrameDatastore;
455
592
  isMock?: boolean;
@@ -457,12 +594,23 @@ type Props$1 = {
457
594
  minHeight?: number;
458
595
  maxHeight?: number;
459
596
  };
597
+ /** A client is required unless artifactUrl is provided explicitly. */
598
+ type Props$1 = BaseProps & ({
599
+ artifactUrl: string;
600
+ client?: undefined;
601
+ } | {
602
+ artifactUrl?: undefined;
603
+ client: RoughClient;
604
+ });
460
605
  declare const SpriteFrame: svelte.Component<Props$1, {}, "">;
461
606
  type SpriteFrame = ReturnType<typeof SpriteFrame>;
462
607
 
463
608
  type Props = {
464
- toolList: readonly AnyTool[];
465
- spriteId: string;
609
+ client: RoughClient;
610
+ surface: RoughSurfaceDefinition;
611
+ spriteId: SpriteId;
612
+ /** Where the dialog attaches. Decides which `--rough-*` values it inherits. */
613
+ portalTarget?: HTMLElement;
466
614
  onclose?: () => void;
467
615
  };
468
616
  declare const RoughEditModal: svelte.Component<Props, {}, "">;
@@ -494,5 +642,5 @@ declare global {
494
642
  }
495
643
  }
496
644
 
497
- export { Mutation, PrimaryButton, Query, ResizeHandle, RoughCreateModal, RoughEditButton, RoughFeature, RoughSurface, SecondaryButton, SpriteBuildMenu, SpriteFrame, Subscription, defineSurface, getRoughFeatures, initRough, openRoughCreate, registerSurfaceEntry };
498
- export type { JsonValue, RoughCreateModalElement, RoughEditButtonElement, RoughEditModalElement, RoughFeatureElement, RoughSurfaceElement, Sprite, SpriteBuild, SpriteFrameDatastore, SpriteFrameDatastoreContext, SurfaceDefinition };
645
+ export { Mutation, PrimaryButton, Query, ResizeHandle, RoughClientDestroyError, RoughClientDestroyedError, RoughCreateModal, RoughEditButton, RoughFeature, RoughInvalidClientError, RoughReplicacheIdentityConflictError, RoughSurface, RoughSurfaceContractConflictError, SecondaryButton, SpriteBuildMenu, SpriteFrame, Subscription, createRoughClient, defineRoughSurface, getRoughFeatures, openRoughCreate, whenRoughClientReady };
646
+ export type { FetchUserTokenFn, GetRoughFeaturesOptions, JsonValue, OpenRoughCreateOptions, RoughCleanup, RoughClient, RoughClientOptions, RoughCreateModalElement, RoughEditButtonElement, RoughEditModalElement, RoughFeatureElement, RoughFeatureSubscription, RoughModalHandle, RoughSurfaceDefinition, RoughSurfaceElement, Sprite, SpriteBuild, SpriteFrameDatastore, SpriteFrameDatastoreContext };