@thatopen/services 0.18.0 → 0.20.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,5 @@
1
+ import { ProjectHistory } from './history';
2
+ import { Recipes } from './recipes';
1
3
  import { ExecutionEntity, ExecutionSuscriptionReturnType } from '../types/execution';
2
4
  import { AppVersionProps, ComponentVersionProps, ComponentItem, AppItem, Item, ItemFolder, ItemVersion, ItemWithVersions } from '../types/items';
3
5
  import { CreateItemResponse, UpdateItemResponse } from '../types/response';
@@ -186,19 +188,23 @@ export declare class EngineServicesClient {
186
188
  * {@link initBuiltInComponent} call will use these globals automatically
187
189
  * — you no longer need to pass a `globals` argument to each one.
188
190
  *
189
- * @param globals - Map of global names to module namespaces.
190
- * Common keys: `OBC`, `OBF`, `BUI`, `CUI`, `THREE`, `FRAGS`, `MARKERJS`.
191
+ * @param globals - Map of global names to module namespaces. The FULL set
192
+ * a built-in may ask for: `OBC`, `OBF`, `BUI`, `THREE`, `FRAGS`,
193
+ * `MARKERJS` (plus `CUI` only if the app still uses the deprecated
194
+ * `@thatopen/ui-obc`). Only `OBC` and `BUI` are strictly required by
195
+ * `setup()`; passing everything you import is the safe default, because
196
+ * a built-in that needs a missing one fails at init.
191
197
  *
192
198
  * @example
193
199
  * ```ts
194
200
  * import * as OBC from "@thatopen/components";
195
201
  * import * as OBF from "@thatopen/components-front";
196
202
  * import * as BUI from "@thatopen/ui";
197
- * import * as CUI from "@thatopen/ui-obc";
198
203
  * import * as THREE from "three";
199
204
  * import * as FRAGS from "@thatopen/fragments";
205
+ * import * as MARKERJS from "@markerjs/markerjs3";
200
206
  *
201
- * client.setBuiltInGlobals({ OBC, OBF, BUI, CUI, THREE, FRAGS });
207
+ * client.setBuiltInGlobals({ OBC, OBF, BUI, THREE, FRAGS, MARKERJS });
202
208
  *
203
209
  * // Now just pass the component — no globals needed:
204
210
  * await client.initBuiltInComponent(AppManager, components);
@@ -218,8 +224,8 @@ export declare class EngineServicesClient {
218
224
  */
219
225
  /**
220
226
  * Socket origin without namespace or query, for gateways other than the
221
- * execution one. `wsUrl` already carries a token that may be stale when a
222
- * provider is in play, so callers append their own.
227
+ * execution one. Callers pass their own credential in the Socket.IO auth
228
+ * payload, resolved at connect time so a provider-backed client is current.
223
229
  */
224
230
  protected get socketOrigin(): string;
225
231
  protected resolveAccessToken(): Promise<string>;
@@ -625,6 +631,18 @@ export declare class EngineServicesClient {
625
631
  * @param onUpdateCallback - Callback invoked on each progress/result event.
626
632
  */
627
633
  onExecutionProgress(executionId: string, onUpdateCallback: (data: ExecutionSuscriptionReturnType) => void): Promise<void>;
634
+ /**
635
+ * A project's history of connected documents (Revit models, AutoCAD
636
+ * drawings…): their version timelines and their change requests. See
637
+ * `src/core/history.ts` — this surface is the only supported way to read
638
+ * or write it; the storage underneath is private and will move.
639
+ */
640
+ history(projectId: string): ProjectHistory;
641
+ /**
642
+ * The recipe registry and runner: scripts that write change requests in a
643
+ * native application's language. See `src/core/recipes.ts`.
644
+ */
645
+ get recipes(): Recipes;
628
646
  /**
629
647
  * Creates a hidden file attached to a parent item (e.g., dependencies, assets).
630
648
  * @param file - The file to upload.
@@ -0,0 +1,4 @@
1
+ import { EngineServicesClient } from '../client';
2
+
3
+ export declare function readTheProjectHistory(client: EngineServicesClient, projectId: string): Promise<void>;
4
+ export declare function runARecipe(client: EngineServicesClient, projectId: string): Promise<void>;
@@ -0,0 +1,204 @@
1
+ import { EngineServicesClient } from './client';
2
+
3
+ /** One element mutation inside a version. Wire-compatible with the plugins. */
4
+ export interface FlowChange {
5
+ /** create | update | delete. */
6
+ type: 'create' | 'update' | 'delete' | string;
7
+ /** The native application's own id for the element (Revit UniqueId, AutoCAD handle). */
8
+ uniqueId: string;
9
+ }
10
+ /** Per-version change tallies. */
11
+ export interface FlowCounts {
12
+ added: number;
13
+ modified: number;
14
+ removed: number;
15
+ }
16
+ /**
17
+ * One sync of a document. Field names are the wire format the plugins write
18
+ * (`revitflow_history.json`); do not rename them.
19
+ */
20
+ export interface FlowVersion {
21
+ /** Identity, minted by the writer. */
22
+ guid: string;
23
+ /** The versions this one was built on; absent on a root. First entry is the continued line. */
24
+ parents?: string[] | null;
25
+ /** The CDE's storage version number, when there is one. Data, never identity. */
26
+ version?: number;
27
+ /** What the author said it was for, when they said anything. */
28
+ message?: string;
29
+ author: string;
30
+ /** Which application wrote it: 'revit', 'autocad', 'rhino'… */
31
+ source: string;
32
+ /** ISO UTC. */
33
+ timestamp: string;
34
+ changes: FlowChange[];
35
+ counts: FlowCounts;
36
+ /** Hidden-file id of the delta .frag; absent on the baseline and geometry-less versions. */
37
+ deltaId?: string;
38
+ /** Hidden-file id of the per-element geometry fingerprints, when the writer keeps them. */
39
+ geomId?: string;
40
+ /** Platform user id of the committer, when the writer knew it. */
41
+ userId?: string;
42
+ /** This version re-exported the whole model (the visible .frag shows it as of here). */
43
+ full?: boolean;
44
+ compact?: boolean;
45
+ /** Suggestions this version applied (their opIds). */
46
+ appliedOps?: string[];
47
+ /** Position in the timeline, derived from the parent graph when read. Never stored. */
48
+ ordinal?: number;
49
+ }
50
+ /**
51
+ * A change request queued for a document. Wire-compatible with
52
+ * `revitflow_ops.json` (schema `revitflow/ops@1`).
53
+ */
54
+ export interface FlowSuggestion {
55
+ opId: string;
56
+ /** Groups the suggestions one authored action produced across several docs. */
57
+ batchId?: string;
58
+ /** Hidden-file id of the payload .frag (envelope `revitflow/op@2`). */
59
+ fragId?: string;
60
+ title?: string;
61
+ /** add | set | remove | mixed. */
62
+ verb?: string;
63
+ author?: string;
64
+ userId?: string;
65
+ createdAt?: string;
66
+ /** The version this was authored against, by guid. */
67
+ baseCommitGuid?: string;
68
+ /** The storage version that version landed in, when known. Data only. */
69
+ baseCommit?: number;
70
+ /** How many elements it targets. */
71
+ targets?: number;
72
+ state?: 'pending' | 'applied' | 'discarded' | string;
73
+ /** The native version that materialised it, once applied (storage version number). */
74
+ appliedInCommit?: number | null;
75
+ /** Native ids it touched or created, written by the plugin when it applied. */
76
+ affectedIds?: string[];
77
+ }
78
+ /** A connected document, as listed from the project. */
79
+ export interface FlowDocInfo {
80
+ /** The native application that owns it: 'revit', 'autocad'… */
81
+ source: string;
82
+ /** The document's own id within that application. */
83
+ docId: string;
84
+ /** The visible model file's name, when one exists yet. */
85
+ modelName?: string;
86
+ }
87
+ /** What `sync` needs: the change list plus whatever the writer knows. */
88
+ export interface SyncInput {
89
+ author: string;
90
+ /** Which application/tool is writing: 'revit', 'rhino', 'recipe'… */
91
+ source: string;
92
+ changes: FlowChange[];
93
+ message?: string;
94
+ /** Delta geometry for this version, uploaded as a hidden file. */
95
+ delta?: Blob;
96
+ /** Re-upload of the whole model; marks the version `full`. */
97
+ model?: Blob;
98
+ /** Platform user id, when known. */
99
+ userId?: string;
100
+ /** Suggestions this version applied. */
101
+ appliedOps?: string[];
102
+ /**
103
+ * The head you built on. When given and the stored head differs, `sync`
104
+ * refuses with {@link StaleHistoryError} instead of forking silently.
105
+ */
106
+ expectedLatestGuid?: string;
107
+ }
108
+ /** What `suggest` needs. */
109
+ export interface SuggestInput {
110
+ title: string;
111
+ /** add | set | remove | mixed. */
112
+ verb: string;
113
+ author: string;
114
+ /** The suggestion's .frag payload (envelope `revitflow/op@2`), when it has one. */
115
+ payload?: Blob;
116
+ targets?: number;
117
+ batchId?: string;
118
+ userId?: string;
119
+ /** The version it was authored against. */
120
+ baseCommitGuid?: string;
121
+ }
122
+ /** The head moved while a writer prepared its version. Re-read and retry. */
123
+ export declare class StaleHistoryError extends Error {
124
+ readonly expectedGuid: string;
125
+ readonly actualGuid: string | undefined;
126
+ constructor(expectedGuid: string, actualGuid: string | undefined);
127
+ }
128
+ /**
129
+ * The history of one project. Get it from {@link EngineServicesClient.history}.
130
+ */
131
+ export declare class ProjectHistory {
132
+ #private;
133
+ private readonly client;
134
+ readonly projectId: string;
135
+ constructor(client: EngineServicesClient, projectId: string);
136
+ /** The project's connected documents. */
137
+ docs(): Promise<FlowDocInfo[]>;
138
+ /** One document's history. `source` and `docId` as {@link docs} lists them. */
139
+ doc(source: string, docId: string): FlowDoc;
140
+ /** @internal The `bimterop` folder id, resolved once. */
141
+ rootFolderId(create?: boolean): Promise<string | undefined>;
142
+ }
143
+ /**
144
+ * One connected document: its timeline, its model, and its suggestions.
145
+ */
146
+ export declare class FlowDoc {
147
+ #private;
148
+ private readonly client;
149
+ private readonly project;
150
+ readonly source: string;
151
+ readonly docId: string;
152
+ constructor(client: EngineServicesClient, project: ProjectHistory, source: string, docId: string);
153
+ /**
154
+ * The document's versions, oldest first, ordered by the parent graph (not
155
+ * by array position, which concurrent writers do not keep meaningful).
156
+ * Each version gets its `ordinal` filled in.
157
+ */
158
+ timeline(): Promise<FlowVersion[]>;
159
+ /** The head of the timeline, or null before the first sync. */
160
+ latest(): Promise<FlowVersion | null>;
161
+ /**
162
+ * A version's delta geometry as a .frag. The baseline (`full`) versions have
163
+ * no delta: their geometry IS the model file — use {@link model}.
164
+ */
165
+ delta(version: FlowVersion): Promise<Blob>;
166
+ /** The visible model .frag as last fully exported. */
167
+ model(): Promise<Blob>;
168
+ /**
169
+ * Appends one version: uploads the geometry it carries, then the updated
170
+ * history. Give `expectedLatestGuid` whenever you built on a head you read,
171
+ * so a concurrent writer surfaces as {@link StaleHistoryError} instead of a
172
+ * silent fork.
173
+ */
174
+ sync(input: SyncInput): Promise<FlowVersion>;
175
+ /** Every suggestion in the document's queue, pending or resolved. */
176
+ suggestions(): Promise<FlowSuggestion[]>;
177
+ /** The suggestions still waiting for the owner, oldest first. */
178
+ pendingSuggestions(): Promise<FlowSuggestion[]>;
179
+ /**
180
+ * Queues a change request for this document. The payload, when given, is
181
+ * uploaded as a hidden file under the suggestions index.
182
+ */
183
+ suggest(input: SuggestInput): Promise<FlowSuggestion>;
184
+ /** A suggestion's .frag payload. */
185
+ suggestionPayload(suggestion: FlowSuggestion): Promise<Blob>;
186
+ /**
187
+ * Marks a suggestion applied (naming the version that materialised it) or
188
+ * discarded. The native plugins do this themselves when they apply; apps
189
+ * use it for `discarded`.
190
+ */
191
+ resolveSuggestion(opId: string, outcome: {
192
+ state: 'applied';
193
+ appliedInCommit?: number;
194
+ affectedIds?: string[];
195
+ } | {
196
+ state: 'discarded';
197
+ }): Promise<FlowSuggestion>;
198
+ }
199
+ /**
200
+ * Orders versions by walking the parent graph from the root along first
201
+ * parents, breaking forks by timestamp — array position is not meaningful
202
+ * once more than one application writes. Fills `ordinal`.
203
+ */
204
+ export declare function orderByGraph(versions: FlowVersion[]): FlowVersion[];
@@ -1,5 +1,5 @@
1
1
  import { EngineServicesClient, EngineServicesClientProps } from './client';
2
- import { Project, ProjectData } from '../types/projects';
2
+ import { Project, ProjectData, ProjectWithRole } from '../types/projects';
3
3
  import { ChannelClient } from './channel';
4
4
  import { MarkNotificationsReadResultDto, NotificationPageDto, NotificationSubscriptionView } from '../types/notifications';
5
5
 
@@ -129,6 +129,22 @@ export declare class PlatformClient extends EngineServicesClient {
129
129
  * ```
130
130
  */
131
131
  get channel(): ChannelClient;
132
+ /**
133
+ * Lists the caller's projects, each with their role in it. The discovery
134
+ * call: everything else on this surface takes a projectId, and until this
135
+ * method there was no way to find out which ids exist — a portfolio
136
+ * dashboard had to be configured by hand. `ProjectWithRole` existed as a
137
+ * type with no producer; this is the producer.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const projects = await client.listProjects();
142
+ * for (const { project, role } of projects) {
143
+ * console.log(project.title, role?.name ?? 'no role');
144
+ * }
145
+ * ```
146
+ */
147
+ listProjects(): Promise<ProjectWithRole[]>;
132
148
  /**
133
149
  * Gets a project by ID. JWT-only — lives here because
134
150
  * `GET /project/:id` is guarded by `AccountActiveGuard + ProjectAccessGuard`.
@@ -0,0 +1,98 @@
1
+ import { EngineServicesClient } from './client';
2
+ import { FlowDoc, FlowSuggestion } from './history';
3
+
4
+ /** What a recipe declares about itself. Shown in catalogs, checked by apps. */
5
+ export interface RecipeManifest {
6
+ /** Machine name, unique in a catalog: 'rhino-points-to-revit-columns'. */
7
+ name: string;
8
+ /** The one sentence a non-programmer reads: what it converts into what. */
9
+ description: string;
10
+ /** The native application whose language it writes: 'revit', 'autocad'… */
11
+ to: string;
12
+ /** The input kinds it understands: 'rhino-model', 'platform-elements', 'points', 'csv'… */
13
+ inputs: string[];
14
+ /** Where it runs by default. Browser unless it must run unattended. */
15
+ runtime?: 'browser' | 'cloud';
16
+ /**
17
+ * JSON Schema for `settings`. Apps generate the settings form from it, so
18
+ * anything that names an EXISTING thing (a target document, a family, a
19
+ * level) must be an enum the app can fill — people pick, they never type
20
+ * names of things that exist.
21
+ */
22
+ settingsSchema?: Record<string, unknown>;
23
+ /**
24
+ * The self-documentation an app renders: a "reads → proposes" table whose
25
+ * rows are RULES (not objects), so a dry run can fill it with real tallies.
26
+ */
27
+ rules?: {
28
+ reads: string;
29
+ proposes: string;
30
+ }[];
31
+ }
32
+ /** Everything a recipe's `run` gets to work with. */
33
+ export interface RecipeContext<Settings = Record<string, unknown>> {
34
+ client: EngineServicesClient;
35
+ projectId: string;
36
+ /** The document the suggestion is for. Read its timeline/model if needed. */
37
+ targetDoc: FlowDoc;
38
+ /** The declared input, already fetched by the caller (bytes, ids, rows…). */
39
+ input: unknown;
40
+ settings: Settings;
41
+ }
42
+ /** What a recipe's `run` returns: the suggestion, ready to queue. */
43
+ export interface RecipeResult {
44
+ title: string;
45
+ /** add | set | remove | mixed. */
46
+ verb: string;
47
+ /** The .frag payload (envelope `revitflow/op@2`), when the recipe builds one. */
48
+ payload?: Blob;
49
+ targets?: number;
50
+ /** Per-rule tallies matching `manifest.rules`, filled by the run. */
51
+ ruleTallies?: {
52
+ read: number;
53
+ proposed: number;
54
+ }[];
55
+ }
56
+ /** A recipe: its manifest plus the function that writes the suggestion. */
57
+ export interface Recipe<Settings = Record<string, unknown>> extends RecipeManifest {
58
+ run(context: RecipeContext<Settings>): Promise<RecipeResult>;
59
+ }
60
+ /** What {@link Recipes.run} returns: the queued suggestion plus the tallies. */
61
+ export interface RecipeRunOutcome {
62
+ suggestion: FlowSuggestion;
63
+ result: RecipeResult;
64
+ }
65
+ /**
66
+ * The local recipe registry and runner. Reach it as `client.recipes`.
67
+ */
68
+ export declare class Recipes {
69
+ #private;
70
+ private readonly client;
71
+ constructor(client: EngineServicesClient);
72
+ /** Registers a recipe. One per name; a second with the same name throws. */
73
+ register<S>(recipe: Recipe<S>): void;
74
+ /** The registered recipes, optionally filtered by target application. */
75
+ list(filter?: {
76
+ to?: string;
77
+ input?: string;
78
+ }): RecipeManifest[];
79
+ /** A registered recipe by name, or undefined. */
80
+ get(name: string): RecipeManifest | undefined;
81
+ /**
82
+ * Runs a recipe and queues its suggestion on the target document. Safe to
83
+ * call freely: nothing touches the native application until its owner
84
+ * accepts the suggestion there.
85
+ */
86
+ run<S>(name: string, options: {
87
+ projectId: string;
88
+ /** The target document, as `client.history(projectId).docs()` lists it. */
89
+ target: {
90
+ source: string;
91
+ docId: string;
92
+ };
93
+ input?: unknown;
94
+ settings?: S;
95
+ author: string;
96
+ userId?: string;
97
+ }): Promise<RecipeRunOutcome>;
98
+ }