@milaboratories/pl-middle-layer 1.71.16 → 1.72.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.
Files changed (85) hide show
  1. package/dist/index.cjs +10 -5
  2. package/dist/index.d.ts +5 -4
  3. package/dist/index.js +3 -2
  4. package/dist/middle_layer/build_stamp.cjs +1 -1
  5. package/dist/middle_layer/build_stamp.js +1 -1
  6. package/dist/middle_layer/folders.cjs +631 -0
  7. package/dist/middle_layer/folders.cjs.map +1 -0
  8. package/dist/middle_layer/folders.d.ts +39 -0
  9. package/dist/middle_layer/folders.d.ts.map +1 -0
  10. package/dist/middle_layer/folders.js +613 -0
  11. package/dist/middle_layer/folders.js.map +1 -0
  12. package/dist/middle_layer/index.cjs +3 -0
  13. package/dist/middle_layer/index.d.ts +4 -3
  14. package/dist/middle_layer/index.js +2 -1
  15. package/dist/middle_layer/middle_layer.cjs +585 -370
  16. package/dist/middle_layer/middle_layer.cjs.map +1 -1
  17. package/dist/middle_layer/middle_layer.d.ts +215 -122
  18. package/dist/middle_layer/middle_layer.d.ts.map +1 -1
  19. package/dist/middle_layer/middle_layer.js +593 -378
  20. package/dist/middle_layer/middle_layer.js.map +1 -1
  21. package/dist/middle_layer/project_list.cjs +36 -19
  22. package/dist/middle_layer/project_list.cjs.map +1 -1
  23. package/dist/middle_layer/project_list.d.ts +1 -1
  24. package/dist/middle_layer/project_list.d.ts.map +1 -1
  25. package/dist/middle_layer/project_list.js +37 -21
  26. package/dist/middle_layer/project_list.js.map +1 -1
  27. package/dist/middle_layer/sharing_list.cjs +123 -93
  28. package/dist/middle_layer/sharing_list.cjs.map +1 -1
  29. package/dist/middle_layer/sharing_list.d.ts +43 -19
  30. package/dist/middle_layer/sharing_list.d.ts.map +1 -1
  31. package/dist/middle_layer/sharing_list.js +123 -93
  32. package/dist/middle_layer/sharing_list.js.map +1 -1
  33. package/dist/middle_layer/template_list.cjs +45 -20
  34. package/dist/middle_layer/template_list.cjs.map +1 -1
  35. package/dist/middle_layer/template_list.d.ts +7 -15
  36. package/dist/middle_layer/template_list.d.ts.map +1 -1
  37. package/dist/middle_layer/template_list.js +44 -21
  38. package/dist/middle_layer/template_list.js.map +1 -1
  39. package/dist/model/index.cjs +7 -5
  40. package/dist/model/index.d.ts +2 -2
  41. package/dist/model/index.js +2 -2
  42. package/dist/model/sharing_model.cjs +46 -20
  43. package/dist/model/sharing_model.cjs.map +1 -1
  44. package/dist/model/sharing_model.d.ts +125 -55
  45. package/dist/model/sharing_model.d.ts.map +1 -1
  46. package/dist/model/sharing_model.js +40 -16
  47. package/dist/model/sharing_model.js.map +1 -1
  48. package/dist/mutator/list.cjs +30 -0
  49. package/dist/mutator/list.cjs.map +1 -0
  50. package/dist/mutator/list.js +29 -0
  51. package/dist/mutator/list.js.map +1 -0
  52. package/dist/mutator/project.cjs +14 -1
  53. package/dist/mutator/project.cjs.map +1 -1
  54. package/dist/mutator/project.d.ts.map +1 -1
  55. package/dist/mutator/project.js +15 -2
  56. package/dist/mutator/project.js.map +1 -1
  57. package/dist/mutator/sharing.cjs +130 -76
  58. package/dist/mutator/sharing.cjs.map +1 -1
  59. package/dist/mutator/sharing.js +130 -76
  60. package/dist/mutator/sharing.js.map +1 -1
  61. package/dist/mutator/template.cjs +24 -17
  62. package/dist/mutator/template.cjs.map +1 -1
  63. package/dist/mutator/template.js +26 -20
  64. package/dist/mutator/template.js.map +1 -1
  65. package/package.json +19 -19
  66. package/src/middle_layer/folders.test.ts +1068 -0
  67. package/src/middle_layer/folders.ts +1059 -0
  68. package/src/middle_layer/folders_read.test.ts +359 -0
  69. package/src/middle_layer/index.ts +3 -2
  70. package/src/middle_layer/middle_layer.ts +813 -547
  71. package/src/middle_layer/project_list.test.ts +183 -0
  72. package/src/middle_layer/project_list.ts +58 -21
  73. package/src/middle_layer/sharing.test.ts +183 -0
  74. package/src/middle_layer/sharing_list.ts +208 -152
  75. package/src/middle_layer/template_list.test.ts +148 -0
  76. package/src/middle_layer/template_list.ts +72 -32
  77. package/src/middle_layer/templates.test.ts +76 -45
  78. package/src/model/sharing_model.test.ts +69 -2
  79. package/src/model/sharing_model.ts +161 -73
  80. package/src/mutator/list.ts +36 -0
  81. package/src/mutator/project-v3.test.ts +3 -1
  82. package/src/mutator/project.ts +13 -2
  83. package/src/mutator/sharing.ts +193 -120
  84. package/src/mutator/template.ts +37 -29
  85. package/src/test/with_ml.ts +73 -16
