@thatopen/services 0.6.0 → 0.8.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 (73) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +485 -100
  3. package/dist/cli/lib/revit-addin.d.ts +8 -0
  4. package/dist/cli.js +135 -24
  5. package/dist/index.cjs.js +2 -2
  6. package/dist/index.es.js +41 -50
  7. package/dist/vite-env.d.ts +1 -1
  8. package/docs/ai-quickstart.md +138 -4
  9. package/docs/cli/create.md +29 -29
  10. package/docs/cli/local-server.md +38 -38
  11. package/docs/cli/login.md +32 -32
  12. package/docs/cli/publish.md +31 -31
  13. package/docs/cli/run.md +26 -26
  14. package/docs/cli/serve.md +43 -43
  15. package/docs/cli/swap.md +30 -30
  16. package/docs/flow-architecture.md +1 -1
  17. package/docs/flow-plugin-guide.md +27 -1
  18. package/docs/platform-token.md +81 -0
  19. package/docs/revit-collab-quickstart.md +115 -35
  20. package/docs/rhino-interop-quickstart.md +458 -0
  21. package/package.json +1 -1
  22. package/resources/AGENTS.md +1 -1
  23. package/src/built-in/CDEManager/example.ts +214 -214
  24. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  25. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  26. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  27. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  28. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  29. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  30. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  31. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  32. package/src/built-in/UIManager/src/app/example.ts +97 -97
  33. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  34. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  35. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  36. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  37. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  38. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  39. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  40. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  41. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  42. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  43. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
  44. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  45. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  46. package/src/built-in/index.ts +485 -100
  47. package/src/cli/templates/app/index.html +35 -35
  48. package/src/cli/templates/app/package.json +32 -32
  49. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  50. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  51. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  52. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  53. package/src/cli/templates/app/src/globals.ts +1 -1
  54. package/src/cli/templates/app/src/main.history.ts +191 -0
  55. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  56. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  57. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  58. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  59. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  60. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  61. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  62. package/src/cli/templates/app/tsconfig.json +16 -16
  63. package/src/cli/templates/app/vite.config.js +57 -57
  64. package/src/cli/templates/cloud-component/README.md +205 -205
  65. package/src/cli/templates/cloud-component/_thatopen +5 -5
  66. package/src/cli/templates/cloud-component/declarations.json +4 -4
  67. package/src/cli/templates/cloud-component/package.json +21 -21
  68. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  69. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  70. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  71. package/src/cli/templates/shared/AGENTS.md +8 -8
  72. package/src/cli/templates/shared/CLAUDE.md +1 -1
  73. package/src/cli/templates/shared/_gitignore +5 -5
package/dist/index.es.js CHANGED
@@ -3,8 +3,8 @@ var we = (n) => {
3
3
  throw TypeError(n);
4
4
  };
