@pstdio/sdk 0.22.0 → 0.23.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
@@ -32,6 +32,32 @@ Page targets change location. Panel targets preserve it. Compound targets contai
32
32
 
33
33
  Use `qualifyRef(owner, ref)` in provider contract modules. Keep definitions local and pass qualified refs between extensions. For webviews, declare capabilities and call the typed `GuestHost`; `placement.close` closes the calling placement through the normal tab controller.
34
34
 
35
+ ## Dashboard URLs
36
+
37
+ Use `serializePageUrl({ projectId, page, resource })` to return a dashboard link from a command. The page descriptor contains its `id`, qualified `ref`, and declared `path`; `resource` is optional. The workbench uses the same route and resource codec.
38
+
39
+ Use `parsePageUrl({ url, projectId, pages })` to resolve a saved link against the expected project and allowed page descriptors. It accepts root-relative URLs and returns a page ID and optional resource, or `undefined` for an unsupported destination. It never fetches the URL. The caller still checks its supported resource kind and owner. A URL grants no access.
40
+
41
+ Page and panel navigation targets remain the API for opening views and tabs.
42
+
43
+ ## Webview change subscriptions
44
+
45
+ Use the typed client's `events.subscribe(event, listener)` to refetch after a command changes data:
46
+
47
+ ```ts
48
+ const client = createWebviewClient<typeof commands>(host);
49
+ const stop = client.events.subscribe(
50
+ { kind: "event", id: "notes.changed" },
51
+ () => void reloadNotes(),
52
+ );
53
+ // Dispose when the view unmounts.
54
+ stop();
55
+ ```
56
+
57
+ Local event refs use the client's extension owner: `{ kind: "event", id: "notes.changed" }` resolves to `<extensionId>.event.notes.changed`. Qualified event refs can name another extension in the same project. String IDs are already resolved and stay unchanged. Host-owned and command-lifecycle refs follow the runtime's rules through the shared `resolveEventReferenceId` helper. Commands emit events after committing their changes through `ctx.events.emit`.
58
+
59
+ The host delivers only events matching the view's project. Projectless events reach global views only. Listeners also run when host sync connects or reconnects, so a mounted view can reconcile missed changes. Load current data when mounting, then subscribe; events are invalidations, not stored records or an exactly-once stream. Duplicate refetches must be safe. The subscription does not reload the webview document or grant new permissions.
60
+
35
61
  ## Package delivery
36
62
 
37
63
  Development and installed consumers both load built SDK entries. Repository development builds this package before starting the source CLI. Builds stage release files under `.publish` through the shared release script. Package verification installs those same staged artifacts into a temporary directory outside the monorepo and checks every entry point.