@@ -3,10 +3,11 @@ import { TemplateApplyReport } from "../model/template_apply.js";
3
3
  import { BlockPackProvider, TemplateResolveOutcome } from "../model/template_resolve.js";
4
4
  import "../block_registry/index.js";
5
5
  import { ProjectId as ProjectId$1, ProjectListEntry as ProjectListEntry$1 } from "../model/project_model.js";
6
- import { ProjectChangeAction, ShareId, ShareProjectsOptions, ShareTemplateOptions } from "../model/sharing_model.js";
7
- import { CreateProjectFromTemplateOutcome, SaveProjectAsTemplateOutcome, ShareTemplateOutcome, StoredTemplateData, TemplateId, TemplateListEntry } from "./template_list.js";
6
+ import { CreateProjectFromTemplateOutcome, SaveProjectAsTemplateOutcome, StoredTemplateData, TemplateId as TemplateId$1, TemplateListEntry } from "./template_list.js";
7
+ import { FoldersListing } from "./folders.js";
8
8
  import { ProjectTemplateExportOutcome } from "../model/template_serializer.js";
9
- import { OutgoingShare, PendingShare } from "./sharing_list.js";
9
+ import { ShareFolderOptions, ShareId, ShareOutcome, ShareProjectsOptions, ShareTemplateOptions } from "../model/sharing_model.js";
10
+ import { AvailableShare, OutgoingShare } from "./sharing_list.js";
10
11
  import "../model/index.js";
11
12
  import { BlockPackPreparer } from "../mutator/block-pack/block_pack.js";
12
13
  import { Project } from "./project.js";
@@ -16,7 +17,7 @@ import { MiddleLayerDriverKit } from "./driver_kit.js";
16
17
  import { ProjectHelper } from "../model/project_helper.js";
17
18
  import { TreeSnapshotStat, TreeSnapshotStore } from "./tree_snapshot_store.js";
18
19
  import { BlockCodeFeatureFlags, DriverKit, RuntimeCapabilities, SupportedRequirement } from "@platforma-sdk/model";
19
- import { AuthorMarker, BlockPlatform, ProjectMeta } from "@milaboratories/pl-model-middle-layer";
20
+ import { AuthorMarker, BlockPlatform, FolderId, FoldersItem, FoldersMoveOutcome, FoldersMovePlan, FoldersMovePlanResult, FoldersRemoval, FoldersRemovalOutcome, FoldersRemovalPlanResult, ProjectMeta } from "@milaboratories/pl-model-middle-layer";
20
21
  import { Dispatcher } from "undici";
21
22
  import { BlockEventDispatcher, MiLogger, Signer } from "@milaboratories/ts-helpers";
22
23
  import { ModelServiceRegistry, ProjectTemplateV1 } from "@milaboratories/pl-model-common";