5
5
  var Ve = (n, e, t) => e in n ? Me(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
6
- var w = (n, e, t) => Ve(n, typeof e != "symbol" ? e + "" : e, t), J = (n, e, t) => e.has(n) || we("Cannot " + t);
7
- var Y = (n, e, t) => (J(n, e, "read from private field"), t ? t.call(n) : e.get(n)), K = (n, e, t) => e.has(n) ? we("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(n) : e.set(n, t), _e = (n, e, t, s) => (J(n, e, "write to private field"), s ? s.call(n, t) : e.set(n, t), t), c = (n, e, t) => (J(n, e, "access private method"), t);
6
+ var w = (n, e, t) => Ve(n, typeof e != "symbol" ? e + "" : e, t), j = (n, e, t) => e.has(n) || we("Cannot " + t);
7
+ var Y = (n, e, t) => (j(n, e, "read from private field"), t ? t.call(n) : e.get(n)), K = (n, e, t) => e.has(n) ? we("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(n) : e.set(n, t), _e = (n, e, t, s) => (j(n, e, "write to private field"), s ? s.call(n, t) : e.set(n, t), t), c = (n, e, t) => (j(n, e, "access private method"), t);
8
8
  const v = /* @__PURE__ */ Object.create(null);
9
9
  v.open = "0";
10
10
  v.close = "1";
@@ -47,7 +47,7 @@ const Ge = (n) => {
47
47
  for (s = 0; s < t; s += 4)
48
48
  i = P[n.charCodeAt(s)], o = P[n.charCodeAt(s + 1)], h = P[n.charCodeAt(s + 2)], l = P[n.charCodeAt(s + 3)], d[r++] = i << 2 | o >> 4, d[r++] = (o & 15) << 4 | h >> 2, d[r++] = (h & 3) << 6 | l & 63;
49
49
  return y;
50
- }, je = typeof ArrayBuffer == "function", fe = (n, e) => {
50
+ }, Je = typeof ArrayBuffer == "function", fe = (n, e) => {
51
51
  if (typeof n != "string")
52
52
  return {
53
53
  type: "message",
@@ -56,15 +56,15 @@ const Ge = (n) => {
56
56
  const t = n.charAt(0);
57
57
  return t === "b" ? {
58
58
  type: "message",
59
- data: Je(n.substring(1), e)
59
+ data: je(n.substring(1), e)
60
60
  } : U[t] ? n.length > 1 ? {
61
61
  type: U[t],
62
62
  data: n.substring(1)
63
63
  } : {
64
64
  type: U[t]
65
65
  } : te;
66
- }, Je = (n, e) => {
67
- if (je) {
66
+ }, je = (n, e) => {
67
+ if (Je) {
68
68
  const t = Ge(n);
69
69
  return $e(t, e);
70
70
  } else
@@ -224,7 +224,7 @@ function xe(n, ...e) {
224
224
  return e.reduce((t, s) => (n.hasOwnProperty(s) && (t[s] = n[s]), t), {});
225
225
  }
226
226
  const Ze = _.setTimeout, et = _.clearTimeout;
227
- function j(n, e) {
227
+ function J(n, e) {
228
228
  e.useNativeTimers ? (n.setTimeoutFn = Ze.bind(_), n.clearTimeoutFn = et.bind(_)) : (n.setTimeoutFn = _.setTimeout.bind(_), n.clearTimeoutFn = _.clearTimeout.bind(_));
229
229
  }
230
230
  const tt = 1.33;
@@ -267,7 +267,7 @@ class pe extends g {
267
267
  * @protected
268
268
  */
269
269
  constructor(e) {
270
- super(), this.writable = !1, j(this, e), this.opts = e, this.query = e.query, this.socket = e.socket, this.supportsBinary = !e.forceBase64;
270
+ super(), this.writable = !1, J(this, e), this.opts = e, this.query = e.query, this.socket = e.socket, this.supportsBinary = !e.forceBase64;
271
271
  }
272
272
  /**
273
273
  * Emits an error.
@@ -474,8 +474,8 @@ class ut extends at {
474
474
  /**
475
475
  * Sends data.
476
476
  *
477
- * @param {String} data - data to send.
478
- * @param {Function} fn - called upon flush.
477
+ * @param {String} data to send.
478
+ * @param {Function} called upon flush.
479
479
  * @private
480
480
  */
481
481
  doWrite(e, t) {
@@ -507,7 +507,7 @@ class T extends g {
507
507
  * @package
508
508
  */
509
509
  constructor(e, t, s) {
510
- super(), this.createRequest = e, j(this, s), this._opts = s, this._method = s.method || "GET", this._uri = t, this._data = s.data !== void 0 ? s.data : null, this._create();
510
+ super(), this.createRequest = e, J(this, s), this._opts = s, this._method = s.method || "GET", this._uri = t, this._data = s.data !== void 0 ? s.data : null, this._create();
511
511
  }
512
512
  /**
513
513
  * Creates the XHR object and sends the request.
@@ -807,7 +807,7 @@ class R extends g {
807
807
  const s = se(e);
808
808
  t.hostname = s.host, t.secure = s.protocol === "https" || s.protocol === "wss", t.port = s.port, s.query && (t.query = s.query);
809
809
  } else t.host && (t.hostname = se(t.host).host);
810
- j(this, t), this.secure = t.secure != null ? t.secure : typeof location < "u" && location.protocol === "https:", t.hostname && !t.port && (t.port = this.secure ? "443" : "80"), this.hostname = t.hostname || (typeof location < "u" ? location.hostname : "localhost"), this.port = t.port || (typeof location < "u" && location.port ? location.port : this.secure ? "443" : "80"), this.transports = [], this._transportsByName = {}, t.transports.forEach((s) => {
810
+ J(this, t), this.secure = t.secure != null ? t.secure : typeof location < "u" && location.protocol === "https:", t.hostname && !t.port && (t.port = this.secure ? "443" : "80"), this.hostname = t.hostname || (typeof location < "u" ? location.hostname : "localhost"), this.port = t.port || (typeof location < "u" && location.port ? location.port : this.secure ? "443" : "80"), this.transports = [], this._transportsByName = {}, t.transports.forEach((s) => {
811
811
  const r = s.prototype.name;
812
812
  this.transports.push(r), this._transportsByName[r] = s;
813
813
  }), this.opts = Object.assign({
@@ -1008,7 +1008,7 @@ class R extends g {
1008
1008
  /**
1009
1009
  * Sends a packet.
1010
1010
  *
1011
- * @param {String} type - packet type.
1011
+ * @param {String} type: packet type.
1012
1012
  * @param {String} data.
1013
1013
  * @param {Object} options.
1014
1014
  * @param {Function} fn - callback function.
@@ -1140,8 +1140,8 @@ class Et extends R {
1140
1140
  }
1141
1141
  let Tt = class extends Et {
1142
1142
  constructor(e, t = {}) {
1143
- const s = typeof e == "object", r = s ? { ...e } : { ...t };
1144
- (!r.transports || r.transports && typeof r.transports[0] == "string") && (r.transports = (r.transports || ["polling", "websocket", "webtransport"]).map((i) => mt[i]).filter((i) => !!i)), super(s ? r : e, r);
1143
+ const s = typeof e == "object" ? e : t;
1144
+ (!s.transports || s.transports && typeof s.transports[0] == "string") && (s.transports = (s.transports || ["polling", "websocket", "webtransport"]).map((r) => mt[r]).filter((r) => !!r)), super(e, s);
1145
1145
  }
1146
1146
  };
1147
1147
  function vt(n, e = "", t) {
@@ -1274,12 +1274,11 @@ class Bt {
1274
1274
  class ye extends g {
1275
1275
  /**
1276
1276
  * Decoder constructor
1277
+ *
1278
+ * @param {function} reviver - custom reviver to pass down to JSON.stringify
1277
1279
  */
1278
1280
  constructor(e) {
1279
- super(), this.opts = Object.assign({
1280
- reviver: void 0,
1281
- maxAttachments: 10
1282
- }, typeof e == "function" ? { reviver: e } : e);
1281
+ super(), this.reviver = e;
1283
1282
  }
1284
1283
  /**
1285
1284
  * Decodes an encoded packet string into packet JSON.
@@ -1322,12 +1321,7 @@ class ye extends g {
1322
1321
  const o = e.substring(i, t);
1323
1322
  if (o != Number(o) || e.charAt(t) !== "-")
1324
1323
  throw new Error("Illegal attachments");
1325
- const h = Number(o);
1326
- if (!Nt(h) || h < 0)
1327
- throw new Error("Illegal attachments");
1328
- if (h > this.opts.maxAttachments)
1329
- throw new Error("too many attachments");
1330
- s.attachments = h;
1324
+ s.attachments = Number(o);
1331
1325
  }
1332
1326
  if (e.charAt(t + 1) === "/") {
1333
1327
  const i = t + 1;
@@ -1361,7 +1355,7 @@ class ye extends g {
1361
1355
  }
1362
1356
  tryParse(e) {
1363
1357
  try {
1364
- return JSON.parse(e, this.opts.reviver);
1358
+ return JSON.parse(e, this.reviver);
1365
1359
  } catch {
1366
1360
  return !1;
1367
1361
  }
@@ -1415,13 +1409,10 @@ class xt {
1415
1409
  this.reconPack = null, this.buffers = [];
1416
1410
  }
1417
1411
  }
1418
- const Nt = Number.isInteger || function(n) {
1419
- return typeof n == "number" && isFinite(n) && Math.floor(n) === n;
1420
- };
1421
1412
  function ke(n) {
1422
1413
  return Object.prototype.toString.call(n) === "[object Object]";
1423
1414
  }
1424
- const Pt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1415
+ const Nt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1425
1416
  __proto__: null,
1426
1417
  Decoder: ye,
1427
1418
  Encoder: Bt,
@@ -1434,7 +1425,7 @@ function b(n, e, t) {
1434
1425
  n.off(e, t);
1435
1426
  };
1436
1427
  }
1437
- const Lt = Object.freeze({
1428
+ const Pt = Object.freeze({
1438
1429
  connect: 1,
1439
1430
  connect_error: 1,
1440
1431
  disconnect: 1,
@@ -1559,7 +1550,7 @@ class Ue extends g {
1559
1550
  */
1560
1551
  emit(e, ...t) {
1561
1552
  var s, r, i;
1562
- if (Lt.hasOwnProperty(e))
1553
+ if (Pt.hasOwnProperty(e))
1563
1554
  throw new Error('"' + e.toString() + '" is a reserved event name');
1564
1555
  if (t.unshift(e), this._opts.retries && !this.flags.fromQueue && !this.flags.volatile)
1565
1556
  return this._addToQueue(t), this;
@@ -2061,12 +2052,12 @@ x.prototype.setJitter = function(n) {
2061
2052
  class oe extends g {
2062
2053
  constructor(e, t) {
2063
2054
  var s;
2064
- super(), this.nsps = {}, this.subs = [], e && typeof e == "object" && (t = e, e = void 0), t = t || {}, t.path = t.path || "/socket.io", this.opts = t, j(this, t), this.reconnection(t.reconnection !== !1), this.reconnectionAttempts(t.reconnectionAttempts || 1 / 0), this.reconnectionDelay(t.reconnectionDelay || 1e3), this.reconnectionDelayMax(t.reconnectionDelayMax || 5e3), this.randomizationFactor((s = t.randomizationFactor) !== null && s !== void 0 ? s : 0.5), this.backoff = new x({
2055
+ super(), this.nsps = {}, this.subs = [], e && typeof e == "object" && (t = e, e = void 0), t = t || {}, t.path = t.path || "/socket.io", this.opts = t, J(this, t), this.reconnection(t.reconnection !== !1), this.reconnectionAttempts(t.reconnectionAttempts || 1 / 0), this.reconnectionDelay(t.reconnectionDelay || 1e3), this.reconnectionDelayMax(t.reconnectionDelayMax || 5e3), this.randomizationFactor((s = t.randomizationFactor) !== null && s !== void 0 ? s : 0.5), this.backoff = new x({
2065
2056
  min: this.reconnectionDelay(),
2066
2057
  max: this.reconnectionDelayMax(),
2067
2058
  jitter: this.randomizationFactor()
2068
2059
  }), this.timeout(t.timeout == null ? 2e4 : t.timeout), this._readyState = "closed", this.uri = e;
2069
- const r = t.parser || Pt;
2060
+ const r = t.parser || Nt;
2070
2061
  this.encoder = new r.Encoder(), this.decoder = new r.Decoder(), this._autoConnect = t.autoConnect !== !1, this._autoConnect && this.open();
2071
2062
  }
2072
2063
  reconnection(e) {
@@ -2309,7 +2300,7 @@ Object.assign(M, {
2309
2300
  io: M,
2310
2301
  connect: M
2311
2302
  });
2312
- function It(n) {
2303
+ function Lt(n) {
2313
2304
  try {
2314
2305
  const e = JSON.parse(n);
2315
2306
  if (e && typeof e == "object") {
@@ -2324,9 +2315,9 @@ function It(n) {
2324
2315
  }
2325
2316
  return {};
2326
2317
  }
2327
- class qt extends Error {
2318
+ class It extends Error {
2328
2319
  constructor(t, s, r) {
2329
- const i = It(r);
2320
+ const i = Lt(r);
2330
2321
  super(i.message ?? `${s || "Request failed"} (${t})`);
2331
2322
  w(this, "status");
2332
2323
  w(this, "code");
@@ -2335,7 +2326,7 @@ class qt extends Error {
2335
2326
  this.name = "RequestError", this.status = t, this.code = i.code, this.details = i.details, this.body = r;
2336
2327
  }
2337
2328
  }
2338
- const E = "item/folder", f = "item", S = "processor", Ut = "npm-registry", C = "hidden", Q = "FILE", Z = "TOOL", ee = "APP";
2329
+ const E = "item/folder", f = "item", S = "processor", qt = "npm-registry", C = "hidden", Q = "FILE", Z = "TOOL", ee = "APP";
2339
2330
  var a, ce, u, $, V, H, he, ue, De;
2340
2331
  const me = class me {
2341
2332
  /**
@@ -2884,7 +2875,7 @@ return main;`)(...h), d = (y == null ? void 0 : y.componentDefinition) ?? y;
2884
2875
  * @returns The registry, scope, read-only token, and assembled `.npmrc`.
2885
2876
  */
2886
2877
  async getNpmCredentials() {
2887
- return await c(this, a, u).call(this, "GET", `${Ut}/credentials`);
2878
+ return await c(this, a, u).call(this, "GET", `${qt}/credentials`);
2888
2879
  }
2889
2880
  // ─── Execution ───────────────────────────────────────────────────
2890
2881
  /**
@@ -3198,7 +3189,7 @@ a = new WeakSet(), ce = function(e) {
3198
3189
  );
3199
3190
  if (!A.ok) {
3200
3191
  const O = await A.text().then((Fe) => Fe).catch(() => "");
3201
- throw new qt(
3192
+ throw new It(
3202
3193
  A.status,
3203
3194
  A.statusText,
3204
3195
  O
@@ -3360,21 +3351,21 @@ const ge = class ge extends ae {
3360
3351
  };
3361
3352
  B = new WeakMap();
3362
3353
  let Ae = ge;
3363
- const Mt = {
3354
+ const Ft = {
3364
3355
  MAX_FIELDS: 200,
3365
3356
  MAX_KEY_LENGTH: 50,
3366
3357
  MAX_VALUE_LENGTH: 50
3367
- }, Vt = { uuid: "a3f82c1d-5b74-4e9a-bc30-d7e12f894a61" }, Ht = { uuid: "2c9f4e8d-3a1b-4c5e-9d7f-0a2b6c8e1d3f" }, Gt = { uuid: "b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c" }, jt = { uuid: "3f9c1a7e-6b2d-4e18-9a5c-7d0e2f4b6c81" }, Jt = { uuid: "2c4ae432-fc24-43e9-9783-0c960c674e96" }, Yt = { uuid: "69af63aa-19ba-4fc7-b17b-75647bac1d76" }, Kt = { uuid: "234f1416-528d-452a-9cc9-a16c5239b2eb" };
3358
+ }, Mt = { uuid: "a3f82c1d-5b74-4e9a-bc30-d7e12f894a61" }, Vt = { uuid: "2c9f4e8d-3a1b-4c5e-9d7f-0a2b6c8e1d3f" }, Ht = { uuid: "b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c" }, Gt = { uuid: "3f9c1a7e-6b2d-4e18-9a5c-7d0e2f4b6c81" }, Jt = { uuid: "2c4ae432-fc24-43e9-9783-0c960c674e96" }, jt = { uuid: "69af63aa-19ba-4fc7-b17b-75647bac1d76" }, Yt = { uuid: "234f1416-528d-452a-9cc9-a16c5239b2eb" };
3368
3359
  export {
3369
- Vt as CDEManager,
3370
- Ht as ClashesManager,
3360
+ Mt as CDEManager,
3361
+ Vt as ClashesManager,
3371
3362
  ae as EngineServicesClient,
3372
- Gt as FileList,
3373
- jt as GitHistoryManager,
3363
+ Ht as FileList,
3364
+ Gt as GitHistoryManager,
3374
3365
  Jt as HelloWorld,
3375
- Mt as METADATA_LIMITS,
3366
+ Ft as METADATA_LIMITS,
3376
3367
  Ae as PlatformClient,
3377
- qt as RequestError,
3378
- Yt as TopicsManager,
3379
- Kt as UIManager
3368
+ It as RequestError,
3369
+ jt as TopicsManager,
3370
+ Yt as UIManager
3380
3371
  };
@@ -1 +1 @@
1
- /// <reference types="vite/client" />
1
+ /// <reference types="vite/client" />
@@ -12,7 +12,7 @@ on the [That Open Platform](https://platform.thatopen.com) from scratch, using
12
12
  > **Not building anything — here to work in Revit?** If what the user actually wants is to share a
13
13
  > Revit model with their team, join one a colleague uploaded, or review a change some other tool
14
14
  > proposed to their model, this is the wrong guide and nothing below applies. Go to
15
- > **[the Revit collaboration quickstart](./revit-collab-quickstart.md)** and follow that instead.
15
+ > **[the Revit collaboration quickstart](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/revit-collab-quickstart.md)** and follow that instead.
16
16
  > It is the same shape as this one: ask first, then drive the CLI.
17
17
  >
18
18
  > Phrases that mean that guide, not this one: *"I want to collaborate on a Revit project"*, *"share
@@ -21,8 +21,8 @@ on the [That Open Platform](https://platform.thatopen.com) from scratch, using
21
21
 
22
22
  > **Making another application talk to this one?** If the user wants their own tool — Rhino, Blender,
23
23
  > a script, an in-house app — to send changes into somebody's model or to show a model it did not
24
- > author, that is a third thing again: **[Writing a flow plugin](./flow-plugin-guide.md)** is the
25
- > byte contract, and **[the architecture notes](./flow-architecture.md)** say why it is shaped that
24
+ > author, that is a third thing again: **[Writing a flow plugin](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-plugin-guide.md)** is the
25
+ > byte contract, and **[the architecture notes](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/flow-architecture.md)** say why it is shaped that
26
26
  > way. Start at its section 0, which sorts the work into three sizes; most integrations are the
27
27
  > smallest one and need no ownership, no history and no collaboration layer.
28
28
  >
@@ -69,6 +69,11 @@ Get the token from the platform dashboard — **https://platform.thatopen.com/da
69
69
  thatopen login --token <platform-token>
70
70
  ```
71
71
 
72
+ **[Logging in to That Open Platform](https://raw.githubusercontent.com/ThatOpen/platform_services/main/docs/platform-token.md)** is the short version of everything that
73
+ goes wrong here: production and dev are separate worlds, `login` does not say which one it saved,
74
+ and a machine that is already logged in is somebody's previous session rather than a decision. Read
75
+ it if the user is not on a fresh machine, or if anything comes back `Unauthorized` or `403`.
76
+
72
77
  This validates the token and stores it in `~/.thatopen/config.json`. Login **must** come
73
78
  first: the next step's install pulls the private `@thatopen-platform/*-beta` packages, and
74
79
  `create` uses your logged-in token to write an authenticated `.npmrc` so that install can
@@ -82,6 +87,16 @@ thatopen create my-app --beta
82
87
  cd my-app
83
88
  ```
84
89
 
90
+ **[app, showing a Revit model's commit history]** If what they asked for is a viewer of the
91
+ revit-flow history, scaffold it directly. Do not build it by hand: the wiring is four additions in
92
+ three places and the ORDER of them is load-bearing (see the boot order section below). This ships
93
+ it already correct, opening on the History layout.
94
+
95
+ ```bash
96
+ thatopen create my-app --beta --history
97
+ cd my-app
98
+ ```
99
+
85
100
  **[component]**
86
101
  ```bash
87
102
  thatopen create my-component -t cloud-component --beta
@@ -105,12 +120,128 @@ npm run dev
105
120
  Serves the app and opens it inside the platform. **You now have a complete, working viewer** —
106
121
  model loading, spatial tree, properties, measurement, sectioning, and more.
107
122
 
123
+ If it does not open by itself, or the user closes the tab, the app you are serving from this
124
+ machine lives at:
125
+
126
+ ```
127
+ https://platform.thatopen.com/dashboard/projects/<PROJECT_ID>/apps/local-app
128
+ ```
129
+
130
+ Substitute the project id. That is the local app running inside the platform's own UI, against
131
+ the project's real data. Send the user there rather than to a bare localhost port, which is the
132
+ app without the platform around it.
133
+
134
+ > **`npx tsc --noEmit` reports errors in a fresh beta scaffold, and they are not real.** You will see
135
+ > a handful of `Cannot find module '@thatopen/components'` and friends. A beta project aliases those
136
+ > imports to the `@thatopen-platform/*-beta` packages in `vite.config.js`, which bare `tsc` does not
137
+ > read — so it is looking for packages this project deliberately does not install.
138
+ >
139
+ > **In this project, `tsc` is not a signal.** Do not try to fix those errors, do not report them as
140
+ > problems you found, and do not use a clean `tsc` as your definition of done. `npm run dev` and
141
+ > `npm run build` are the ones that tell you anything; to check that something you added really
142
+ > shipped, grep the built bundle for it.
143
+
108
144
  **[component]**
109
145
  ```bash
110
146
  npm run run # executes the component locally against the platform (no browser)
111
147
  ```
112
148
  Runs `main()` in a local emulation of the cloud engine so you can iterate before publishing.
113
149
 
150
+ ## THE BOOT ORDER. Read this before you write a line of an app.
151
+
152
+ **This is where apps break, and the error never names the real cause.** An app boots in four
153
+ stages, and a built-in that touches models or the 3D scene must be initialised in the LAST one.
154
+ Call it where it reads naturally, right after `client.setup`, and you get:
155
+
156
+ ```
157
+ Error: FragmentsManager not initialized. Call init() first.
158
+ Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onHighlight')
159
+ Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onClear')
160
+ ```
161
+
162
+ None of which says "you called this too early". The app renders, the panel mounts, and it sits
163
+ there inert.
164
+
165
+ **Why.** `FragmentsManager` is initialised inside `top-app`'s own `setup` callback, and the 3D
166
+ world does not exist until `<top-app>` has been added to the DOM and `top-viewer` has built it.
167
+ Both of those happen well after `client.setup` returns. Any manager that reads model files or
168
+ colours elements reaches for them the moment it starts.
169
+
170
+ **The order, and it is not negotiable:**
171
+
172
+ 1. `client.setup(...)` with every built-in you need, then `components.get(UIManager).init()`
173
+ 2. `app.setup = ...`, which is where `FragmentsManager.init` goes, and append `<top-app>`
174
+ 3. `await firstWorld(...)`, so the world exists
175
+ 4. **only now**, `init(client)` on the model-facing managers
176
+
177
+ Copy this skeleton. It is the boot of an app that works.
178
+
179
+ ```ts
180
+ async function main() {
181
+ const client = PlatformClient.fromPlatformContext();
182
+
183
+ // 1 ── built-ins registered, UI shell up
184
+ const { components } = await client.setup<OBC.Components>(
185
+ { OBC, OBF, BUI, THREE, FRAGS, MARKERJS },
186
+ { uuid: UIManager.uuid },
187
+ { uuid: GitHistoryManager.uuid }, // and any other built-in you use
188
+ );
189
+ components.get(UIManager).init();
190
+
191
+ const viewerEl = document.createElement("top-viewer");
192
+ const app = document.createElement("top-app") as any;
193
+
194
+ // 2 ── this callback is what initialises FragmentsManager. Nothing that needs
195
+ // fragments may run before it has resolved.
196
+ app.setup = (waitUntil: (p: Promise<void>, label?: string) => void) => {
197
+ waitUntil(
198
+ (async () => {
199
+ const fragments = components.get(OBC.FragmentsManager);
200
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
201
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), { classicWorker: true });
202
+ })(),
203
+ "Fragments Core",
204
+ );
205
+ return { components, client };
206
+ };
207
+
208
+ app.elements = { viewer: () => BUI.html`${viewerEl}` };
209
+ app.layouts = { Main: { template: `"viewer" 1fr / 1fr` } };
210
+ app.layout = "Main";
211
+ (document.getElementById("that-open-app") ?? document.body).appendChild(app);
212
+
213
+ // 3 ── the world is created asynchronously by top-viewer. Wait for it.
214
+ await firstWorld(components.get(OBC.Worlds));
215
+
216
+ // 4 ── ONLY NOW. Everything above had to have happened first.
217
+ await components.get(GitHistoryManager).init(client);
218
+
219
+ // ...and from here, the real layouts, panels and elements.
220
+ }
221
+
222
+ /** Resolves with the first world once it exists (top-viewer creates it async). */
223
+ function firstWorld(worlds: any): Promise<any> {
224
+ const existing = [...worlds.list.values()][0];
225
+ if (existing) return Promise.resolve(existing);
226
+ return new Promise((resolve) => {
227
+ const handler = ({ value }: any) => {
228
+ worlds.list.onItemSet.remove(handler);
229
+ resolve(value);
230
+ };
231
+ worlds.list.onItemSet.add(handler);
232
+ });
233
+ }
234
+ ```
235
+
236
+ **Two more traps in the same family:**
237
+
238
+ - **One stable `top-viewer` node, held in a variable and returned by reference** from every
239
+ `elements.viewer()` call. Creating a fresh element on each render disposes the world and
240
+ builds another one, and everything wired to the old one goes quiet.
241
+ - **Registering a built-in is not the same as initialising it.** `client.setup` makes
242
+ `<top-git-history>` resolvable; `init(client)` is what fills it. A panel that mounts and stays
243
+ empty is usually the second one missing, not the first.
244
+
114
245
  ## 5. Then build — read the in-project agent guide
115
246
 
116
247
  The scaffold is real, working code — a complete viewer **[app]** or a runnable `main()`
@@ -148,5 +279,8 @@ component, once added to a project, is then triggered by an app or an automation
148
279
  - **Platform token only.** Never introduce, request, or store an npm token.
149
280
  - **Never echo or persist the user's token.** It belongs only in `~/.thatopen/config.json` /
150
281
  `.npmrc`, both of which the CLI manages and git-ignores.
151
- - **Propose a short plan and get the user's OK** before changing files.
282
+ - **Do the thing they asked for.** If the request already says what to build, build it. Proposing a
283
+ four-point plan and waiting for approval to do what was just requested costs the user a whole turn
284
+ and buys nothing. Stop and ask only when you are about to change files you did not create, when
285
+ the request can be read two ways that mean different work, or when the next step is destructive.
152
286
  - The scaffold already works — **extend it, don't replace it.**
@@ -1,29 +1,29 @@
1
- ---
2
- description: "Scaffold a new app or cloud component project from a template. Covers --template (app for browser BIM apps, cloud-component for server-side components), --beta for switching to private beta engine libraries at scaffold time, and using '.' as the project name to scaffold into the current directory."
3
- ---
4
-
5
- ## thatopen create
6
-
7
- Scaffolds a new ThatOpen project and runs `npm install` automatically.
8
-
9
- **Usage:**
10
- ```bash
11
- thatopen create <project-name> [flags]
12
- thatopen create . # scaffold into the current directory
13
- ```
14
-
15
- **Flags:**
16
-
17
- - `--template <template>`, `-t` — Template to use. Default: `app`.
18
- - `app` — Browser BIM app running inside the platform iframe.
19
- - `cloud-component` — Server-side component executed by the platform's cloud engine and consumed in apps and/or automations.
20
- - `--beta` — Use private beta engine libraries (`@thatopen-platform/*-beta`) instead of the public ones. Sets `beta: true` in `.thatopen` so `serve`, `local-server`, and `vite build` all resolve to beta packages automatically. Requires beta npm access configured (handled automatically once you `thatopen login`).
21
-
22
- > **`--beta` is currently required.** The templates depend on engine APIs that only exist in the beta libraries, so scaffolding without `--beta` errors out for now. Public (non-beta) support is coming with the October release.
23
-
24
- **Examples:**
25
- ```bash
26
- thatopen create my-app --beta
27
- thatopen create my-app -t cloud-component --beta
28
- thatopen create . --beta
29
- ```
1
+ ---
2
+ description: "Scaffold a new app or cloud component project from a template. Covers --template (app for browser BIM apps, cloud-component for server-side components), --beta for switching to private beta engine libraries at scaffold time, and using '.' as the project name to scaffold into the current directory."
3
+ ---
4
+
5
+ ## thatopen create
6
+
7
+ Scaffolds a new ThatOpen project and runs `npm install` automatically.
8
+
9
+ **Usage:**
10
+ ```bash
11
+ thatopen create <project-name> [flags]
12
+ thatopen create . # scaffold into the current directory
13
+ ```
14
+
15
+ **Flags:**
16
+
17
+ - `--template <template>`, `-t` — Template to use. Default: `app`.
18
+ - `app` — Browser BIM app running inside the platform iframe.
19
+ - `cloud-component` — Server-side component executed by the platform's cloud engine and consumed in apps and/or automations.
20
+ - `--beta` — Use private beta engine libraries (`@thatopen-platform/*-beta`) instead of the public ones. Sets `beta: true` in `.thatopen` so `serve`, `local-server`, and `vite build` all resolve to beta packages automatically. Requires beta npm access configured (handled automatically once you `thatopen login`).
21
+
22
+ > **`--beta` is currently required.** The templates depend on engine APIs that only exist in the beta libraries, so scaffolding without `--beta` errors out for now. Public (non-beta) support is coming with the October release.
23
+
24
+ **Examples:**
25
+ ```bash
26
+ thatopen create my-app --beta
27
+ thatopen create my-app -t cloud-component --beta
28
+ thatopen create . --beta
29
+ ```
@@ -1,38 +1,38 @@
1
- ---
2
- description: "Start a local HTTP + Socket.IO server that replicates the platform's cloud execution API on port 4001, with esbuild watch mode. Connect an app to it via EngineServicesClient's localServerUrl option to execute and monitor a local cloud component without publishing."
3
- ---
4
-
5
- ## thatopen local-server
6
-
7
- Starts a local server that mimics the platform's cloud execution API. Runs esbuild in watch mode and rebuilds the component on every save.
8
-
9
- Connect an app to it by passing `localServerUrl` to `EngineServicesClient`:
10
-
11
- ```ts
12
- const client = new EngineServicesClient(token, apiUrl, {
13
- localServerUrl: 'http://localhost:4001'
14
- });
15
- ```
16
-
17
- **Usage:**
18
- ```bash
19
- thatopen local-server [flags]
20
- ```
21
-
22
- **Flags:**
23
-
24
- - `--port <port>` — Port to run the server on. Default: `4001`.
25
- - `--skip-build` — Skip the initial build and use the existing `dist/bundle.js`.
26
-
27
- **Endpoints:**
28
-
29
- - `POST /api/processor/:componentId/execute`
30
- - `GET /api/processor/progress/:executionId`
31
- - `POST /api/processor/progress/:executionId/abort`
32
- - `GET /api/processor/:componentId/progress`
33
-
34
- **Example:**
35
- ```bash
36
- thatopen local-server
37
- thatopen local-server --port 5000
38
- ```
1
+ ---
2
+ description: "Start a local HTTP + Socket.IO server that replicates the platform's cloud execution API on port 4001, with esbuild watch mode. Connect an app to it via EngineServicesClient's localServerUrl option to execute and monitor a local cloud component without publishing."
3
+ ---
4
+
5
+ ## thatopen local-server
6
+
7
+ Starts a local server that mimics the platform's cloud execution API. Runs esbuild in watch mode and rebuilds the component on every save.
8
+
9
+ Connect an app to it by passing `localServerUrl` to `EngineServicesClient`:
10
+
11
+ ```ts
12
+ const client = new EngineServicesClient(token, apiUrl, {
13
+ localServerUrl: 'http://localhost:4001'
14
+ });
15
+ ```
16
+
17
+ **Usage:**
18
+ ```bash
19
+ thatopen local-server [flags]
20
+ ```
21
+
22
+ **Flags:**
23
+
24
+ - `--port <port>` — Port to run the server on. Default: `4001`.
25
+ - `--skip-build` — Skip the initial build and use the existing `dist/bundle.js`.
26
+
27
+ **Endpoints:**
28
+
29
+ - `POST /api/processor/:componentId/execute`
30
+ - `GET /api/processor/progress/:executionId`
31
+ - `POST /api/processor/progress/:executionId/abort`
32
+ - `GET /api/processor/:componentId/progress`
33
+
34
+ **Example:**
35
+ ```bash
36
+ thatopen local-server
37
+ thatopen local-server --port 5000
38
+ ```
package/docs/cli/login.md CHANGED
@@ -1,32 +1,32 @@
1
- ---
2
- description: "Authenticate with the ThatOpen platform by validating an access token against the API. Saves credentials globally (~/.thatopen/config.json) by default, or locally to the project's .thatopen file with --local. Local config takes priority over global."
3
- ---
4
-
5
- ## thatopen login
6
-
7
- Validates an access token against the platform API and saves the credentials. Run this once globally before using any other command, or per-project with `--local`.
8
-
9
- To get a token:
10
- 1. Go to [https://platform.thatopen.com](https://platform.thatopen.com)
11
- 2. In the header, click **Data**
12
- 3. Find the **Tokens** card and create a new token
13
- 4. Enable the permissions: **Apps**, **Components**, and **Storage**
14
- 5. Copy the token
15
-
16
- **Usage:**
17
- ```bash
18
- thatopen login --token <token> [flags]
19
- ```
20
-
21
- **Flags:**
22
-
23
- - `--token <token>` — Access token from the dashboard. Required.
24
- - `--api-url <url>` — Platform API URL. Default: `https://platform.thatopen.com` (production). Pass `https://dev.platform.thatopen.com` to target the dev environment.
25
- - `--local` — Save credentials to `.thatopen` in the current directory instead of the global config. Use this inside a project to override the global token for that project only.
26
-
27
- **Examples:**
28
- ```bash
29
- thatopen login --token my-token
30
- thatopen login --token my-token --local
31
- npm run login -- --token my-token # from inside a scaffolded project
32
- ```
1
+ ---
2
+ description: "Authenticate with the ThatOpen platform by validating an access token against the API. Saves credentials globally (~/.thatopen/config.json) by default, or locally to the project's .thatopen file with --local. Local config takes priority over global."
3
+ ---
4
+
5
+ ## thatopen login
6
+
7
+ Validates an access token against the platform API and saves the credentials. Run this once globally before using any other command, or per-project with `--local`.
8
+
9
+ To get a token:
10
+ 1. Go to [https://platform.thatopen.com](https://platform.thatopen.com)
11
+ 2. In the header, click **Data**
12
+ 3. Find the **Tokens** card and create a new token
13
+ 4. Enable the permissions: **Apps**, **Components**, and **Storage**
14
+ 5. Copy the token
15
+
16
+ **Usage:**
17
+ ```bash
18
+ thatopen login --token <token> [flags]
19
+ ```
20
+
21
+ **Flags:**
22
+
23
+ - `--token <token>` — Access token from the dashboard. Required.
24
+ - `--api-url <url>` — Platform API URL. Default: `https://platform.thatopen.com` (production). Pass `https://dev.platform.thatopen.com` to target the dev environment.
25
+ - `--local` — Save credentials to `.thatopen` in the current directory instead of the global config. Use this inside a project to override the global token for that project only.
26
+
27
+ **Examples:**
28
+ ```bash
29
+ thatopen login --token my-token
30
+ thatopen login --token my-token --local
31
+ npm run login -- --token my-token # from inside a scaffolded project
32
+ ```