@thatopen/services 0.21.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.
@@ -1,3 +1,4 @@
1
+ import { ProjectDocuments } from './documents';
1
2
  import { ProjectHistory } from './history';
2
3
  import { Recipes } from './recipes';
3
4
  import { ExecutionEntity, ExecutionSuscriptionReturnType } from '../types/execution';
@@ -631,6 +632,12 @@ export declare class EngineServicesClient {
631
632
  * @param onUpdateCallback - Callback invoked on each progress/result event.
632
633
  */
633
634
  onExecutionProgress(executionId: string, onUpdateCallback: (data: ExecutionSuscriptionReturnType) => void): Promise<void>;
635
+ /**
636
+ * A project's documents. Every document is a FOLDER holding its files plus
637
+ * a `document.json` descriptor naming the document, its native form and its
638
+ * renderable views. See `src/core/documents.ts` for the model and schema.
639
+ */
640
+ documents(projectId: string): ProjectDocuments;
634
641
  /**
635
642
  * A project's history of connected documents (Revit models, AutoCAD
636
643
  * drawings…): their version timelines and their change requests. See
@@ -0,0 +1,59 @@
1
+ import { EngineServicesClient } from './client';
2
+
3
+ /** One renderable representation of a document. */
4
+ export interface DocumentView {
5
+ /** The view file's item id — ids survive renames, names do not. */
6
+ fileId: string;
7
+ /** Lowercase format name: 'frag', 'ifc', 'potree', 'pdf'… */
8
+ format: string;
9
+ }
10
+ /** The wire shape of `document.json`, schema `thatopen/document@1`. */
11
+ export interface DocumentDescriptor {
12
+ schema: typeof DOCUMENT_SCHEMA;
13
+ /** The document's own name — what a row shows. Not any file's name. */
14
+ name: string;
15
+ /**
16
+ * The native form, when the document has one (a .rvt, a .3dm). A document
17
+ * born on the platform (a loose .frag) may not.
18
+ */
19
+ native?: {
20
+ fileId: string;
21
+ format: string;
22
+ };
23
+ /** The renderable representations, in no particular order. */
24
+ views: DocumentView[];
25
+ }
26
+ export declare const DOCUMENT_SCHEMA = "thatopen/document@1";
27
+ export declare const DOCUMENT_FILE = "document.json";
28
+ /**
29
+ * Documents of one project. Get it from {@link EngineServicesClient.documents}.
30
+ * Every method takes the document's FOLDER id — the id interfaces know the
31
+ * document by.
32
+ */
33
+ export declare class ProjectDocuments {
34
+ #private;
35
+ private readonly client;
36
+ readonly projectId: string;
37
+ constructor(client: EngineServicesClient, projectId: string);
38
+ /**
39
+ * The document's descriptor, read and validated, or null when the folder
40
+ * has none yet (an old folder, or a plain folder that is not a document).
41
+ */
42
+ descriptor(folderId: string): Promise<DocumentDescriptor | null>;
43
+ /**
44
+ * The first view whose format appears in `formats` — searched in the ORDER
45
+ * OF THE LIST, so the caller's preference wins (ask `['frag', 'ifc']` and a
46
+ * document carrying both answers the frag). Null when none matches: that is
47
+ * where an interface offers "Process".
48
+ */
49
+ view(folderId: string, formats: string[]): Promise<DocumentView | null>;
50
+ /**
51
+ * Registers a renderable representation — a processor's last act. One view
52
+ * per format: an existing view of the same format is replaced (a re-run
53
+ * processor updates, it does not accumulate). The descriptor must exist;
54
+ * create it first with {@link setDescriptor}.
55
+ */
56
+ addView(folderId: string, view: DocumentView): Promise<void>;
57
+ /** Writes the descriptor, creating or replacing `document.json`. */
58
+ setDescriptor(folderId: string, descriptor: Omit<DocumentDescriptor, 'schema'> & Partial<Pick<DocumentDescriptor, 'schema'>>): Promise<void>;
59
+ }
@@ -1,4 +1,5 @@
1
1
  import { EngineServicesClient } from './client';
2
+ import { DocumentDescriptor, DocumentView } from './documents';
2
3
 
3
4
  /** One element mutation inside a version. Wire-compatible with the plugins. */
4
5
  export interface FlowChange {
@@ -53,6 +54,13 @@ export interface FlowVersion {
53
54
  */
54
55
  export interface FlowSuggestion {
55
56
  opId: string;
57
+ /**
58
+ * The application that receives and executes it: 'revit', 'autocad', or a
59
+ * platform app like 'designer'. A suggestion without a recipient is not a
60
+ * suggestion; absent only in entries written before this field existed,
61
+ * where the recipient is the document's own source.
62
+ */
63
+ to?: string;
56
64
  /** Groups the suggestions one authored action produced across several docs. */
57
65
  batchId?: string;
58
66
  /** Hidden-file id of the payload .frag (envelope `revitflow/op@2`). */
@@ -81,8 +89,33 @@ export interface FlowDocInfo {
81
89
  source: string;
82
90
  /** The document's own id within that application. */
83
91
  docId: string;
92
+ /**
93
+ * The document folder's item id — the id the CDE and the documents API
94
+ * ({@link EngineServicesClient.documents}) know this document by.
95
+ */
96
+ folderId: string;
84
97
  /** The visible model file's name, when one exists yet. */
85
98
  modelName?: string;
99
+ /**
100
+ * True when the folder exists but holds neither history, model nor
101
+ * suggestion queue — a document somebody connected and never synced (a
102
+ * plugin mid-first-publish, or an abandoned start). A legitimate state,
103
+ * listed rather than hidden; apps decide whether to show it dimmed.
104
+ */
105
+ empty?: boolean;
106
+ }
107
+ /**
108
+ * One person's current hold on a document's elements, read from the lock
109
+ * registry the native plugins arbitrate through. Element keys are the native
110
+ * application's own ids (Revit UniqueIds). Workset-level ownership lives in
111
+ * Revit's own binary worksharing files (the ownership mirror) and is not
112
+ * readable here — what IS here is every element-level claim.
113
+ */
114
+ export interface FlowOwner {
115
+ /** The native application's user name, as the plugin claimed with. */
116
+ owner: string;
117
+ /** The element keys this person currently holds, arbitration applied. */
118
+ elements: string[];
86
119
  }
87
120
  /** What `sync` needs: the change list plus whatever the writer knows. */
88
121
  export interface SyncInput {
@@ -107,6 +140,11 @@ export interface SyncInput {
107
140
  }
108
141
  /** What `suggest` needs. */
109
142
  export interface SuggestInput {
143
+ /**
144
+ * The application that receives and executes it. Defaults to the document's
145
+ * own source — the native app that owns the document.
146
+ */
147
+ to?: string;
110
148
  /**
111
149
  * The suggestion's id, when the caller minted it up front — a payload
112
150
  * (envelope `revitflow/op@2`) carries the opId INSIDE, so whoever builds
@@ -171,6 +209,33 @@ export declare class FlowDoc {
171
209
  delta(version: FlowVersion): Promise<Blob>;
172
210
  /** The visible model .frag as last fully exported. */
173
211
  model(): Promise<Blob>;
212
+ /** The document folder's item id — the id the CDE knows this document by. */
213
+ folderId(): Promise<string>;
214
+ /**
215
+ * The document descriptor (`document.json`), or null when the folder does
216
+ * not carry one yet — an old folder from before descriptors existed.
217
+ */
218
+ descriptor(): Promise<DocumentDescriptor | null>;
219
+ /**
220
+ * The first view whose format appears in `formats`, searched in the order
221
+ * of the list (the caller's preference wins). Falls back to the visible
222
+ * model .frag on a descriptor-less folder, so old documents keep viewing.
223
+ */
224
+ view(formats: string[]): Promise<DocumentView | null>;
225
+ /**
226
+ * Registers a renderable representation, creating the descriptor when the
227
+ * folder predates them (named after the model file, or the folder itself).
228
+ * One view per format: a same-format view is replaced, not accumulated.
229
+ */
230
+ addView(view: DocumentView): Promise<void>;
231
+ /**
232
+ * Who currently holds which elements, read from the lock registry the
233
+ * native plugins arbitrate through. Read-only: claiming and releasing stay
234
+ * with the plugins. Empty when nobody holds anything (or the document has
235
+ * no lock anchor yet). Arbitration is applied: a key contested by two
236
+ * claims counts only for the earliest one, matching the plugins' rule.
237
+ */
238
+ ownership(): Promise<FlowOwner[]>;
174
239
  /**
175
240
  * Appends one version: uploads the geometry it carries, then the updated
176
241
  * history. Give `expectedLatestGuid` whenever you built on a head you read,
@@ -17,6 +17,15 @@ export interface ExistingLookup {
17
17
  of: string;
18
18
  /** Where it exists: the target document, or the declared source. Default 'target'. */
19
19
  from?: 'target' | 'source';
20
+ /**
21
+ * Narrows the candidates: attribute equals value, against the attribute
22
+ * names EXACTLY as the native application publishes them, no case
23
+ * normalization. Prefer the stable machine fields over display names —
24
+ * Revit's `Category` text is localized per install ("Armazón estructural"),
25
+ * so a column filter is `{ CategoryId: '-2001330' }`, never
26
+ * `{ Category: 'Columns' }`.
27
+ */
28
+ where?: Record<string, string>;
20
29
  }
21
30
  /** What a recipe declares about itself. Shown in catalogs, checked by apps. */
22
31
  export interface RecipeManifest {
@@ -104,6 +113,22 @@ export interface RecipeRunOutcome {
104
113
  suggestion: FlowSuggestion;
105
114
  result: RecipeResult;
106
115
  }
116
+ /**
117
+ * What {@link Recipes.plan} returns: everything needed to queue EXACTLY what
118
+ * was planned, without running the recipe again — the payload already embeds
119
+ * this `opId`. Hand it to {@link Recipes.queue} when the person confirms.
120
+ */
121
+ export interface RecipePlan {
122
+ opId: string;
123
+ projectId: string;
124
+ target: {
125
+ source: string;
126
+ docId: string;
127
+ };
128
+ /** The head the plan was built against, for the suggestion's base. */
129
+ baseCommitGuid?: string;
130
+ result: RecipeResult;
131
+ }
107
132
  /**
108
133
  * The local recipe registry and runner. Reach it as `client.recipes`.
109
134
  */
@@ -121,12 +146,21 @@ export declare class Recipes {
121
146
  /** A registered recipe by name, or undefined. */
122
147
  get(name: string): RecipeManifest | undefined;
123
148
  /**
124
- * Runs a recipe WITHOUT queueing anything: the dry pass. Returns the
125
- * recipe's result — title, tallies per rule, targets — so an app can show
126
- * what would be proposed before anybody confirms. The payload it may have
127
- * built is discarded along with its opId.
149
+ * Runs a recipe WITHOUT queueing anything: the dry pass. Returns a
150
+ * {@link RecipePlan} — the result to show (title, tallies, targets) plus
151
+ * everything {@link queue} needs to queue EXACTLY this, payload and opId
152
+ * included, with no second run. Discard it and the plan never happened.
153
+ */
154
+ plan<S>(name: string, options: RecipeRunOptions<S>): Promise<RecipePlan>;
155
+ /**
156
+ * Queues exactly what {@link plan} planned: same opId, same payload, no
157
+ * second execution. The pair plan → confirm → queue is the UI flow; `run`
158
+ * is the one-shot shortcut.
128
159
  */
129
- plan<S>(name: string, options: RecipeRunOptions<S>): Promise<RecipeResult>;
160
+ queue(plan: RecipePlan, options: {
161
+ author: string;
162
+ userId?: string;
163
+ }): Promise<RecipeRunOutcome>;
130
164
  /**
131
165
  * Runs a recipe and queues its suggestion on the target document. Safe to
132
166
  * call freely: nothing touches the native application until its owner