@@ -66,25 +67,29 @@ export declare class MiddleLayer {
66
67
  private readonly templateListResourceId;
67
68
  private readonly sharingOutboxResourceId;
68
69
  private readonly sharingStateResourceId;
70
+ private readonly foldersResourceId;
69
71
  private readonly openedProjectsList;
70
72
  private readonly projectListTree;
71
73
  private readonly templateListTree;
74
+ private readonly foldersTree;
72
75
  private readonly sharingOutboxTree;
73
76
  private readonly sharingStateTree;
74
- private readonly pendingSharesTree;
77
+ private readonly availableSharesTree;
75
78
  readonly blockRegistryProvider: V2RegistryProvider;
76
79
  /** Contains a reactive list of projects along with their meta information. */
77
80
  readonly projectList: ComputableStableDefined<ProjectListEntry$1[]>;
78
81
  /** Contains a reactive list of stored templates along with their labels and provenance. */
79
82
  readonly templateList: ComputableStableDefined<TemplateListEntry[]>;
80
- /** Reactive view of the donor's outbox — the shares this user has created.
81
- * v1: API only, no UI. */
83
+ /** The folder tree and the project list, already joined. The desktop reads this and never
84
+ * the two halves, so it cannot render a list and a tree that are one refresh apart. */
85
+ readonly folders: ComputableStableDefined<FoldersListing>;
86
+ /** Reactive view of the donor's outbox — the shares this user has created. */
82
87
  outgoingShares: Computable<OutgoingShare[] | undefined>;
83
- /** Envelopes granted to this user, not yet accepted or rejected. Fed by the
88
+ /** Shares granted to this user, hidden ones included and flagged as such. Fed by the
84
89
  * shared-resource discovery tree. */
85
- pendingShares: Computable<PendingShare[] | undefined>;
86
- /** Internal: the acceptor's currently-live envelopes, read from the same shared-resource
87
- * discovery tree as {@link pendingShares}. The single source the accept/reject flow resolves
90
+ availableShares: Computable<AvailableShare[] | undefined>;
91
+ /** Internal: the recipient's currently-live envelopes, read from the same shared-resource
92
+ * discovery tree as {@link availableShares}. The single source {@link copyShare} resolves
88
93
  * live envelopes from — no second discovery path. */
89
94
  private readonly liveEnvelopes;
90
95
  readonly pl: PlClient;
@@ -109,7 +114,7 @@ export declare class MiddleLayer {
109
114
  /**
110
115
  * Whether the connected backend supports project sharing. Synthetic — computed
111
116
  * in the middle layer from the backend capabilities the share flow needs (the
112
- * cross-color field-reference relaxation the accept flow rests on). It can absorb
117
+ * cross-color field-reference relaxation a copy out of a share rests on). It can absorb
113
118
  * additional required capabilities later without a UI change.
114
119
  */
115
120
  get sharingSupported(): boolean;
@@ -147,10 +152,84 @@ export declare class MiddleLayer {
147
152
  /** Resolves a ProjectId to a signed SignedResourceId.
148
153
  * Uses LRU cache with TX-scan fallback. */
149
154
  private resolveProjectId;
150
- /** Creates a project with initial state and adds it to project list. */
151
- createProject(meta: ProjectMeta): Promise<ProjectId$1>;
152
- /** Updates project metadata */
153
- setProjectMeta(id: ProjectId$1, meta: ProjectMeta, author?: AuthorMarker): Promise<void>;
155
+ /**
156
+ * Creates a project with initial state and adds it to project list.
157
+ *
158
+ * `folder` is where it lands, placed in the transaction that creates it so the project never
159
+ * shows up at the top level first. Left out, or naming a folder that is gone, it lands at the
160
+ * top level.
161
+ */
162
+ createProject(meta: ProjectMeta, folder?: FolderId): Promise<ProjectId$1>;
163
+ /**
164
+ * Updates the project metadata fields the caller names, leaving the others as they are.
165
+ *
166
+ * A patch rather than a replacement because the label and the description are edited from two
167
+ * different places: a rename that carried a stale description alongside the new name would
168
+ * undo a description edit that happened in between, and the reverse.
169
+ *
170
+ * The label is stored trimmed, and it goes into the namespace folders share, so a label
171
+ * already carried by something else inside the same folder is refused rather than written — a
172
+ * human typed it, and a name that silently becomes a different name is worse than one that is
173
+ * turned down. The check and the write share one transaction, so two renames racing for the
174
+ * same name cannot both win. Duplicates an account already holds are tolerated and never
175
+ * rewritten; see the name check of {@link openFoldersTx}.
176
+ */
177
+ setProjectMeta(id: ProjectId$1, meta: Partial<ProjectMeta>, author?: AuthorMarker): Promise<void>;
178
+ private get foldersRids();
179
+ /** Creates a folder inside `parent`, or at the top level, and returns its id. A name already
180
+ * used there is rejected. */
181
+ createFolder(name: string, parent?: FolderId): Promise<FolderId>;
182
+ /** Renames a folder. A name already used beside it is rejected. */
183
+ renameFolder(folder: FolderId, name: string): Promise<void>;
184
+ /** Sets what a folder says about itself; blank clears it. Descriptions are in no namespace, so
185
+ * nothing is refused here. */
186
+ setFolderDescription(folder: FolderId, description: string): Promise<void>;
187
+ /** The plan a move would produce — every item and the name it ends up with. Shown for
188
+ * confirmation, then handed back to {@link moveFolderItems} unchanged. */
189
+ previewFoldersMove(items: readonly FoldersItem[], destination?: FolderId): Promise<FoldersMovePlanResult>;
190
+ /**
191
+ * Moves folders, projects and templates into one destination.
192
+ *
193
+ * The plan is recomputed inside the write transaction and the move commits only when it is
194
+ * identical to `confirmedPlan`; otherwise nothing is written and the fresh plan comes back to
195
+ * be confirmed again.
196
+ */
197
+ moveFolderItems(items: readonly FoldersItem[], destination: FolderId | undefined, confirmedPlan: FoldersMovePlan): Promise<FoldersMoveOutcome>;
198
+ /** What deleting a folder would destroy: the subtree of folders, and everything in it. */
199
+ previewFolderDeletion(folder: FolderId): Promise<FoldersRemovalPlanResult>;
200
+ /** Deletes a folder, every folder inside it, and every project and template held anywhere in
201
+ * that subtree.
202
+ * Refused as `needs-confirmation` until the removal it returns is passed back as
203
+ * `confirmedRemoval`, and as `plan-changed` if the subtree has changed since. */
204
+ deleteFolder(folder: FolderId, confirmedRemoval?: FoldersRemoval): Promise<FoldersRemovalOutcome>;
205
+ /**
206
+ * Replaces a folder document this build cannot read — one written by a newer version, or one
207
+ * nothing here can parse — with an empty one. Every folder is gone afterwards; every project and
208
+ * template stays and shows at the top level. Refused while the document can be read.
209
+ */
210
+ resetFolders(): Promise<void>;
211
+ /**
212
+ * Duplicates a folder and everything under it: the folders inside, a duplicate of every project
213
+ * in them, and a copy of every template.
214
+ *
215
+ * The copy lands beside the source, so only its root needs a name of its own — `X (Copy)`.
216
+ * Nothing inside is renamed: names are compared among siblings, and a copied folder's children
217
+ * are only ever compared with each other, where they came in distinct already.
218
+ *
219
+ * The subtree is read first and rebuilt in one write transaction, so the folders and everything
220
+ * they hold appear together or not at all.
221
+ */
222
+ duplicateFolder(folder: FolderId): Promise<void>;
223
+ /**
224
+ * A folder subtree as a copy needs it: the folders under ids local to this read, the key the
225
+ * subtree's root goes by, the resource of every project in them, every template read whole, and
226
+ * the folder the source sits in — which is where a duplicate lands.
227
+ *
228
+ * Read in a transaction of its own, before the write that copies it: every template is a read,
229
+ * and none of it has to be atomic with the copying, because a project or a template that
230
+ * disappears meanwhile fails that write on its own.
231
+ */
232
+ private loadFolderSubtree;
154
233
  /**
155
234
  * Renders a project as a `template-v1` YAML document, or reports every reason it
156
235
  * cannot be — the backing call for an "Export Project as Template…" command.
@@ -240,23 +319,40 @@ export declare class MiddleLayer {
240
319
  * A block that cannot be expressed as a template entry stores nothing at all, and every
241
320
  * such block is reported — fixing an unexportable project takes one pass, not one per block.
242
321
  *
322
+ * The template lands beside its project, and is named by the rule everything there is named
323
+ * by: a label the caller chose is stored trimmed and refused if it is already used there, and
324
+ * without one the project's own label is taken — suffixed, since the project itself already
325
+ * answers to it.
326
+ *
327
+ * The template's description is the one the caller gives, stored trimmed; blank means none.
328
+ * Without one the template is stored undescribed, whatever the project's own description says.
329
+ *
243
330
  * @param projectId project to snapshot
244
- * @param label label for the template; defaults to the project's own label
331
+ * @param label label for the template; defaults to the project's own label, made free
332
+ * @param description what the template is for; blank or absent stores none
333
+ */
334
+ saveProjectAsTemplate(projectId: ProjectId$1, label?: string, description?: string): Promise<SaveProjectAsTemplateOutcome>;
335
+ /**
336
+ * Changes a template's label. The stored document is immutable and stays untouched —
337
+ * improving a template means saving a new one.
338
+ *
339
+ * The label is stored trimmed. It is in the namespace folders, projects and templates share,
340
+ * so one already used beside the template is refused, in the transaction that writes it.
245
341
  */
246
- saveProjectAsTemplate(projectId: ProjectId$1, label?: string): Promise<SaveProjectAsTemplateOutcome>;
247
- /** Changes a template's label. The stored document is immutable and stays untouched —
248
- * improving a template means saving a new one. */
249
- renameTemplate(id: TemplateId, label: string): Promise<void>;
342
+ renameTemplate(id: TemplateId$1, label: string): Promise<void>;
343
+ /** Sets what a stored template says about itself; blank clears it. The stored document is not
344
+ * touched, so it stays byte-identical. */
345
+ setTemplateDescription(id: TemplateId$1, description: string): Promise<void>;
250
346
  /** Permanently deletes a template from the template list. */
251
- deleteTemplate(id: TemplateId): Promise<void>;
347
+ deleteTemplate(id: TemplateId$1): Promise<void>;
252
348
  /** Reads a stored template: its document plus what was true when it was taken. */
253
- getTemplateData(id: TemplateId): Promise<StoredTemplateData>;
349
+ getTemplateData(id: TemplateId$1): Promise<StoredTemplateData>;
254
350
  /**
255
351
  * Where each entry of a stored template would get its block from, and which entries have
256
352
  * nowhere to get one — resolution creates nothing, so this is the preview a UI shows before
257
353
  * offering Apply. {@link createProjectFromTemplate} runs the same stage itself.
258
354
  */
259
- resolveTemplate(id: TemplateId, provider: BlockPackProvider, options?: {
355
+ resolveTemplate(id: TemplateId$1, provider: BlockPackProvider, options?: {
260
356
  allowUnstable?: boolean;
261
357
  }): Promise<TemplateResolveOutcome>;
262
358
  /**
@@ -269,11 +365,14 @@ export declare class MiddleLayer {
269
365
  * @param id template to apply
270
366
  * @param label label for the new project
271
367
  * @param provider where each entry's block comes from
272
- * @param options `allowUnstable` widens resolution to pre-release implementations
368
+ * @param options `allowUnstable` widens resolution to pre-release implementations; `folder` is
369
+ * where the new project lands
273
370
  */
274
- createProjectFromTemplate(id: TemplateId, label: string, provider: BlockPackProvider, options?: {
371
+ createProjectFromTemplate(id: TemplateId$1, label: string, provider: BlockPackProvider, options?: {
275
372
  allowUnstable?: boolean;
276
373
  author?: AuthorMarker;
374
+ /** Where the project lands; the top level when left out. */
375
+ folder?: FolderId;
277
376
  }): Promise<CreateProjectFromTemplateOutcome>;
278
377
  /** Resolves a TemplateId to a signed SignedResourceId.
279
378
  * Uses LRU cache with TX-scan fallback. */
@@ -282,62 +381,39 @@ export declare class MiddleLayer {
282
381
  * destruction of all attached objects, like files, analysis results etc. */
283
382
  deleteProject(id: ProjectId$1): Promise<void>;
284
383
  /**
285
- * Duplicates an existing project and adds the copy to this user's project list.
384
+ * Duplicates an existing project and adds the copy to this user's project list, beside the
385
+ * project it was copied from.
386
+ *
387
+ * Without `rename` the copy is named by the rule everything beside the source is named by: the
388
+ * source's own label, suffixed to be free there — `X (Copy)`. The name is chosen inside the
389
+ * transaction that creates the copy, against the tree that transaction reads.
286
390
  *
287
391
  * @param srcProjectId - project id of the project to duplicate
288
392
  * @param rename - optional function that receives the source label and all existing
289
- * project labels (read within the same transaction), and returns the label for the copy
393
+ * project labels (read within the same transaction), and returns the label for the copy.
394
+ * A label chosen this way is the caller's and is never suffixed: when it is already taken
395
+ * beside the source, the copy lands at the top level instead.
290
396
  */
291
397
  duplicateProject(srcProjectId: ProjectId$1, rename?: (previousLabel: string, existingLabels: string[]) => string): Promise<ProjectId$1>;
292
398
  /**
293
399
  * Duplicates a project into another user's root, minted in the TARGET user's color so the target
294
400
  * owns it. Sibling of {@link duplicateProject}, but writes into a different root. The source
295
401
  * project (on the current client root) is referenced cross-color for its block data, kept alive
296
- * by refcounting, exactly like accepting a shared project. Works both ways: pull (while
402
+ * by refcounting, exactly like a project copied out of a share. Works both ways: pull (while
297
403
  * impersonating a user, copy their project to yourself) and push (from your own root, copy a
298
404
  * project to a user). Admin cross-root op; requires the crossTreeRefs:v1 backend capability.
299
405
  */
300
406
  duplicateProjectToUser(srcProjectId: ProjectId$1, targetLogin: string, rename?: (previousLabel: string, existingLabels: string[]) => string): Promise<void>;
301
407
  /**
302
- * Shares the given projects (Copy & Share). Snapshots the projects, creates one envelope, and
303
- * grants it — all in one atomic write transaction, so a failed grant rolls the whole thing back
304
- * and the outbox is left as it was.
305
- *
306
- * Two variants (see {@link ShareProjectsOptions}):
307
- * - `{ recipients }` — one writable grant per named recipient; the envelope expires after the
308
- * default TTL (`sharedAt + envelopeTtlMs`).
309
- * - `{ everyone: true }` — one make-public grant (backend rewrites the target to the
310
- * everyone-user); the envelope's `expiresAt` is `null`, so it never expires.
408
+ * Shares the given projects (Copy & Share): snapshots them into one envelope, in the transaction
409
+ * {@link shareEnvelope} describes.
311
410
  *
312
411
  * v1 always passes `mode: "copy"`.
313
412
  */
314
- shareProjects(projectIds: ProjectId$1[], options: ShareProjectsOptions): Promise<void>;
315
- /**
316
- * Mints a fresh share: snapshots the projects into one new envelope (a fresh shareId),
317
- * supersedes prior shares of the same project, and grants it — all in one atomic write
318
- * transaction, so a failed grant rolls the whole thing back and the outbox is left as it was.
319
- * The everyone-refresh path is the {@link changeShare} branch of {@link shareProjects}; this is
320
- * the mint-a-new-envelope branch.
321
- */
322
- private createNewShare;
323
- /**
324
- * Grants one freshly built envelope inside the transaction that created it: a single make-public
325
- * grant for an everyone-share (empty/ignored target, ANY_AUTHORISED — the backend rewrites the
326
- * target to the everyone-user, gated by role + permission ceiling), or one grant per named
327
- * recipient.
328
- *
329
- * `writable` is not a preference. A project pack needs a writable grant because accepting copies
330
- * the snapshots out of the envelope, and the cross-color attach rule permits that only to a
331
- * writable grant holder. A template share copies nothing — the document sits in the envelope's
332
- * own immutable data — so it is granted read-only, and must be: a writable everyone-grant would
333
- * hand every user on the server write access to the envelope.
334
- */
335
- private grantShareEnvelope;
413
+ shareProjects(projectIds: ProjectId$1[], options: ShareProjectsOptions): Promise<ShareOutcome>;
336
414
  /**
337
415
  * Shares one stored template. The envelope carries the document itself, so there is no project
338
416
  * snapshot and no resource for the recipient to copy out — which is why the grant is read-only.
339
- * The cost of that is the donor's receipt: nobody can write an acceptance onto a read-only
340
- * envelope, so a template share never reports who accepted it.
341
417
  *
342
418
  * Nothing about the document is checked: a stored template is shareable by virtue of existing.
343
419
  * An entry the recipient cannot resolve — a block installed from a folder on the sender's
@@ -347,49 +423,58 @@ export declare class MiddleLayer {
347
423
  * @param id template to share
348
424
  * @param options recipients XOR everyone, plus the title recipients see
349
425
  */
350
- shareTemplate(id: TemplateId, options: ShareTemplateOptions): Promise<ShareTemplateOutcome>;
351
- /** The document and the label of a template about to be shared. The label is what the
352
- * recipient's own list will show, so it travels with the document. */
353
- private loadTemplateForShare;
354
- /**
355
- * Changes a share in place (same {@link ShareId}), in one write transaction: re-snapshots live
356
- * source projects and carries deleted ones' snapshots forward; applies edited recipients/title;
357
- * transfers already-decided recipients' accept/reject records (they keep their copy and aren't
358
- * re-prompted); re-grants; drops the old envelope.
359
- *
360
- * `opts.recipients` is the full targeted set (decided users are always kept). `opts.title`
361
- * replaces the title — omit keeps the current one. `opts.everyone` upgrades targeted ->
362
- * everyone; the reverse is impossible and ignored.
363
- *
364
- * `opts.projectActions` is a per-source-project decision, keyed by projectId: `update`
365
- * re-snapshots the live source (falls back to carry if the source is gone), `keep` carries the
366
- * existing snapshot (and its timestamp), `remove` drops the project from the pack. A project not
367
- * in the map defaults to `keep`. Omit the whole map for the legacy auto behavior (live sources
368
- * updated, gone ones kept) — the everyone-refresh path relies on that.
369
- *
370
- * `opts.templateId` is required for, and only used by, a share that carries a template: a stored
371
- * template is immutable, so an improved one is a different template and the share cannot re-read
372
- * the one it started from — the caller names the new target. Every other option means the same
373
- * thing for both kinds of share.
374
- */
375
- changeShare(shareId: ShareId, opts?: {
376
- recipients?: string[];
377
- everyone?: boolean;
378
- title?: string;
379
- projectActions?: Record<ProjectId$1, ProjectChangeAction>;
380
- templateId?: TemplateId;
381
- }): Promise<void>;
382
- /**
383
- * Finds the donor's own outgoing envelopes built from any of the given source projects —
384
- * the supersede candidates for a fresh share of the same project(s). Reads each envelope's
385
- * recipient set via `ListGrants` so the caller can pull individual recipients or detect an
386
- * everyone-share.
387
- */
388
- private findSupersedableEnvelopes;
426
+ shareTemplate(id: TemplateId$1, options: ShareTemplateOptions): Promise<ShareOutcome>;
427
+ /**
428
+ * Shares one folder and everything under it: the subtree's folders, every project in them
429
+ * snapshotted, and every template carried whole.
430
+ *
431
+ * The grant is writable, because a folder holding projects is copied out of the envelope the
432
+ * way a project pack is. An everyone-share of a folder therefore hands every user on the server
433
+ * write access to the envelope — the same trade a project share already makes.
434
+ *
435
+ * Folder ids do not travel. What the recipient gets is the shape of the subtree, rebuilt under
436
+ * a folder of their own choosing with ids their own document mints.
437
+ *
438
+ * @param folder folder to share; everything beneath it goes with it
439
+ * @param options recipients XOR everyone, plus the title recipients see
440
+ */
441
+ shareFolder(folder: FolderId, options: ShareFolderOptions): Promise<ShareOutcome>;
442
+ /**
443
+ * The one transaction every share is made in: the shares named by `options.replace` are
444
+ * dropped, the envelope `build` makes is created, and it is granted — all at once, so a failed
445
+ * grant rolls the whole thing back and the outbox is left as it was.
446
+ *
447
+ * A share with named recipients grants each of them and expires after the default TTL
448
+ * (`sharedAt + envelopeTtlMs`). A share with everyone is one make-public grant, and its
449
+ * `expiresAt` is `null`, so it never expires.
450
+ */
451
+ private shareEnvelope;
452
+ /**
453
+ * Detaches the named shares from the donor's outbox inside the caller's transaction, so a
454
+ * replacement and the shares it supersedes land together or not at all.
455
+ *
456
+ * A share that no longer resolves is skipped rather than reported: the caller names shares the
457
+ * author saw a moment ago, and one revoked meanwhile is already in the wanted state.
458
+ */
459
+ private dropShares;
460
+ /**
461
+ * Grants one freshly built envelope inside the transaction that created it: a single make-public
462
+ * grant for an everyone-share (empty/ignored target, ANY_AUTHORISED — the backend rewrites the
463
+ * target to the everyone-user, gated by role + permission ceiling), or one grant per named
464
+ * recipient.
465
+ *
466
+ * `writable` is not a preference. A project pack needs a writable grant because a copy out of
467
+ * it takes the snapshots out of the envelope, and the cross-color attach rule permits that only
468
+ * to a writable grant holder. A template share copies nothing — the document sits in the
469
+ * envelope's own immutable data — so it is granted read-only, and must be: a writable
470
+ * everyone-grant would hand every user on the server write access to the envelope.
471
+ */
472
+ private grantShareEnvelope;
389
473
  /**
390
474
  * Revokes and deletes an outgoing share for all recipients: detaches and deletes the envelope, and
391
- * its grants are revoked along with it. Already-accepted copies are unaffected (ref-counting keeps
392
- * the adopted resources alive). Idempotent — revoking a share that is already gone is a no-op.
475
+ * its grants are revoked along with it. Copies recipients already took out of it are unaffected
476
+ * (ref-counting keeps the resources they point at alive). Idempotent — revoking a share that is
477
+ * already gone is a no-op.
393
478
  */
394
479
  revokeShare(shareId: ShareId): Promise<void>;
395
480
  /**
@@ -404,35 +489,43 @@ export declare class MiddleLayer {
404
489
  * the envelope's logical `shareId`.
405
490
  *
406
491
  * Reads the {@link liveEnvelopes} Computable — the same shared-resource discovery tree that
407
- * feeds {@link pendingShares}. This is the single discovery mechanism: there is no separate
408
- * `ListUserResources` re-stream on every accept/reject. `refreshState()` is awaited first so a
492
+ * feeds {@link availableShares}. This is the single discovery mechanism: there is no separate
493
+ * `ListUserResources` re-stream on every copy. `refreshState()` is awaited first so a
409
494
  * just-granted envelope is observed (the tree's discovery poll may otherwise lag a freshly
410
495
  * landed grant). The tree is gRPC-only, so this is empty on a REST-connected client.
411
496
  */
412
497
  private resolveLiveEnvelopes;
413
498
  /**
414
- * Accepts one or more pending shares. What accepting does depends on what the share carries: a
415
- * pack of projects is duplicated into this user's project list, while a template is added to this
416
- * user's own template list and builds nothing — the recipient decides later whether to apply it.
417
- * Either way the decision is recorded per share, and a read-write share also gets the
418
- * donor-visible acceptance written onto its envelope. Per-share failures (e.g. an expiry race)
419
- * are collected, not short-circuited — the rest still get accepted. Accept-all = pass every
420
- * current pending shareId.
421
- *
422
- * `rename` resolves label collisions (same callback contract as {@link duplicateProject}), but
423
- * the source lives in the envelope tree, so accept calls the low-level mutator directly. It does
424
- * not apply to a template share, whose label is not required to be unique.
425
- */
426
- acceptShare(shareIds: ShareId[], rename?: (previousLabel: string, existingLabels: string[]) => string): Promise<{
427
- accepted: ProjectId$1[];
428
- acceptedTemplates: TemplateId[];
499
+ * Copies what one or more shares carry into this user's own tree, optionally into a folder.
500
+ *
501
+ * A share is a shelf, not an invitation: copying takes nothing off it and records no decision,
502
+ * so the same share can be copied from again, by this user or anyone else it was granted to.
503
+ * What a copy produces depends on the payload — a pack of projects lands in the project list,
504
+ * a template among their templates, building nothing until the recipient applies it, and a
505
+ * folder is rebuilt whole with everything it held.
506
+ *
507
+ * Names are chosen against the destination folder, since that is where the uniqueness rule
508
+ * applies, and a project and a template follow the same rule. A destination deleted meanwhile
509
+ * fails the copy rather than spilling it at the top level.
510
+ * Per-share failures (a revoked envelope, say) are collected rather than short-circuited, so
511
+ * one dead share does not cost the others.
512
+ */
513
+ copyShare(shareIds: ShareId[], destination?: FolderId): Promise<{
514
+ projects: ProjectId$1[];
515
+ templates: TemplateId$1[];
429
516
  failed: {
430
517
  shareId: ShareId;
431
518
  error: string;
432
519
  }[];
433
520
  }>;
434
- /** Records rejection of a pending share; it never surfaces again. */
435
- rejectShare(shareId: ShareId): Promise<void>;
521
+ /**
522
+ * Puts a share out of this user's sight. Private to them and reversible with
523
+ * {@link unhideShare}: nothing is deleted, the donor is not told, and what was already copied
524
+ * out of it is unaffected.
525
+ */
526
+ hideShare(shareId: ShareId): Promise<void>;
527
+ /** Brings a hidden share back into this user's list. */
528
+ unhideShare(shareId: ShareId): Promise<void>;
436
529
  private static readonly EnvelopeCleanupIntervalMs;
437
530
  private envelopeCleanupTimer;
438
531
  /** On ML start and every 6h, delete envelopes whose immutable `expiresAt` has passed. */
@@ -1 +1 @@
1
- {"version":3,"file":"middle_layer.d.ts","names":[],"sources":["../../src/middle_layer/middle_layer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4IiB;EACf,WAAW;WACF,IAAI;WACJ,qBAAqB;WACrB,QAAQ;WACR,sBAAsB;WACtB,gBAAgB;WAChB,qBAAqB;WACrB,QAAQ;WACR,KAAK;WACL,YAAY;WACZ,wBAAwB;WACxB,oBAAoB;WACpB,SAAS;WACT,WAAW;WACX,iBAAiB;WACjB,eAAe;;;WAGf,gBAAgB;;;;;;;;;;;;;;qBAed;mBAIQ;WACD,WAAW;WACX,QAAQ;mBACP;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;WACD,uBAAuB;;WAEvB,aAAa,wBAAwB;;WAErC,cAAc,wBAAwB;;;EAG/C,gBAAgB,WAAW;;;EAG3B,eAAe,WAAW;;;;mBAIhB;WA9BH,IAAI;UAEb;;;;;MAsCI,kBAAkB;;;;;;;MAUlB;;;;;MAQA;;;;;;;MAUA;;MAQA;;;;;MAQA,mBAAmB;;;;;;;MAUnB;;;;;;;;MAeA;;EAKJ,qBACL,aAAa,sBACb;;EAMK,wBAAwB,cAAc;;MAKlC,qBAAqB;;MAKrB,mBAAmB;mBAQb;;;UAIH;;EAuBD,cAAc,MAAM,cAAc,QAAQ;;EAgB1C,eACX,IAAI,aACJ,MAAM,aACN,SAAS,eACR;;;;;;;;;;;;;;;;EA8BU,wBAAwB,IAAI,cAAY,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDhD,uBACX,IAAI,aACJ,UAAU,mBACV,UAAU,mBACV;IAAW;IAAyB,SAAS;MAC5C,QAAQ;;;;;;;;;UAgBG;;;;;;;UAiFA;mBA0CG;;;;;;;;;;;;;EAcJ,sBACX,WAAW,aACX,iBACC,QAAQ;;;EAyBE,eAAe,IAAI,YAAY,gBAAgB;;EAU/C,eAAe,IAAI,aAAa;;EAUhC,gBAAgB,IAAI,aAAa,QAAQ;;;;;;EAczC,gBACX,IAAI,YACJ,UAAU,mBACV;IAAW;MACV,QAAQ;;;;;;;;;;;;;EAmBE,0BACX,IAAI,YACJ,eACA,UAAU,mBACV;IAAW;IAAyB,SAAS;MAC5C,QAAQ;;;UA2BG;;;EAoBD,cAAc,IAAI,cAAY;;;;;;;;EA0B9B,iBACX,cAAc,aACd,UAAU,uBAAuB,sCAChC,QAAQ;;;;;;;;;EAoDE,uBACX,cAAc,aACd,qBACA,UAAU,uBAAuB,sCAChC;;;;;;;;;;;;;;EAsDU,cACX,YAAY,eACZ,SAAS,uBACR;;;;;;;;UA0BW;;;;;;;;;;;;;UA2EA;;;;;;;;;;;;;;;EA0BD,cACX,IAAI,YACJ,SAAS,uBACR,QAAQ;;;UA6BG;;;;;;;;;;;;;;;;;;;;;;EAmCD,YACX,SAAS,SACT;IACE;IACA;IACA;IACA,iBAAiB,OAAO,aAAW;IACnC,aAAa;MAEd;;;;;;;UAwIW;;;;;;EA6CD,YAAY,SAAS,UAAU;;;;;;;UAiB9B;;;;;;;;;;;UA0BA;;;;;;;;;;;;;;EAsBD,YACX,UAAU,WACV,UAAU,uBAAuB,sCAChC;IACD,UAAU;IACV,mBAAmB;IACnB;MAAU,SAAS;MAAS;;;;EAyFjB,YAAY,SAAS,UAAU;0BA4BpB;UAChB;;UAGA;;;UAWM;mBAsCG;;;mBAIA;UAET;;;;;;;UAWM;;EAgBD,YAAY,IAAI,cAAY;;EAQ5B,aAAa,IAAI,cAAY;;EAmBnC,iBAAiB,IAAI,cAAY;;EAOjC,gBAAgB,IAAI;;;;MAOhB,qBAAqB,SAAS;;;;;;EAS5B,SAAK;;EAiBL,4BAAwB;;;SAMvB;;MAKH,wBAAwB;;SAKf,KAClB,IAAI,UACJ,iBACA,MAAM,4BACL,QAAQ"}
1
+ {"version":3,"file":"middle_layer.d.ts","names":[],"sources":["../../src/middle_layer/middle_layer.ts"],"mappings":"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"}