@dev.fast/whiteboard 0.4.0 → 0.4.1-preview.20261026.138

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.
Files changed (78) hide show
  1. package/dist/{account-alias-2yTAxoBx.js → account-alias-BPlPnzQM.js} +7 -7
  2. package/dist/{agent-cli-CHNDd4Ga.js → agent-cli-BbCf9uid.js} +11 -11
  3. package/dist/agent-cli-ipN_JmzG.js +2 -0
  4. package/dist/{agent-client-Dc0vHFJt.js → agent-client-DfpM8F5N.js} +3 -3
  5. package/dist/{authoring-tools-D8EkIDjm.js → authoring-tools-bm39DRoV.js} +23 -10
  6. package/dist/{background-server-DyWnPJyZ.js → background-server--0EeCCeV.js} +1 -1
  7. package/dist/{background-server-DHWHzpo5.js → background-server-B--5p72o.js} +6 -6
  8. package/dist/build-info.json +1 -1
  9. package/dist/{cli-qZomwJmK.js → cli-DBTQIroM.js} +2 -2
  10. package/dist/{cli-runner-DFBaVGij.js → cli-runner-CRFCdb7G.js} +25 -25
  11. package/dist/cli.js +3 -3
  12. package/dist/{fuzzy-match-CfSxqzBF.js → fuzzy-match-l3mJZ1Ge.js} +5 -5
  13. package/dist/{headless-host-dwlqhqMY.js → headless-host-5ei3nHS8.js} +1 -1
  14. package/dist/{headless-host-DJhcab6r.js → headless-host-BkJ9OUQY.js} +131 -299
  15. package/dist/{client-p9WB8Sj0.js → import-Bwvf9Wos.js} +505 -390
  16. package/dist/{lens-selection-9byzEgvU.js → lens-selection-EuZdag4Q.js} +1 -1
  17. package/dist/{local-data-DHedfhxw.js → local-data-BCSeTyic.js} +98 -154
  18. package/dist/{mcp-Br-r_wwR.js → mcp-_Xg1gQuA.js} +17 -8
  19. package/dist/{package-paths-BeYHYjeR.js → package-paths-Bg731FHS.js} +2 -2
  20. package/dist/profile-C8zL4u3L.js +2 -0
  21. package/dist/{profile-D9wdlPgw.js → profile-xzrUFgbT.js} +4 -4
  22. package/dist/{remote-attach-DSGpTOJ4.js → remote-attach-DF25Epoa.js} +4 -4
  23. package/dist/{remote-extensions-WtJO7pkb.js → remote-extensions-BJamgsCe.js} +1 -1
  24. package/dist/{remote-extensions-CsI_BJ_f.js → remote-extensions-CeywZL-Z.js} +4 -4
  25. package/dist/{remote-language-server-Dx13Mg2m.js → remote-language-server-CH8sG_CU.js} +6 -6
  26. package/dist/{remote-uninstall-Dp2o3qz0.js → remote-uninstall-Cq-lkBY7.js} +7 -7
  27. package/dist/{request-origin-CNSLVn7x.js → request-origin-CargJFvH.js} +14 -3
  28. package/dist/{review-app-launcher-BZAnR8Vp.js → review-app-launcher-D8ApNDUV.js} +3 -3
  29. package/dist/{review-home-paths-CG-sb9Cw.js → review-home-paths-DcB4xLD-.js} +2 -2
  30. package/dist/{review-telemetry-BM-_KdLi.js → review-telemetry-BW7gJrSo.js} +7 -7
  31. package/dist/{runtime-C6_1K-3Z.js → runtime-DY6Ctyog.js} +1 -1
  32. package/dist/runtime.js +1 -1
  33. package/dist/{s3-CsG2EWED.js → s3-BIWv4h1J.js} +2 -2
  34. package/dist/{s3-config-zz4GWh2T.js → s3-config-Du3RBIvv.js} +1 -1
  35. package/dist/server/desktop-host.js +51 -17
  36. package/dist/{server-discovery-DKLIOgg3.js → server-discovery-aHK_1r3U.js} +5 -5
  37. package/dist/sharing/index.d.ts +209 -98
  38. package/dist/sharing/index.js +1 -1
  39. package/dist/{src-DYHVzX3b.js → src-DEOc8bvC.js} +63 -2
  40. package/dist/{src-Z1x4-Qmb.js → src-DjRaCctk.js} +6 -6
  41. package/dist/{stored-document-migration-DEt8iwem.js → stored-document-migration-CwmTwTb8.js} +139 -13
  42. package/dist/{thread-state-CO97FUqK.js → thread-state-CbLB0jUA.js} +1 -1
  43. package/dist/{tool-failure-C1L1vJyz.js → tool-failure-DZaQzi4f.js} +2 -2
  44. package/instructions/animation.md +345 -0
  45. package/instructions/authoring.md +2 -2
  46. package/package.json +6 -6
  47. package/src/fixtures/blocks/animation.json +19 -0
  48. package/src/review-api/authoring-tools.ts +17 -6
  49. package/src/review-api/blocks/animation.ts +98 -0
  50. package/src/review-api/blocks/index.ts +3 -0
  51. package/src/review-api/blocks/sequence.ts +3 -1
  52. package/src/review-api/comparison-coverage.ts +120 -89
  53. package/src/review-api/component-reference.ts +16 -0
  54. package/src/review-api/document-text.ts +11 -0
  55. package/src/review-api/document.ts +26 -5
  56. package/src/review-api/http.ts +31 -2
  57. package/src/review-api/instructions.ts +11 -2
  58. package/src/review-api/local-data.ts +21 -4
  59. package/src/review-api/mcp.ts +22 -14
  60. package/src/review-api/onboarding.ts +100 -26
  61. package/src/review-preferences.ts +43 -0
  62. package/src/server/desktop-server.ts +24 -0
  63. package/src/server/json-review-reporting.ts +1 -1
  64. package/src/server/review-server-core.ts +2 -0
  65. package/src/server/structural-comparisons.ts +5 -11
  66. package/src/server/structural-diff.ts +8 -2
  67. package/src/sharing/cli.ts +1 -1
  68. package/src/sharing/client.ts +54 -222
  69. package/src/sharing/export.ts +172 -114
  70. package/src/sharing/host.ts +32 -36
  71. package/src/sharing/import.ts +93 -147
  72. package/src/sharing/index.ts +1 -6
  73. package/src/sharing/routes.ts +18 -17
  74. package/src/ui-telemetry-events.ts +11 -0
  75. package/dist/agent-cli-Cn4-16XA.js +0 -2
  76. package/dist/profile-BopGMZUh.js +0 -2
  77. package/dist/{s3-config-CjcLyFV5.js → s3-config-D7Yt0VhS.js} +1 -1
  78. package/dist/{src-X9ZebeOD.js → src-DXnn7RrI.js} +1 -1
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { existsSync } from "node:fs";
2
3
  import {
3
4
  mkdir,
@@ -12,17 +13,12 @@ import {
12
13
  } from "node:fs/promises";
13
14
  import path from "node:path";
14
15
 
15
- import { parseJsonText } from "@dev.fast/json";
16
+ import { type JsonObject, type JsonValue } from "@dev.fast/review-protocol";
16
17
  import {
17
- type JsonObject,
18
- type JsonValue,
19
- isJsonObject,
20
- } from "@dev.fast/review-protocol";
21
- import {
22
- MAX_SHARE_MANIFEST_BYTES,
23
- type ShareManifest,
24
- shareIdSchema,
25
- shareManifestSchema,
18
+ MAX_SHARE_BYTES,
19
+ type ShareBundle,
20
+ capabilitySchema,
21
+ shareBundleSchema,
26
22
  } from "@dev.fast/review-share-protocol";
27
23
  import { textIncludesQuote } from "@review/evidence.js";
28
24
  import { lensSchema } from "@review/review-api/diff-lenses.js";
@@ -50,12 +46,7 @@ import {
50
46
  } from "@review/stored-document-migration.js";
51
47
  import { z } from "zod";
52
48
 
53
- import {
54
- type ShareBundle,
55
- digestBytes,
56
- referenceCommits,
57
- validateShareSources,
58
- } from "./export.js";
49
+ import { referenceCommits, validateShareSources } from "./export.js";
59
50
  import {
60
51
  COMMIT_ID,
61
52
  assertCommitIds,
@@ -132,59 +123,48 @@ export const sharePresentationSchema = z.strictObject({
132
123
  maps: z.record(z.string(), mapSchema),
133
124
  });
134
125
 
135
- export function validateShareBundle(bundle: ShareBundle) {
136
- const manifest = shareManifestSchema.parse(bundle.manifest);
137
-
138
- if (Buffer.byteLength(JSON.stringify(manifest)) > MAX_SHARE_MANIFEST_BYTES)
139
- throw new Error("Share manifest is too large.");
140
-
141
- if (bundle.objects.size !== manifest.objects.length)
142
- throw new Error("Share object count does not match its manifest.");
143
-
144
- for (const object of manifest.objects) {
145
- const bytes = bundle.objects.get(object.id);
146
-
147
- if (
148
- !bytes ||
149
- bytes.byteLength !== object.size ||
150
- digestBytes(bytes) !== object.sha256
151
- )
152
- throw new Error(`Shared object ${object.id} is missing or corrupt.`);
153
- }
154
-
155
- const json = (id: string) =>
156
- parseJsonText(Buffer.from(bundle.objects.get(id)!).toString());
126
+ export interface ShareAttribution {
127
+ login: string;
128
+ sharedAt: number;
129
+ }
157
130
 
158
- const stored = json(manifest.snapshot);
131
+ export function validateShareBundle(input: ShareBundle) {
132
+ const bundle = shareBundleSchema.parse(input);
133
+ // SAFETY: a bundle is parsed from JSON, so its snapshot is a JSON object.
134
+ const stored = bundle.snapshot as JsonObject;
159
135
 
160
- // Bundles shared before a field was retired still open; the bytes stay
161
- // sealed and only the parsed document drops the retired form. Lenses
162
- // shared as document blocks read as the snapshot's lenses.
136
+ // Bundles shared before a field was retired still open; only the parsed
137
+ // document drops the retired form. Lenses shared as document blocks read
138
+ // as the snapshot's lenses.
163
139
  const snapshot = sharedSnapshotSchema.parse(
164
- isJsonObject(stored) && "document" in stored
165
- ? liftSharedLenses(stored, migrateStoredDocument(stored.document))
166
- : stored,
140
+ stored.document === undefined
141
+ ? stored
142
+ : liftSharedLenses(stored, migrateStoredDocument(stored.document)),
167
143
  );
168
144
 
169
145
  if (
170
- snapshot.reviewId !== manifest.reviewId ||
171
- snapshot.version !== manifest.version ||
172
- snapshot.title !== manifest.title
146
+ snapshot.reviewId !== bundle.reviewId ||
147
+ snapshot.version !== bundle.version ||
148
+ snapshot.title !== bundle.title
173
149
  )
174
- throw new Error("Shared snapshot does not match its manifest.");
150
+ throw new Error("Shared snapshot does not match its bundle.");
175
151
  checkReferences(snapshot.document);
176
152
  validateShareSources(snapshot);
177
153
  // Every commit it quotes reaches git as an argument.
178
154
  assertCommitIds(referenceCommits(snapshot));
179
155
 
180
- if (snapshot.pins && !manifest.repository)
181
- throw new Error("Shared source pins require a repository.");
156
+ if (
157
+ (snapshot.pins || referenceCommits(snapshot).length) &&
158
+ !bundle.repository
159
+ )
160
+ throw new ReviewInputError(
161
+ "This share can only be viewed in the browser.",
162
+ 409,
163
+ );
182
164
 
183
- const presentation = sharePresentationSchema.parse(
184
- json(manifest.presentation),
185
- );
165
+ const maps = z.record(z.string(), mapSchema).parse(bundle.maps);
186
166
 
187
- if (!snapshot.pins && Object.keys(presentation.maps).length)
167
+ if (!snapshot.pins && Object.keys(maps).length)
188
168
  throw new Error("Shared maps require source pins.");
189
169
 
190
170
  const traces = new Map<
@@ -193,25 +173,18 @@ export function validateShareBundle(bundle: ShareBundle) {
193
173
  >();
194
174
 
195
175
  for (const block of resourceReferences(snapshot.document)) {
196
- const reference = resourceReference(block);
197
-
198
- const resource = manifest.resources.find(
199
- (item) => item.id === reference?.id,
200
- );
201
-
202
- if (!reference || !resource || resource.kind !== reference.kind)
203
- throw new Error("Missing or mismatched shared resource.");
204
-
205
176
  if (block.type === "trace_quote") {
206
- let events = traces.get(resource.object);
177
+ let events = traces.get(block.traceId);
207
178
 
208
179
  if (!events) {
209
- const trace = traceSchema.parse(json(resource.object));
180
+ if (!Object.hasOwn(bundle.traces, block.traceId))
181
+ throw new Error("Missing shared trace.");
182
+ const trace = traceSchema.parse(bundle.traces[block.traceId]);
210
183
  events = new Map(trace.events.map((event) => [event.id, event]));
211
184
 
212
185
  if (events.size !== trace.events.length)
213
186
  throw new Error("Duplicate shared trace event.");
214
- traces.set(resource.object, events);
187
+ traces.set(block.traceId, events);
215
188
  }
216
189
 
217
190
  const event = events.get(block.eventId);
@@ -221,14 +194,16 @@ export function validateShareBundle(bundle: ShareBundle) {
221
194
  }
222
195
 
223
196
  if (block.type === "software_map") {
224
- const map = presentation.maps[block.mapVersionId];
197
+ const map = Object.hasOwn(maps, block.mapVersionId)
198
+ ? maps[block.mapVersionId]
199
+ : undefined;
225
200
 
226
201
  if (!map || map.commit !== snapshot.pins?.[map.side])
227
202
  throw new Error("Shared map pins do not match.");
228
203
  }
229
204
  }
230
205
 
231
- return { manifest, snapshot, presentation };
206
+ return { bundle, snapshot, maps };
232
207
  }
233
208
 
234
209
  function liftSharedLenses(stored: JsonObject, migrated: JsonValue): JsonObject {
@@ -246,16 +221,21 @@ function liftSharedLenses(stored: JsonObject, migrated: JsonValue): JsonObject {
246
221
  };
247
222
  }
248
223
 
249
- export function sharedReviewId(origin: string, shareId: string) {
224
+ export function sharedReviewId(origin: string, capability: string) {
250
225
  const url = new URL(origin);
251
- shareIdSchema.parse(shareId);
226
+ capabilitySchema.parse(capability);
252
227
 
253
- return `shared-${digestBytes(Buffer.from(JSON.stringify([url.origin, shareId])))}`;
228
+ return `shared-${createHash("sha256")
229
+ .update(JSON.stringify([url.origin, capability]))
230
+ .digest("hex")}`;
254
231
  }
255
232
 
256
233
  /** Separate immutable directory tree; authoring mutation handlers cannot write it. */
257
234
  export class SharedReviewStore {
258
- private readonly loaded = new Map<string, Omit<ShareBundle, "objects">>();
235
+ private readonly loaded = new Map<
236
+ string,
237
+ { attribution?: ShareAttribution }
238
+ >();
259
239
  private readonly validated = new Map<
260
240
  string,
261
241
  ReturnType<typeof validateShareBundle>
@@ -383,7 +363,7 @@ export class SharedReviewStore {
383
363
  throw new ReviewInputError("Shared review is not available.", 404);
384
364
 
385
365
  if (!saved.snapshot.pins) {
386
- await this.prepareUnpinned(id, saved.manifest.repository?.cloneUrl);
366
+ await this.prepareUnpinned(id, saved.bundle.repository?.cloneUrl);
387
367
 
388
368
  return;
389
369
  }
@@ -405,7 +385,7 @@ export class SharedReviewStore {
405
385
  await rm(root, { recursive: true, force: true });
406
386
  await this.fetchRepository(
407
387
  root,
408
- saved.manifest.repository!.cloneUrl,
388
+ saved.bundle.repository!.cloneUrl,
409
389
  saved.snapshot.pins,
410
390
  false,
411
391
  commits,
@@ -481,7 +461,7 @@ export class SharedReviewStore {
481
461
  add({ ...anchorPins(source, pins), repositoryId }, source);
482
462
 
483
463
  if (pins)
484
- for (const map of Object.values(saved.presentation.maps))
464
+ for (const map of Object.values(saved.maps))
485
465
  for (const element of map.elements)
486
466
  for (const range of element.sourceRanges ?? [])
487
467
  add(pins, { ...range, side: map.side });
@@ -588,6 +568,13 @@ export class SharedReviewStore {
588
568
 
589
569
  for (const id of await readdir(this.root)) {
590
570
  if (!/^shared-[a-f0-9]{64}$/.test(id)) continue;
571
+
572
+ // Earlier share formats are not read; reopening the link imports again.
573
+ if (!existsSync(path.join(this.root, id, "bundle.json"))) {
574
+ await rm(path.join(this.root, id), { recursive: true, force: true });
575
+ continue;
576
+ }
577
+
591
578
  let saved: Awaited<ReturnType<SharedReviewStore["readValidated"]>>;
592
579
 
593
580
  try {
@@ -603,11 +590,8 @@ export class SharedReviewStore {
603
590
  continue;
604
591
  }
605
592
 
606
- const { bundle, validated } = saved;
607
- this.loaded.set(id, {
608
- manifest: bundle.manifest,
609
- attribution: bundle.attribution,
610
- });
593
+ const { attribution, validated } = saved;
594
+ this.loaded.set(id, { attribution });
611
595
  this.validated.set(id, validated);
612
596
 
613
597
  let prepared = false;
@@ -641,7 +625,7 @@ export class SharedReviewStore {
641
625
  }
642
626
 
643
627
  if (!validated.snapshot.pins) {
644
- if (this.repositories.has(id) || !validated.manifest.repository)
628
+ if (this.repositories.has(id) || !validated.bundle.repository)
645
629
  this.setStatus(id, "ready");
646
630
  else void this.prepare(id).catch(() => {});
647
631
  } else if (
@@ -719,9 +703,9 @@ export class SharedReviewStore {
719
703
  }
720
704
 
721
705
  get(id: string) {
722
- const bundle = this.loaded.get(id);
706
+ const loaded = this.loaded.get(id);
723
707
 
724
- if (!bundle)
708
+ if (!loaded)
725
709
  throw new ReviewInputError("Shared review is not available.", 404);
726
710
  const validated = this.validated.get(id)!;
727
711
  const repositoryId = this.repositories.get(id);
@@ -757,8 +741,8 @@ export class SharedReviewStore {
757
741
  ? { kind: "project" as const, repositoryId }
758
742
  : undefined,
759
743
  shared: {
760
- ...bundle.attribution,
761
- cloneUrl: bundle.manifest.repository?.cloneUrl,
744
+ ...loaded.attribution,
745
+ cloneUrl: validated.bundle.repository?.cloneUrl,
762
746
  },
763
747
  },
764
748
  };
@@ -795,25 +779,6 @@ export class SharedReviewStore {
795
779
  return this.local?.store.withDiffStats(reviews) ?? reviews;
796
780
  }
797
781
 
798
- async readObject(id: string, objectId: string) {
799
- const entry = this.get(id).manifest.objects.find(
800
- (object) => object.id === objectId,
801
- );
802
-
803
- if (!entry)
804
- throw new ReviewInputError("Shared object is unavailable.", 404);
805
- const file = path.join(this.root, id, objectId);
806
-
807
- if ((await stat(file)).size !== entry.size)
808
- throw new Error("Corrupt cached shared object.");
809
- const bytes = await readFile(file);
810
-
811
- if (digestBytes(bytes) !== entry.sha256)
812
- throw new Error("Corrupt cached shared object.");
813
-
814
- return bytes;
815
- }
816
-
817
782
  async removeLocal(id: string) {
818
783
  await this.jobs.get(id)?.catch(() => {});
819
784
 
@@ -893,30 +858,32 @@ export class SharedReviewStore {
893
858
  for (const listener of this.listeners) listener();
894
859
  }
895
860
 
896
- async import(origin: string, shareId: string, bundle: ShareBundle) {
861
+ async import(
862
+ origin: string,
863
+ capability: string,
864
+ bundle: ShareBundle,
865
+ attribution?: ShareAttribution,
866
+ ) {
897
867
  const validated = validateShareBundle(bundle);
898
- const id = sharedReviewId(origin, shareId);
868
+ const id = sharedReviewId(origin, capability);
899
869
  this.assertWritable(id);
900
870
  await mkdir(this.root, { recursive: true, mode: 0o700 });
901
871
  const staging = await mkdtemp(path.join(this.root, ".import-"));
902
872
 
903
873
  try {
904
874
  await writeFile(
905
- path.join(staging, "manifest.json"),
906
- JSON.stringify(bundle.manifest),
875
+ path.join(staging, "bundle.json"),
876
+ JSON.stringify(validated.bundle),
907
877
  { mode: 0o600 },
908
878
  );
909
879
 
910
- if (bundle.attribution)
880
+ if (attribution)
911
881
  await writeFile(
912
882
  path.join(staging, "attribution.json"),
913
- JSON.stringify(bundle.attribution),
883
+ JSON.stringify(attribution),
914
884
  { mode: 0o600 },
915
885
  );
916
886
 
917
- for (const [objectId, bytes] of bundle.objects)
918
- await writeFile(path.join(staging, objectId), bytes, { mode: 0o600 });
919
-
920
887
  try {
921
888
  await rename(staging, path.join(this.root, id));
922
889
  } catch (error) {
@@ -930,19 +897,14 @@ export class SharedReviewStore {
930
897
  throw error;
931
898
  const existing = await this.read(id);
932
899
 
933
- if (
934
- JSON.stringify(existing.manifest) !== JSON.stringify(bundle.manifest)
935
- )
936
- throw new Error("An immutable share changed its manifest.");
900
+ if (JSON.stringify(existing) !== JSON.stringify(validated.bundle))
901
+ throw new Error("An immutable share changed its bundle.");
937
902
  }
938
903
  } finally {
939
904
  await rm(staging, { recursive: true, force: true });
940
905
  }
941
906
 
942
- this.loaded.set(id, {
943
- manifest: bundle.manifest,
944
- attribution: bundle.attribution,
945
- });
907
+ this.loaded.set(id, { attribution });
946
908
  this.validated.set(id, validated);
947
909
 
948
910
  for (const listener of this.listeners) listener();
@@ -953,35 +915,19 @@ export class SharedReviewStore {
953
915
  }
954
916
 
955
917
  async read(id: string): Promise<ShareBundle> {
956
- return (await this.readValidated(id)).bundle;
918
+ return (await this.readValidated(id)).validated.bundle;
957
919
  }
958
920
 
959
921
  private async readValidated(id: string) {
960
922
  if (!/^shared-[a-f0-9]{64}$/.test(id))
961
923
  throw new Error("Invalid shared review ID.");
962
924
  const dir = path.join(this.root, id);
925
+ const file = path.join(dir, "bundle.json");
963
926
 
964
- if (
965
- (await stat(path.join(dir, "manifest.json"))).size >
966
- MAX_SHARE_MANIFEST_BYTES
967
- )
968
- throw new Error("Shared manifest is too large.");
969
-
970
- const manifest: ShareManifest = shareManifestSchema.parse(
971
- JSON.parse(await readFile(path.join(dir, "manifest.json"), "utf8")),
972
- );
973
-
974
- const objects = new Map<string, Uint8Array>();
975
-
976
- for (const object of manifest.objects) {
977
- const file = path.join(dir, object.id);
927
+ if ((await stat(file)).size > MAX_SHARE_BYTES)
928
+ throw new Error("Shared bundle is too large.");
978
929
 
979
- if ((await stat(file)).size !== object.size)
980
- throw new Error("Corrupt cached shared object.");
981
- objects.set(object.id, await readFile(file));
982
- }
983
-
984
- let attribution: ShareBundle["attribution"];
930
+ let attribution: ShareAttribution | undefined;
985
931
 
986
932
  try {
987
933
  attribution = z
@@ -995,9 +941,9 @@ export class SharedReviewStore {
995
941
  /* Local-only fixtures have no hosted sender. */
996
942
  }
997
943
 
998
- const bundle = { manifest, objects, attribution };
999
- const validated = validateShareBundle(bundle);
1000
-
1001
- return { bundle, validated };
944
+ return {
945
+ attribution,
946
+ validated: validateShareBundle(JSON.parse(await readFile(file, "utf8"))),
947
+ };
1002
948
  }
1003
949
  }
@@ -1,11 +1,6 @@
1
1
  export { ShareClient } from "./client.js";
2
2
 
3
- export {
4
- exportShare,
5
- type ShareBundle,
6
- type ShareExportData,
7
- type ShareExportStore,
8
- } from "./export.js";
3
+ export { exportShare } from "./export.js";
9
4
 
10
5
  export {
11
6
  SharedReviewStore,
@@ -7,28 +7,29 @@ export class SharedReviewData {
7
7
  constructor(private readonly store: SharedReviewStore) {}
8
8
 
9
9
  map(id: string, resourceId: string) {
10
- const map = this.store.get(id).presentation.maps[resourceId];
10
+ const { maps } = this.store.get(id);
11
11
 
12
- if (!map)
12
+ if (!Object.hasOwn(maps, resourceId))
13
13
  throw new ReviewInputError("Map is not included in this share.", 404);
14
14
 
15
- return map;
15
+ return maps[resourceId]!;
16
16
  }
17
17
 
18
18
  async resource(id: string, resourceId: string) {
19
- const resource = this.store
20
- .get(id)
21
- .manifest.resources.find((item) => item.id === resourceId);
22
-
23
- if (!resource)
24
- throw new ReviewInputError(
25
- "Resource is not included in this share.",
26
- 404,
27
- );
28
-
29
- return {
30
- data: await this.store.readObject(id, resource.object),
31
- mimeType: resource.mimeType,
32
- };
19
+ const { bundle } = this.store.get(id);
20
+
21
+ if (Object.hasOwn(bundle.images, resourceId))
22
+ return {
23
+ data: Buffer.from(bundle.images[resourceId]!.png, "base64"),
24
+ mimeType: "image/png",
25
+ };
26
+
27
+ if (Object.hasOwn(bundle.traces, resourceId))
28
+ return {
29
+ data: Buffer.from(JSON.stringify(bundle.traces[resourceId])),
30
+ mimeType: "application/json",
31
+ };
32
+
33
+ throw new ReviewInputError("Resource is not included in this share.", 404);
33
34
  }
34
35
  }
@@ -148,6 +148,7 @@ export const SETTING_NAME = [
148
148
  "sharing_enabled",
149
149
  "diffr_config",
150
150
  "theme",
151
+ "diff_theme",
151
152
  "ctrl_tab",
152
153
  "open_files_in",
153
154
  "ready_notification",
@@ -155,6 +156,7 @@ export const SETTING_NAME = [
155
156
  "code_font_size",
156
157
  "document_font_size",
157
158
  "reading_font",
159
+ "reading_font_family",
158
160
  "code_font_family",
159
161
  "font_ligatures",
160
162
  "diff_font_size",
@@ -621,6 +623,15 @@ export const UI_TELEMETRY_EVENTS = {
621
623
  event: "review_diff_failed",
622
624
  properties: { reason: DIFF_FAILED_REASON, files: "number" },
623
625
  },
626
+ diff_loaded: {
627
+ event: "review_diff_loaded",
628
+ properties: {
629
+ first_file_ms: "number",
630
+ updates_ms: "number",
631
+ files: "number",
632
+ update_errors: "number",
633
+ },
634
+ },
624
635
  discord_clicked: {
625
636
  event: "review_discord_clicked",
626
637
  properties: { via: DISCORD_VIA },
@@ -1,2 +0,0 @@
1
- import { n as runReviewAgentCli } from "./agent-cli-CHNDd4Ga.js";
2
- export { runReviewAgentCli };
@@ -1,2 +0,0 @@
1
- import { t as openReviewProfile } from "./profile-D9wdlPgw.js";
2
- export { openReviewProfile };
@@ -1,9 +1,9 @@
1
1
  import { a as jsonNumber, c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
2
  import { createHash, randomBytes } from "node:crypto";
3
3
  import { z } from "zod";
4
+ import path from "node:path";
4
5
  import { chmodSync, chownSync, closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeSync } from "node:fs";
5
6
  import { chmod, chown, lstat, mkdir, open, readFile, realpath, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
6
- import path from "node:path";
7
7
  import os from "node:os";
8
8
  import { spawnSync } from "node:child_process";
9
9
  import { setTimeout } from "node:timers/promises";
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
- import fs from "node:fs";
3
2
  import path from "node:path";
3
+ import fs from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { execFile, execFileSync, spawn } from "node:child_process";
6
6
  import "node:timers/promises";