@@ -499,21 +499,26 @@ var createSyncClient = (clientOptions) => ({
499
499
  if (event === "init")
500
500
  setConnected();
501
501
  };
502
+ const readConnection = async (signal) => {
503
+ const response = await resolveFetch(clientOptions)(resolveClientUrl(resolveBaseUrl(clientOptions), buildSyncStreamPath(lastSeq)), {
504
+ headers: Object.fromEntries(createRequestHeaders(clientOptions).entries()),
505
+ signal,
506
+ credentials: "same-origin"
507
+ });
508
+ if (!response.ok || !response.body)
509
+ throw new Error("SSE connection failed");
510
+ if (closed)
511
+ return;
512
+ if (lastSeq > 0)
513
+ setConnected();
514
+ await readSseStream(response.body, ({ event, data }) => handleEvent(event, data), { signal });
515
+ };
502
516
  const connect = async () => {
503
517
  if (closed)
504
518
  return;
505
519
  abortController = new AbortController;
506
520
  try {
507
- const response = await resolveFetch(clientOptions)(resolveClientUrl(resolveBaseUrl(clientOptions), buildSyncStreamPath(lastSeq)), {
508
- headers: Object.fromEntries(createRequestHeaders(clientOptions).entries()),
509
- signal: abortController.signal,
510
- credentials: "same-origin"
511
- });
512
- if (!response.ok || !response.body)
513
- throw new Error("SSE connection failed");
514
- await readSseStream(response.body, ({ event, data }) => handleEvent(event, data), {
515
- signal: abortController.signal
516
- });
521
+ await readConnection(abortController.signal);
517
522
  } catch (err) {
518
523
  if (closed || err.name === "AbortError")
519
524
  return;
package/dist/client.d.ts CHANGED
@@ -1432,6 +1432,7 @@ export declare type SseEvent = {
1432
1432
  };
1433
1433
 
1434
1434
  export declare type StartSyncInput = SyncWriterProvider & {
1435
+ /** Called after the initial snapshot, or when a cursor-based connection resumes. */
1435
1436
  onConnected?: () => void;
1436
1437
  onDisconnected?: () => void;
1437
1438
  onConnectionLost?: () => void;
@@ -214,6 +214,11 @@ var WEBVIEW_HOST_CAPABILITIES = [
214
214
  ...WEBVIEW_SCOPED_DECLARABLE_CAPABILITIES,
215
215
  ...ALWAYS_AVAILABLE_WEBVIEW_CAPABILITIES
216
216
  ];
217
+ // package.json
218
+ var version = "0.23.0";
219
+
220
+ // src/version.ts
221
+ var SDK_VERSION = version;
217
222
  // src/extensions/command-outcome.ts
218
223
  var unwrapCommandOutcome = (response, fallbackReason = "Command failed.") => {
219
224
  const { outcome } = response;
@@ -340,6 +345,180 @@ var defineExtensionView = (definition) => ({
340
345
  return typeof cleanup === "function" ? cleanup : () => {};
341
346
  }
342
347
  });
348
+ // src/extensions/event-reference.ts
349
+ var resolveEventReferenceId = (ref, ownerExtensionId) => {
350
+ if (typeof ref === "string")
351
+ return ref;
352
+ const extensionId = ref.extensionId ?? ownerExtensionId;
353
+ if (!extensionId || extensionId === "pstdio")
354
+ return ref.id;
355
+ const lifecycle = /^(command\.(?:requested|started|completed|rejected|failed):)(.+)$/.exec(ref.id);
356
+ if (lifecycle)
357
+ return `${lifecycle[1]}${extensionId}.command.${lifecycle[2]}`;
358
+ return `${extensionId}.event.${ref.id}`;
359
+ };
360
+ // src/extensions/page-resource-codec.ts
361
+ var fromUri = (uri) => {
362
+ try {
363
+ const parsed = new URL(uri);
364
+ if (parsed.protocol !== "pstdio:" || parsed.hostname !== "extension-resource")
365
+ return;
366
+ const [type, id, ...rest] = parsed.pathname.slice(1).split("/");
367
+ if (!type || !id || rest.length > 0)
368
+ return;
369
+ const extensionId = parsed.searchParams.get("extensionId");
370
+ const projectId = parsed.searchParams.get("projectId");
371
+ return {
372
+ type: decodeURIComponent(type),
373
+ id: decodeURIComponent(id),
374
+ ...extensionId ? { extensionId } : {},
375
+ ...projectId ? { projectId } : {}
376
+ };
377
+ } catch {
378
+ return;
379
+ }
380
+ };
381
+ var defaultPageResourceCodec = {
382
+ normalize: (resource) => ({ ...resource }),
383
+ toUri: (resource) => {
384
+ const uri = `pstdio://extension-resource/${encodeURIComponent(resource.type)}/${encodeURIComponent(resource.id)}`;
385
+ const query = new URLSearchParams;
386
+ if (resource.extensionId)
387
+ query.set("extensionId", resource.extensionId);
388
+ if (resource.projectId)
389
+ query.set("projectId", resource.projectId);
390
+ return query.size ? `${uri}?${query}` : uri;
391
+ },
392
+ fromUri
393
+ };
394
+ // src/extensions/page-url.ts
395
+ var hostExtensionId = "pstdio";
396
+ var pageRefKey = (ref) => `${ref.extensionId ?? ""}:page:${ref.id}`;
397
+ var pageForRef = (pages, ref) => pages.find((page) => pageRefKey(page.ref) === pageRefKey(ref));
398
+ var encodePath = (path) => path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
399
+ var routePath = (projectId, page) => {
400
+ const project = `/projects/${encodeURIComponent(projectId)}`;
401
+ const path = encodePath(page.path);
402
+ if (page.ref.extensionId === hostExtensionId)
403
+ return path ? `${project}/${path}` : project;
404
+ if (!page.ref.extensionId)
405
+ throw new Error(`Page "${page.id}" has no extension id`);
406
+ return `${project}/extensions/${encodeURIComponent(page.ref.extensionId)}/${path}`;
407
+ };
408
+ var normalizedSection = (value) => {
409
+ if (!value || typeof value !== "object" || !Array.isArray(value.anchors))
410
+ return;
411
+ const anchors = value.anchors;
412
+ if (anchors.some((anchor) => !anchor || typeof anchor !== "object" || typeof anchor.id !== "string" || typeof anchor.heading !== "string" || anchor.occurrence !== undefined && (!Number.isInteger(anchor.occurrence) || Number(anchor.occurrence) < 0))) {
413
+ return;
414
+ }
415
+ return {
416
+ anchors: anchors.map((anchor) => {
417
+ const value2 = anchor;
418
+ return {
419
+ id: value2.id,
420
+ heading: value2.heading,
421
+ ...value2.occurrence !== undefined ? { occurrence: value2.occurrence } : {}
422
+ };
423
+ })
424
+ };
425
+ };
426
+ var serializeSection = (section) => {
427
+ const normalized = normalizedSection(section);
428
+ if (!normalized)
429
+ throw new Error("Page location has an invalid section");
430
+ return JSON.stringify(normalized);
431
+ };
432
+ var parseSection = (value) => {
433
+ try {
434
+ return normalizedSection(JSON.parse(value));
435
+ } catch {
436
+ return;
437
+ }
438
+ };
439
+ var serializeWorkbenchPageUrl = (input) => {
440
+ const page = pageForRef(input.pages, input.location.page);
441
+ if (!page)
442
+ throw new Error(`Unknown page ref: ${pageRefKey(input.location.page)}`);
443
+ const query = new URLSearchParams;
444
+ if (input.location.resource) {
445
+ query.set("resource", input.resources.toUri(input.resources.normalize(input.location.resource)));
446
+ }
447
+ if (input.location.section)
448
+ query.set("section", serializeSection(input.location.section));
449
+ const encoded = query.toString();
450
+ const path = routePath(input.projectId, page);
451
+ return encoded ? `${path}?${encoded}` : path;
452
+ };
453
+ var decodedSegments = (pathname) => {
454
+ try {
455
+ return pathname.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
456
+ } catch {
457
+ return;
458
+ }
459
+ };
460
+ var isWorkbenchProjectUrl = (urlValue, projectId) => {
461
+ try {
462
+ const url = new URL(urlValue, "http://workbench.local");
463
+ const segments = decodedSegments(url.pathname);
464
+ return Boolean(segments && segments[0] === "projects" && segments[1] === projectId);
465
+ } catch {
466
+ return false;
467
+ }
468
+ };
469
+ var pageForRoute = (pages, extensionId, path) => pages.find((page) => page.ref.extensionId === extensionId && page.path === path);
470
+ var parseResource = (uri, resources) => {
471
+ try {
472
+ const resource = resources.fromUri(uri);
473
+ return resource ? resources.normalize(resource) : undefined;
474
+ } catch {
475
+ return;
476
+ }
477
+ };
478
+ var parseWorkbenchPageUrl = (input) => {
479
+ let url;
480
+ try {
481
+ url = new URL(input.url, "http://workbench.local");
482
+ } catch {
483
+ return;
484
+ }
485
+ const segments = decodedSegments(url.pathname);
486
+ if (!segments || segments[0] !== "projects" || segments[1] !== input.projectId)
487
+ return;
488
+ const route = segments.slice(2);
489
+ const extensionRoute = route[0] === "extensions";
490
+ const extensionId = extensionRoute ? route[1] : hostExtensionId;
491
+ const path = (extensionRoute ? route.slice(2) : route).join("/");
492
+ if (!extensionId || extensionRoute && !path)
493
+ return;
494
+ const page = pageForRoute(input.pages, extensionId, path);
495
+ if (!page)
496
+ return;
497
+ const resourceValue = url.searchParams.get("resource");
498
+ const resource = resourceValue ? parseResource(resourceValue, input.resources) : undefined;
499
+ if (resourceValue && !resource)
500
+ return;
501
+ const sectionValue = url.searchParams.get("section");
502
+ const section = sectionValue ? parseSection(sectionValue) : undefined;
503
+ if (sectionValue && !section)
504
+ return;
505
+ return {
506
+ pageId: page.id,
507
+ ...resource ? { resource } : {},
508
+ ...section ? { section } : {}
509
+ };
510
+ };
511
+ var serializePageUrl = (input) => serializeWorkbenchPageUrl({
512
+ projectId: input.projectId,
513
+ location: { page: input.page.ref, resource: input.resource },
514
+ pages: [input.page],
515
+ resources: defaultPageResourceCodec
516
+ });
517
+ var parsePageUrl = (input) => {
518
+ if (!input.url.startsWith("/") || input.url.startsWith("//"))
519
+ return;
520
+ return parseWorkbenchPageUrl({ ...input, resources: defaultPageResourceCodec });
521
+ };
343
522
  // src/extensions/params.ts
344
523
  var params = {
345
524
  text: (options) => ({ type: "text", ...options }),
@@ -454,6 +633,19 @@ var createTerminalSessionBridge = (host) => ({
454
633
  // src/extensions/webview-capabilities.ts
455
634
  var artifactMountId = (mount) => typeof mount === "string" ? mount : mount.id;
456
635
  var artifactsRead = (mount) => `artifacts.read:${artifactMountId(mount)}`;
636
+ // src/extensions/webview-events.ts
637
+ var EXTENSION_EVENTS_SCOPE = "extension.events";
638
+ var createWebviewEventsClient = (host, extensionId) => ({
639
+ subscribe(event, listener) {
640
+ const id = resolveEventReferenceId(event, extensionId);
641
+ return host.onEvent(EXTENSION_EVENTS_SCOPE, (payload) => {
642
+ const notification = payload;
643
+ if (notification.type === "reset" || notification.type === "changed" && notification.id === id)
644
+ listener();
645
+ });
646
+ }
647
+ });
648
+
457
649
  // src/extensions/webview-client.ts
458
650
  var createWebviewClient = (host, options) => {
459
651
  const extensionId = options?.extensionId ?? host.extensionId;
@@ -490,7 +682,8 @@ var createWebviewClient = (host, options) => {
490
682
  readText: (mount, path) => host.call("artifacts.read", { op: "readText", mount: artifactMountId(mount), path }),
491
683
  imageUrl: (mount, path) => host.call("artifacts.read", { op: "imageUrl", mount: artifactMountId(mount), path })
492
684
  };
493
- return { artifacts, commands, settings };
685
+ const events = createWebviewEventsClient(host, extensionId);
686
+ return { artifacts, commands, events, settings };
494
687
  };
495
688
  // src/extensions/when.ts
496
689
  var matchesResourceWhen = (when, resourceType) => {
@@ -515,17 +708,23 @@ export {
515
708
  unwrapCommandOutcome,
516
709
  sessionSlots,
517
710
  sessionEvents,
711
+ serializeWorkbenchPageUrl,
712
+ serializePageUrl,
518
713
  resourceMenuSlotRef2 as resourceMenuSlotRef,
519
714
  resourceKey,
715
+ resolveEventReferenceId,
520
716
  qualifyRef,
521
717
  projectSlots,
522
718
  projectEvents,
719
+ parseWorkbenchPageUrl,
720
+ parsePageUrl,
523
721
  params,
524
722
  packageAsset,
525
723
  matchesResourceWhen,
526
724
  localContributionIdPattern,
527
725
  localContributionIdGrammar,
528
726
  l10n,
727
+ isWorkbenchProjectUrl,
529
728
  isValidLocalContributionId,
530
729
  isLocalizedString,
531
730
  gitEvents,
@@ -564,6 +763,7 @@ export {
564
763
  defineCommand,
565
764
  defineArtifactMount,
566
765
  defineActivityItem,
766
+ defaultPageResourceCodec,
567
767
  createWebviewClient,
568
768
  createTerminalSessionBridge,
569
769
  commandRef,
@@ -574,6 +774,8 @@ export {
574
774
  WEBVIEW_HOST_CAPABILITY_VERSION,
575
775
  WEBVIEW_HOST_CAPABILITIES,
576
776
  WEBVIEW_DECLARABLE_CAPABILITIES,
777
+ SDK_VERSION,
778
+ EXTENSION_EVENTS_SCOPE,
577
779
  EXTENSION_API_VERSION,
578
780
  ALWAYS_AVAILABLE_WEBVIEW_CAPABILITIES
579
781
  };
@@ -680,6 +680,9 @@ export declare interface DateControl extends BaseControl {
680
680
  max?: string;
681
681
  }
682
682
 
683
+ /** Resource URI conversion belongs to route and persistence adapters. */
684
+ export declare const defaultPageResourceCodec: PageResourceCodec;
685
+
683
686
  export declare const defineActivityItem: <Definition extends Omit<ActivityItemContribution, "ref">>(definition: Definition) => Definition & ContributionDefinition<"activity-item">;
684
687
 
685
688
  export declare const defineArtifactMount: <Definition extends Omit<ArtifactMountContribution, "ref">>(definition: Definition) => Definition & ContributionDefinition<"artifact-mount">;
@@ -815,7 +818,7 @@ export declare interface EventRef<TPayload extends Struct = Struct> {
815
818
  /** Build a typed event reference from separate ownership and local identity. */
816
819
  export declare const eventRef: <TPayload extends Struct = Struct>(input: ExternalRefInput) => EventRef<TPayload>;
817
820
 
818
- declare type ExactOptions<Definition, Contract> = unknown extends Contract ? Definition : [Contract] extends [Definition] ? Definition : Contract extends unknown ? Definition extends Contract ? Contract extends EventRef ? Definition : Definition extends (...args: never[]) => unknown ? Definition : Definition extends readonly unknown[] ? Contract extends readonly (infer Item)[] ? {
821
+ declare type ExactOptions<Definition, Contract> = unknown extends Contract ? Definition : [Contract] extends [Definition] ? Definition : Contract extends unknown ? Definition extends Contract ? Contract extends EventRef | CommandRef ? Definition : Definition extends (...args: never[]) => unknown ? Definition : Definition extends readonly unknown[] ? Contract extends readonly (infer Item)[] ? {
819
822
  [Index in keyof Definition]: ExactOptions<Definition[Index], Item>;
820
823
  } : never : Definition extends object ? {
821
824
  [Key in keyof Definition]: Key extends keyof Contract ? ExactOptions<Definition[Key], Contract[Key]> : never;
@@ -824,6 +827,8 @@ declare type ExactOptions<Definition, Contract> = unknown extends Contract ? Def
824
827
  /** Current host extension API version. `engines.pstdio` in package.json is a semver range checked against this. */
825
828
  export declare const EXTENSION_API_VERSION = "1.0.0-alpha.10";
826
829
 
830
+ export declare const EXTENSION_EVENTS_SCOPE = "extension.events";
831
+
827
832
  export declare interface ExtensionActivityApi {
828
833
  record(input: {
829
834
  message: string;
@@ -1028,6 +1033,11 @@ export declare const extensionPanelRegions: readonly ["main", "secondary", "side
1028
1033
  export declare interface ExtensionProcessApi {
1029
1034
  run(input: ProcessRunInput): Promise<ProcessRunResult>;
1030
1035
  runOrThrow(input: ProcessRunInput): Promise<ProcessRunResult>;
1036
+ /**
1037
+ * Starts an independent process with no attached standard streams.
1038
+ * It survives runtime and desktop shutdown and does not delay host exit.
1039
+ * The extension owns the child's lifetime and must arrange its cleanup.
1040
+ */
1031
1041
  spawnDetached(input: {
1032
1042
  command: string[];
1033
1043
  cwd?: string;
@@ -1470,6 +1480,8 @@ export declare const isLocalizedString: (value: unknown) => value is LocalizedSt
1470
1480
 
1471
1481
  export declare const isValidLocalContributionId: (id: string) => boolean;
1472
1482
 
1483
+ export declare const isWorkbenchProjectUrl: (urlValue: string, projectId: string) => boolean;
1484
+
1473
1485
  export declare type JsonObject = {
1474
1486
  [key: string]: JsonValue;
1475
1487
  };
@@ -2028,6 +2040,12 @@ declare type PagePanelRefs<Slots extends readonly PageSlot[]> = {
2028
2040
 
2029
2041
  export declare type PageRef = ContributionRef<"page">;
2030
2042
 
2043
+ declare interface PageResourceCodec {
2044
+ normalize(resource: ResourceRef): ResourceRef;
2045
+ toUri(resource: ResourceRef): string;
2046
+ fromUri(uri: string): ResourceRef | undefined;
2047
+ }
2048
+
2031
2049
  /** A page-owned panel. Main presentation and routed context belong to the page. */
2032
2050
  export declare interface PageSlot extends PlacementPresentation {
2033
2051
  readonly id: string;
@@ -2049,6 +2067,12 @@ export declare type PageSlotRegion = ExtensionPanelRegion;
2049
2067
 
2050
2068
  export declare type PageSlotRole = "primary" | "auxiliary";
2051
2069
 
2070
+ export declare interface PageUrlDefinition {
2071
+ id: string;
2072
+ ref: PageRef;
2073
+ path: string;
2074
+ }
2075
+
2052
2076
  export declare type PanelRef = PlacementRef | PageSlotRef;
2053
2077
 
2054
2078
  declare type ParamBase<TValue, TRequired extends boolean | undefined = boolean | undefined> = {
@@ -2134,6 +2158,26 @@ export declare type ParamType = "text" | "longtext" | "markdown" | "number" | "b
2134
2158
 
2135
2159
  export declare type ParamValue<TDescriptor extends ParamDescriptor> = TDescriptor extends ParamDescriptor<infer V> ? V : never;
2136
2160
 
2161
+ export declare interface ParsedWorkbenchPageUrl {
2162
+ pageId: string;
2163
+ resource?: ResourceRef;
2164
+ section?: FileRendererSectionTarget;
2165
+ }
2166
+
2167
+ /** Resolve a dashboard URL against the caller's declared pages and project. */
2168
+ export declare const parsePageUrl: (input: {
2169
+ url: string;
2170
+ projectId: string;
2171
+ pages: readonly PageUrlDefinition[];
2172
+ }) => ParsedWorkbenchPageUrl | undefined;
2173
+
2174
+ export declare const parseWorkbenchPageUrl: (input: {
2175
+ url: string;
2176
+ projectId: string;
2177
+ pages: readonly PageUrlDefinition[];
2178
+ resources: PageResourceCodec;
2179
+ }) => ParsedWorkbenchPageUrl | undefined;
2180
+
2137
2181
  export declare type PatchPart = {
2138
2182
  type: "patch";
2139
2183
  hash?: string;
@@ -2395,6 +2439,9 @@ declare type RequiredParamKeys<TSchema extends ParamObjectSchema> = {
2395
2439
  } ? K : never;
2396
2440
  }[keyof TSchema];
2397
2441
 
2442
+ /** Resolve an event ref using the same ownership rules as commands and hooks. */
2443
+ export declare const resolveEventReferenceId: (ref: RendererEventReference, ownerExtensionId?: string) => string;
2444
+
2398
2445
  export declare interface ResourceAnchor extends ResourceRef {
2399
2446
  role?: ResourceRole;
2400
2447
  }
@@ -2507,6 +2554,8 @@ export declare type ScheduleExpression = string;
2507
2554
 
2508
2555
  declare type SchemaParams<TSchema extends ParamObjectSchema | undefined> = TSchema extends ParamObjectSchema ? ParamsOf<TSchema> : Record<string, never>;
2509
2556
 
2557
+ export declare const SDK_VERSION: string;
2558
+
2510
2559
  /** Mutually-exclusive mode control rendered as a segmented button group. */
2511
2560
  export declare interface SegmentedControl extends BaseControl {
2512
2561
  type: "segmented";
@@ -2576,6 +2625,20 @@ export declare interface SerializedError {
2576
2625
  cause?: JsonValue;
2577
2626
  }
2578
2627
 
2628
+ /** Serialize a project page using the same resource encoding as the dashboard. */
2629
+ export declare const serializePageUrl: (input: {
2630
+ projectId: string;
2631
+ page: PageUrlDefinition;
2632
+ resource?: ResourceRef;
2633
+ }) => string;
2634
+
2635
+ export declare const serializeWorkbenchPageUrl: (input: {
2636
+ projectId: string;
2637
+ location: PageLocation;
2638
+ pages: readonly PageUrlDefinition[];
2639
+ resources: PageResourceCodec;
2640
+ }) => string;
2641
+
2579
2642
  declare type SessionAttachmentRef = z.infer<typeof sessionAttachmentRefSchema>;
2580
2643
 
2581
2644
  declare const sessionAttachmentRefSchema: z.ZodObject<{
@@ -3214,6 +3277,7 @@ export declare type WebviewCapabilityDeclaration = WebviewDeclarableCapability |
3214
3277
  export declare type WebviewClient<TCommands, TSettings = undefined> = {
3215
3278
  artifacts: WebviewArtifactsClient;
3216
3279
  commands: WebviewCommandsClient<TCommands>;
3280
+ events: WebviewEventsClient;
3217
3281
  settings: WebviewSettingsClient<ClientSettingsMap<TSettings>>;
3218
3282
  };
3219
3283
 
@@ -3242,6 +3306,18 @@ export declare interface WebviewContribution {
3242
3306
 
3243
3307
  export declare type WebviewDeclarableCapability = (typeof WEBVIEW_DECLARABLE_CAPABILITIES)[number];
3244
3308
 
3309
+ export declare interface WebviewEventsClient {
3310
+ /** Refetch on a matching event or host sync reconnect. Returns the unsubscribe function. */
3311
+ subscribe(event: RendererEventReference, listener: () => void): () => void;
3312
+ }
3313
+
3314
+ export declare type WebviewExtensionEvent = {
3315
+ type: "changed";
3316
+ id: string;
3317
+ } | {
3318
+ type: "reset";
3319
+ };
3320
+
3245
3321
  export declare interface WebviewExtensionSettingKeyParams {
3246
3322
  key: string;
3247
3323
  }
package/dist/hooks.d.ts CHANGED
@@ -1465,6 +1465,7 @@ declare type SseEvent = {
1465
1465
  };
1466
1466
 
1467
1467
  declare type StartSyncInput = SyncWriterProvider & {
1468
+ /** Called after the initial snapshot, or when a cursor-based connection resumes. */
1468
1469
  onConnected?: () => void;
1469
1470
  onDisconnected?: () => void;
1470
1471
  onConnectionLost?: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pstdio/sdk",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/pufflyai/prompt-studio"