@ifc-lite/flow-nodes 0.3.1 → 0.5.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 (94) hide show
  1. package/README.md +1 -0
  2. package/dist/aps-client.d.ts +55 -0
  3. package/dist/aps-client.d.ts.map +1 -0
  4. package/dist/aps-client.js +194 -0
  5. package/dist/aps-client.js.map +1 -0
  6. package/dist/aps-nodes.d.ts +3 -0
  7. package/dist/aps-nodes.d.ts.map +1 -0
  8. package/dist/aps-nodes.js +192 -0
  9. package/dist/aps-nodes.js.map +1 -0
  10. package/dist/aps-table.d.ts +24 -0
  11. package/dist/aps-table.d.ts.map +1 -0
  12. package/dist/aps-table.js +123 -0
  13. package/dist/aps-table.js.map +1 -0
  14. package/dist/base64.d.ts +8 -0
  15. package/dist/base64.d.ts.map +1 -0
  16. package/dist/base64.js +59 -0
  17. package/dist/base64.js.map +1 -0
  18. package/dist/bcf-client.d.ts +36 -0
  19. package/dist/bcf-client.d.ts.map +1 -0
  20. package/dist/bcf-client.js +108 -0
  21. package/dist/bcf-client.js.map +1 -0
  22. package/dist/bcf-nodes.d.ts +3 -0
  23. package/dist/bcf-nodes.d.ts.map +1 -0
  24. package/dist/bcf-nodes.js +181 -0
  25. package/dist/bcf-nodes.js.map +1 -0
  26. package/dist/documents-fetch.d.ts +23 -0
  27. package/dist/documents-fetch.d.ts.map +1 -0
  28. package/dist/documents-fetch.js +48 -0
  29. package/dist/documents-fetch.js.map +1 -0
  30. package/dist/documents-nodes.d.ts +5 -0
  31. package/dist/documents-nodes.d.ts.map +1 -0
  32. package/dist/documents-nodes.js +167 -0
  33. package/dist/documents-nodes.js.map +1 -0
  34. package/dist/host.d.ts +28 -0
  35. package/dist/host.d.ts.map +1 -1
  36. package/dist/host.js.map +1 -1
  37. package/dist/http-request-node.d.ts +9 -0
  38. package/dist/http-request-node.d.ts.map +1 -0
  39. package/dist/http-request-node.js +109 -0
  40. package/dist/http-request-node.js.map +1 -0
  41. package/dist/index.d.ts +20 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +34 -5
  44. package/dist/index.js.map +1 -1
  45. package/dist/model-nodes.d.ts.map +1 -1
  46. package/dist/model-nodes.js +11 -6
  47. package/dist/model-nodes.js.map +1 -1
  48. package/dist/open-model-node.d.ts +3 -0
  49. package/dist/open-model-node.d.ts.map +1 -0
  50. package/dist/open-model-node.js +47 -0
  51. package/dist/open-model-node.js.map +1 -0
  52. package/dist/script-node.d.ts.map +1 -1
  53. package/dist/script-node.js +55 -21
  54. package/dist/script-node.js.map +1 -1
  55. package/dist/secrets.d.ts +88 -0
  56. package/dist/secrets.d.ts.map +1 -0
  57. package/dist/secrets.js +202 -0
  58. package/dist/secrets.js.map +1 -0
  59. package/dist/speckle/client.d.ts +52 -0
  60. package/dist/speckle/client.d.ts.map +1 -0
  61. package/dist/speckle/client.js +153 -0
  62. package/dist/speckle/client.js.map +1 -0
  63. package/dist/speckle/geometry.d.ts +37 -0
  64. package/dist/speckle/geometry.d.ts.map +1 -0
  65. package/dist/speckle/geometry.js +126 -0
  66. package/dist/speckle/geometry.js.map +1 -0
  67. package/dist/speckle/mapping.d.ts +62 -0
  68. package/dist/speckle/mapping.d.ts.map +1 -0
  69. package/dist/speckle/mapping.js +208 -0
  70. package/dist/speckle/mapping.js.map +1 -0
  71. package/dist/speckle/parameters.d.ts +49 -0
  72. package/dist/speckle/parameters.d.ts.map +1 -0
  73. package/dist/speckle/parameters.js +95 -0
  74. package/dist/speckle/parameters.js.map +1 -0
  75. package/dist/speckle/refusals.d.ts +45 -0
  76. package/dist/speckle/refusals.d.ts.map +1 -0
  77. package/dist/speckle/refusals.js +40 -0
  78. package/dist/speckle/refusals.js.map +1 -0
  79. package/dist/speckle/units.d.ts +13 -0
  80. package/dist/speckle/units.d.ts.map +1 -0
  81. package/dist/speckle/units.js +59 -0
  82. package/dist/speckle/units.js.map +1 -0
  83. package/dist/speckle/url.d.ts +35 -0
  84. package/dist/speckle/url.d.ts.map +1 -0
  85. package/dist/speckle/url.js +48 -0
  86. package/dist/speckle/url.js.map +1 -0
  87. package/dist/speckle-receive-node.d.ts +9 -0
  88. package/dist/speckle-receive-node.d.ts.map +1 -0
  89. package/dist/speckle-receive-node.js +180 -0
  90. package/dist/speckle-receive-node.js.map +1 -0
  91. package/dist/xlsx-nodes.d.ts.map +1 -1
  92. package/dist/xlsx-nodes.js +1 -40
  93. package/dist/xlsx-nodes.js.map +1 -1
  94. package/package.json +11 -9
package/README.md CHANGED
@@ -13,6 +13,7 @@ in the headless CLI.
13
13
  | `table.*` | fromEntities, longFormat, column, groupRows, pivot, rowCount | Typed columns with IFC value types and pset/prop bindings |
14
14
  | `viewer.*` | colorize, isolate, select, selection, flyTo | `noop` on a headless host; entities pass through |
15
15
  | `model.set*` | setProperty, setAttribute | Capability-checked against the actual pset (`model.mutate:<Pset>`) |
16
+ | `http.request`, `speckle.receive` | network | Every request goes through the gated `coreNetworkRequest` (`network.fetch:<host>` grants, https only); `speckle.receive` writes a Speckle model's walls, floors, roofs, columns and beams, and reports what it cannot map |
16
17
  | `script.run` | one node | JavaScript in the QuickJS sandbox with the sandbox `bim` API |
17
18
 
18
19
  ## Usage
@@ -0,0 +1,55 @@
1
+ import type { Ctx } from './host.js';
2
+ export declare const APS_HOST = "developer.api.autodesk.com";
3
+ export declare const APS_DEFAULT_SCOPE = "data:read viewables:read";
4
+ export declare const APS_REGIONS: readonly ["US", "EMEA", "AUS", "CAN", "DEU", "IND", "JPN", "GBR"];
5
+ /**
6
+ * An opaque APS access token. Only this module can read the bearer string;
7
+ * serialising the handle yields its description, never the token.
8
+ */
9
+ export declare class ApsToken {
10
+ /** `client_credentials` (2-legged, minted here) or `provided` (a 3-legged token passed in). */
11
+ readonly grant: 'client_credentials' | 'provided';
12
+ readonly scope: string;
13
+ /** Epoch ms, when the token endpoint reported `expires_in`. */
14
+ readonly expiresAt: number | null;
15
+ readonly kind = "aps.token";
16
+ constructor(bearer: string,
17
+ /** `client_credentials` (2-legged, minted here) or `provided` (a 3-legged token passed in). */
18
+ grant: 'client_credentials' | 'provided', scope: string,
19
+ /** Epoch ms, when the token endpoint reported `expires_in`. */
20
+ expiresAt: number | null);
21
+ toJSON(): Record<string, unknown>;
22
+ toString(): string;
23
+ }
24
+ interface ApsGetOptions {
25
+ readonly region: string;
26
+ readonly timeoutMs: number;
27
+ readonly maxBytes: number;
28
+ }
29
+ /**
30
+ * A 2-legged token from the client-credentials grant. `clientSecret` is
31
+ * normally a `{{secret:APS_CLIENT_SECRET}}` reference, already interpolated
32
+ * by the CLI/MCP runner; it only ever travels in the request body.
33
+ */
34
+ export declare function requestClientToken(ctx: Ctx, node: string, clientId: string, clientSecret: string, scope: string): Promise<ApsToken>;
35
+ /** Wrap a ready (typically 3-legged) access token without any request. */
36
+ export declare function providedToken(accessToken: string): ApsToken;
37
+ /** Authorised GET against the APS host; returns status and parsed-or-raw body. */
38
+ export declare function apsGet(ctx: Ctx, node: string, token: ApsToken, path: string, opts: ApsGetOptions): Promise<{
39
+ status: number;
40
+ body: string;
41
+ }>;
42
+ /**
43
+ * The Model Derivative URN for a model reference:
44
+ * - a raw `urn:adsk.…` id (a Docs/ACC version id such as
45
+ * `urn:adsk.wipprod:fs.file:vf.…?version=3`, or an OSS object id) is
46
+ * base64url-encoded without padding, as ifc-ai-rendering does;
47
+ * - an already-encoded URN (optionally `urn:`-prefixed, as the viewer
48
+ * writes it) is normalised to the base64url alphabet without padding.
49
+ * A lineage (item) id names no version, so it is refused with a hint.
50
+ */
51
+ export declare function toDerivativeUrn(raw: string): string;
52
+ /** Resolves after `ms`, or rejects as soon as `signal` aborts. */
53
+ export declare function delay(ms: number, signal: AbortSignal | undefined): Promise<void>;
54
+ export {};
55
+ //# sourceMappingURL=aps-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-client.d.ts","sourceRoot":"","sources":["../src/aps-client.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,eAAO,MAAM,QAAQ,+BAA+B,CAAC;AAErD,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAC5D,eAAO,MAAM,WAAW,mEAAoE,CAAC;AAO7F;;;GAGG;AACH,qBAAa,QAAQ;IAKjB,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,UAAU;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IARnC,QAAQ,CAAC,IAAI,eAAe;gBAG1B,MAAM,EAAE,MAAM;IACd,+FAA+F;IACtF,KAAK,EAAE,oBAAoB,GAAG,UAAU,EACxC,KAAK,EAAE,MAAM;IACtB,+DAA+D;IACtD,SAAS,EAAE,MAAM,GAAG,IAAI;IAKnC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIjC,QAAQ,IAAI,MAAM;CAGnB;AAqBD,UAAU,aAAa;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAaD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAC5E,OAAO,CAAC,QAAQ,CAAC,CAyBnB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAE3D;AAED,kFAAkF;AAClF,wBAAsB,MAAM,CAC1B,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GACzE,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAe3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYnD;AAQD,kEAAkE;AAClE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhF"}
@@ -0,0 +1,194 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Autodesk Platform Services (APS) protocol handling for the `aps.*` flow
6
+ * nodes (#5634): OAuth token acquisition and Model Derivative reads.
7
+ *
8
+ * The protocol knowledge is adapted, at the author's request, from the
9
+ * author's own ifc-ai-rendering project (`lib/aps-auth.ts`,
10
+ * `app/api/aps/{viewer-token,viewable,derivative/[urn]}`), which runs these
11
+ * exact calls against live APS:
12
+ * - `POST /authentication/v2/token`, form-encoded, `grant_type=client_credentials`
13
+ * with `client_id`/`client_secret`/`scope` in the body;
14
+ * - the derivative URN is the Docs/ACC version id (or OSS object id),
15
+ * base64url-encoded without padding;
16
+ * - `GET /modelderivative/v2/designdata/{urn}/metadata`, picking the `3d`
17
+ * view, then `.../metadata/{guid}/properties`; `x-ads-region` selects the
18
+ * data centre.
19
+ *
20
+ * Every request goes through `coreNetworkRequest`, so the https-only /
21
+ * exact-host-grant policy of `http.request` applies unchanged: a graph must
22
+ * declare `network.fetch:developer.api.autodesk.com`.
23
+ *
24
+ * The access token never becomes a plain flow value. A token derived from a
25
+ * client secret is not itself a secret the redaction map knows, so a string
26
+ * output would print it in clear on `flow run` / `run_flow`. It travels as an
27
+ * {@link ApsToken} handle instead: the bearer string lives in a module-private
28
+ * `WeakMap`, invisible to `JSON.stringify`, `redactDeep`, digests and
29
+ * inspectors, and every error message is scrubbed of it before it is thrown.
30
+ */
31
+ import { coreNetworkRequest } from '@ifc-lite/sandbox';
32
+ export const APS_HOST = 'developer.api.autodesk.com';
33
+ const APS_BASE = `https://${APS_HOST}`;
34
+ export const APS_DEFAULT_SCOPE = 'data:read viewables:read';
35
+ export const APS_REGIONS = ['US', 'EMEA', 'AUS', 'CAN', 'DEU', 'IND', 'JPN', 'GBR'];
36
+ const TOKEN_TIMEOUT_MS = 15_000;
37
+ const TOKEN_MAX_BYTES = 64 * 1024;
38
+ const bearerByHandle = new WeakMap();
39
+ /**
40
+ * An opaque APS access token. Only this module can read the bearer string;
41
+ * serialising the handle yields its description, never the token.
42
+ */
43
+ export class ApsToken {
44
+ grant;
45
+ scope;
46
+ expiresAt;
47
+ kind = 'aps.token';
48
+ constructor(bearer,
49
+ /** `client_credentials` (2-legged, minted here) or `provided` (a 3-legged token passed in). */
50
+ grant, scope,
51
+ /** Epoch ms, when the token endpoint reported `expires_in`. */
52
+ expiresAt) {
53
+ this.grant = grant;
54
+ this.scope = scope;
55
+ this.expiresAt = expiresAt;
56
+ bearerByHandle.set(this, bearer);
57
+ }
58
+ toJSON() {
59
+ return { kind: this.kind, grant: this.grant, scope: this.scope, expiresAt: this.expiresAt };
60
+ }
61
+ toString() {
62
+ return `[APS token, ${this.grant}]`;
63
+ }
64
+ }
65
+ function bearerOf(token) {
66
+ const bearer = bearerByHandle.get(token);
67
+ // A handle that crossed a structured clone (worker, IPC) loses its bearer on purpose.
68
+ if (bearer === undefined)
69
+ throw new Error('APS token handle has no bearer (was it copied across a process or worker boundary?)');
70
+ return bearer;
71
+ }
72
+ /** `aps.xxx: ` prefixed error, with every occurrence of `bearer` scrubbed. */
73
+ function apsError(node, message, bearer) {
74
+ const clean = bearer && bearer.length > 0 ? message.split(bearer).join('<aps-token>') : message;
75
+ return new Error(`${node}: ${clean}`);
76
+ }
77
+ /** At most `max` characters of an APS error body, for a diagnosable message. */
78
+ function snippet(body, max = 300) {
79
+ const flat = body.replace(/\s+/g, ' ').trim();
80
+ return flat.length > max ? `${flat.slice(0, max)}…` : flat;
81
+ }
82
+ async function send(ctx, node, init, bearer) {
83
+ try {
84
+ return await coreNetworkRequest(init, ctx.host.networkGrants ?? [], ctx.host.networkTransport);
85
+ }
86
+ catch (err) {
87
+ // Denials (ungranted host, redirect) and transport failures alike: prefix and scrub.
88
+ throw apsError(node, err instanceof Error ? err.message : String(err), bearer);
89
+ }
90
+ }
91
+ /**
92
+ * A 2-legged token from the client-credentials grant. `clientSecret` is
93
+ * normally a `{{secret:APS_CLIENT_SECRET}}` reference, already interpolated
94
+ * by the CLI/MCP runner; it only ever travels in the request body.
95
+ */
96
+ export async function requestClientToken(ctx, node, clientId, clientSecret, scope) {
97
+ const body = new URLSearchParams({ grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret, scope }).toString();
98
+ const res = await send(ctx, node, {
99
+ url: `${APS_BASE}/authentication/v2/token`,
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
102
+ body,
103
+ timeoutMs: TOKEN_TIMEOUT_MS,
104
+ maxBytes: TOKEN_MAX_BYTES,
105
+ signal: ctx.signal,
106
+ });
107
+ if (res.status < 200 || res.status >= 300) {
108
+ throw apsError(node, `token request failed (HTTP ${res.status}): ${snippet(res.body)}`);
109
+ }
110
+ let parsed;
111
+ try {
112
+ parsed = JSON.parse(res.body);
113
+ }
114
+ catch {
115
+ throw apsError(node, 'token endpoint returned a non-JSON body');
116
+ }
117
+ if (typeof parsed.access_token !== 'string' || parsed.access_token.length === 0) {
118
+ throw apsError(node, 'token endpoint returned no access_token');
119
+ }
120
+ const expiresAt = typeof parsed.expires_in === 'number' ? Date.now() + parsed.expires_in * 1000 : null;
121
+ return new ApsToken(parsed.access_token, 'client_credentials', scope, expiresAt);
122
+ }
123
+ /** Wrap a ready (typically 3-legged) access token without any request. */
124
+ export function providedToken(accessToken) {
125
+ return new ApsToken(accessToken, 'provided', '', null);
126
+ }
127
+ /** Authorised GET against the APS host; returns status and parsed-or-raw body. */
128
+ export async function apsGet(ctx, node, token, path, opts) {
129
+ const bearer = bearerOf(token);
130
+ const res = await send(ctx, node, {
131
+ url: `${APS_BASE}${path}`,
132
+ method: 'GET',
133
+ headers: { Authorization: `Bearer ${bearer}`, 'x-ads-region': opts.region, Accept: 'application/json' },
134
+ timeoutMs: opts.timeoutMs,
135
+ maxBytes: opts.maxBytes,
136
+ signal: ctx.signal,
137
+ }, bearer);
138
+ if (res.truncated) {
139
+ throw apsError(node, `response for ${path.split('?')[0]} exceeded maxBytes (${opts.maxBytes}); raise the node's maxBytes param`, bearer);
140
+ }
141
+ // The body is APS's, but scrub anyway: a proxy echoing the request must not leak the bearer.
142
+ return { status: res.status, body: res.body.split(bearer).join('<aps-token>') };
143
+ }
144
+ /**
145
+ * The Model Derivative URN for a model reference:
146
+ * - a raw `urn:adsk.…` id (a Docs/ACC version id such as
147
+ * `urn:adsk.wipprod:fs.file:vf.…?version=3`, or an OSS object id) is
148
+ * base64url-encoded without padding, as ifc-ai-rendering does;
149
+ * - an already-encoded URN (optionally `urn:`-prefixed, as the viewer
150
+ * writes it) is normalised to the base64url alphabet without padding.
151
+ * A lineage (item) id names no version, so it is refused with a hint.
152
+ */
153
+ export function toDerivativeUrn(raw) {
154
+ const value = raw.trim();
155
+ if (value.length === 0)
156
+ throw new Error('a model URN is required');
157
+ const body = value.startsWith('urn:') ? value.slice(4) : value;
158
+ if (value.startsWith('urn:') && body.includes(':')) {
159
+ if (body.includes(':dm.lineage:')) {
160
+ throw new Error(`"${value}" is an item (lineage) id, not a version; pass the item's version id (…:fs.file:vf.…?version=N)`);
161
+ }
162
+ return base64Url(value);
163
+ }
164
+ if (!/^[A-Za-z0-9+/_=-]+$/.test(body))
165
+ throw new Error(`"${value}" is neither a urn:adsk… id nor a base64 derivative URN`);
166
+ return body.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
167
+ }
168
+ function base64Url(text) {
169
+ let binary = '';
170
+ for (const byte of new TextEncoder().encode(text))
171
+ binary += String.fromCharCode(byte);
172
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
173
+ }
174
+ /** Resolves after `ms`, or rejects as soon as `signal` aborts. */
175
+ export function delay(ms, signal) {
176
+ if (ms <= 0)
177
+ return signal?.aborted ? Promise.reject(new Error('cancelled')) : Promise.resolve();
178
+ return new Promise((resolve, reject) => {
179
+ if (signal?.aborted) {
180
+ reject(new Error('cancelled'));
181
+ return;
182
+ }
183
+ const timer = setTimeout(() => {
184
+ signal?.removeEventListener('abort', onAbort);
185
+ resolve();
186
+ }, ms);
187
+ const onAbort = () => {
188
+ clearTimeout(timer);
189
+ reject(new Error('cancelled'));
190
+ };
191
+ signal?.addEventListener('abort', onAbort, { once: true });
192
+ });
193
+ }
194
+ //# sourceMappingURL=aps-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-client.js","sourceRoot":"","sources":["../src/aps-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type FlowNodeDef } from './host.js';
2
+ export declare const apsNodes: FlowNodeDef[];
3
+ //# sourceMappingURL=aps-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-nodes.d.ts","sourceRoot":"","sources":["../src/aps-nodes.ts"],"names":[],"mappings":"AAyBA,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AA0G1F,eAAO,MAAM,QAAQ,EAAE,WAAW,EA8EjC,CAAC"}
@@ -0,0 +1,192 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * `aps.token` / `aps.modelProperties` — receive Autodesk Platform Services
6
+ * (APS) model data into a flow graph (#5634), so Revit/ACC properties can be
7
+ * joined to an IFC model with `table.joinByKey`.
8
+ *
9
+ * Model Derivative is the read path, not Data Exchange: it is the path the
10
+ * author's ifc-ai-rendering project runs successfully against live APS for
11
+ * any translated Docs/ACC version, while its Data Exchange route needed
12
+ * region/URN-format fallbacks and hard-coded exchange ids to find anything.
13
+ *
14
+ * Credentials: either chain `aps.token` into `token`, or set the credential
15
+ * params on `aps.modelProperties` itself. The token is an opaque
16
+ * {@link ApsToken} handle (see `aps-client.ts`), never a string, so neither
17
+ * shape can print it. All I/O is `coreNetworkRequest` behind the graph's
18
+ * `network.fetch:developer.api.autodesk.com` grant.
19
+ */
20
+ import { APS_DEFAULT_SCOPE, APS_REGIONS, ApsToken, apsGet, delay, providedToken, requestClientToken, toDerivativeUrn, } from './aps-client.js';
21
+ import { propertiesToTable } from './aps-table.js';
22
+ import { ANY_ITEM, SCALAR_ITEM, TABLE_ITEM } from './host.js';
23
+ const DEFAULT_TIMEOUT_MS = 30_000;
24
+ const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
25
+ const DEFAULT_MAX_ATTEMPTS = 6;
26
+ const DEFAULT_RETRY_DELAY_MS = 5_000;
27
+ const CREDENTIAL_PARAMS = [
28
+ { name: 'clientId', kind: 'string', default: '', doc: 'APS app client id, e.g. {{secret:APS_CLIENT_ID}}. With clientSecret, mints a 2-legged token.' },
29
+ { name: 'clientSecret', kind: 'string', default: '', doc: 'APS app client secret: use {{secret:APS_CLIENT_SECRET}}, never a literal.' },
30
+ { name: 'scope', kind: 'string', default: APS_DEFAULT_SCOPE, doc: 'OAuth scopes for the 2-legged token.' },
31
+ { name: 'accessToken', kind: 'string', default: '', doc: 'A ready (e.g. 3-legged) access token, {{secret:APS_TOKEN}}. Wins over clientId/clientSecret.' },
32
+ ];
33
+ function str(v) {
34
+ return typeof v === 'string' ? v.trim() : '';
35
+ }
36
+ function bounded(value, fallback, min) {
37
+ const n = typeof value === 'number' ? value : typeof value === 'string' && value.trim() !== '' ? Number(value) : Number.NaN;
38
+ return Number.isFinite(n) && n >= min ? n : fallback;
39
+ }
40
+ async function tokenFromParams(ctx, node, p) {
41
+ const accessToken = str(p.accessToken);
42
+ if (accessToken)
43
+ return providedToken(accessToken);
44
+ const clientId = str(p.clientId);
45
+ const clientSecret = str(p.clientSecret);
46
+ if (!clientId || !clientSecret) {
47
+ throw new Error(`${node}: no credentials — set accessToken, or clientId and clientSecret (e.g. {{secret:APS_CLIENT_ID}} / {{secret:APS_CLIENT_SECRET}})`);
48
+ }
49
+ return requestClientToken(ctx, node, clientId, clientSecret, str(p.scope) || APS_DEFAULT_SCOPE);
50
+ }
51
+ async function resolveToken(ctx, node, input, p) {
52
+ if (input instanceof ApsToken)
53
+ return input;
54
+ if (input !== undefined && input !== null) {
55
+ throw new Error(`${node}: "token" input is not an aps.token handle (a token cannot be passed as text or through a serialised value)`);
56
+ }
57
+ return tokenFromParams(ctx, node, p);
58
+ }
59
+ /**
60
+ * GET a Model Derivative resource, waiting out `202 Accepted` (APS is still
61
+ * extracting the property database) for at most `maxAttempts` requests.
62
+ */
63
+ async function getJson(ctx, node, token, path, region, limits, retry, what) {
64
+ for (let attempt = 1;; attempt++) {
65
+ const res = await apsGet(ctx, node, token, path, { region, ...limits });
66
+ if (res.status === 202) {
67
+ if (attempt >= retry.maxAttempts) {
68
+ throw new Error(`${node}: ${what} still processing on APS after ${attempt} attempt(s) (HTTP 202); the derivative is being extracted — re-run later or raise maxAttempts/retryDelayMs`);
69
+ }
70
+ ctx.log('info', `${what}: APS is still processing (HTTP 202), retry ${attempt}/${retry.maxAttempts - 1} in ${retry.retryDelayMs} ms`);
71
+ await delay(retry.retryDelayMs, ctx.signal);
72
+ continue;
73
+ }
74
+ if (res.status >= 200 && res.status < 300) {
75
+ try {
76
+ return JSON.parse(res.body);
77
+ }
78
+ catch {
79
+ throw new Error(`${node}: ${what} returned a non-JSON body`);
80
+ }
81
+ }
82
+ throw new Error(`${node}: ${what} failed — ${describeStatus(res.status)} ${res.body.replace(/\s+/g, ' ').slice(0, 300)}`.trim());
83
+ }
84
+ }
85
+ function describeStatus(status) {
86
+ if (status === 401 || status === 403) {
87
+ return `not authorised (HTTP ${status}): the token lacks data:read/viewables:read, or a 2-legged app is not provisioned for this ACC/BIM 360 hub (use a 3-legged accessToken).`;
88
+ }
89
+ if (status === 404)
90
+ return 'not found (HTTP 404): the URN has no derivatives (not translated yet, wrong region, or wrong URN).';
91
+ if (status === 413)
92
+ return 'too large (HTTP 413): set forceget to true.';
93
+ return `HTTP ${status}:`;
94
+ }
95
+ function pickView(metadata, requested) {
96
+ const list = metadata?.data?.metadata;
97
+ const views = (Array.isArray(list) ? list : []).filter((v) => !!v && typeof v.guid === 'string');
98
+ if (requested) {
99
+ const hit = views.find((v) => v.guid === requested);
100
+ if (!hit)
101
+ throw new Error(`view "${requested}" is not in this model's metadata (views: ${views.map((v) => `${v.guid} ${v.name ?? ''} [${v.role ?? '?'}]`).join(', ') || 'none'})`);
102
+ return hit;
103
+ }
104
+ const threeD = views.filter((v) => v.role === '3d');
105
+ const view = threeD.find((v) => v.isMasterView) ?? threeD[0];
106
+ if (!view)
107
+ throw new Error(`the model has no 3d view (views: ${views.map((v) => `${v.name ?? v.guid} [${v.role ?? '?'}]`).join(', ') || 'none'}); set viewGuid`);
108
+ return view;
109
+ }
110
+ export const apsNodes = [
111
+ {
112
+ type: 'aps.token',
113
+ title: 'APS token',
114
+ category: 'network',
115
+ doc: 'An Autodesk Platform Services access token as an opaque handle for aps.* nodes: 2-legged from clientId/clientSecret, or a ready accessToken (3-legged). The token itself is never output, logged or serialised.',
116
+ inputs: [],
117
+ outputs: [{ name: 'token', type: ANY_ITEM }],
118
+ params: CREDENTIAL_PARAMS,
119
+ capabilities: ['network.fetch:*'],
120
+ requires: { network: true },
121
+ volatile: true,
122
+ run: async (ctx, _i, p) => ({ token: await tokenFromParams(ctx, 'aps.token', p) }),
123
+ },
124
+ {
125
+ type: 'aps.modelProperties',
126
+ title: 'APS model properties',
127
+ category: 'network',
128
+ doc: 'Reads Model Derivative metadata and properties of a translated model (base64 derivative URN, or a Docs/ACC version id) into a table: objectid, externalId (Revit UniqueId), name, category, IfcGUID and every property as a Group.Property column. Joinable with IFC via table.joinByKey.',
129
+ inputs: [
130
+ { name: 'token', type: ANY_ITEM, optional: true, doc: 'From aps.token; otherwise the credential params are used.' },
131
+ { name: 'urn', type: SCALAR_ITEM, optional: true, doc: 'Overrides the urn param.' },
132
+ ],
133
+ outputs: [
134
+ { name: 'table', type: TABLE_ITEM },
135
+ { name: 'view', type: SCALAR_ITEM },
136
+ ],
137
+ params: [
138
+ { name: 'urn', kind: 'string', default: '', doc: 'Base64 derivative URN, or a urn:adsk.wipprod:fs.file:vf.…?version=N version id (encoded for you).' },
139
+ { name: 'viewGuid', kind: 'string', default: '', doc: 'Metadata view guid; defaults to the master (else first) 3d view.' },
140
+ { name: 'region', kind: 'enum', default: 'US', options: [...APS_REGIONS], doc: 'Data centre, sent as x-ads-region.' },
141
+ { name: 'key', kind: 'string', default: 'externalId', doc: 'Key column of the output table, e.g. externalId, IfcGUID or objectid.' },
142
+ { name: 'forceget', kind: 'boolean', default: false, doc: 'Ask APS for a property payload over its 20 MB default (answers HTTP 413 otherwise).' },
143
+ { name: 'maxAttempts', kind: 'number', default: DEFAULT_MAX_ATTEMPTS, doc: 'Requests per resource while APS answers 202 (still processing).' },
144
+ { name: 'retryDelayMs', kind: 'number', default: DEFAULT_RETRY_DELAY_MS },
145
+ { name: 'timeoutMs', kind: 'number', default: DEFAULT_TIMEOUT_MS },
146
+ { name: 'maxBytes', kind: 'number', default: DEFAULT_MAX_BYTES, doc: 'Response size cap per request.' },
147
+ ...CREDENTIAL_PARAMS,
148
+ ],
149
+ capabilities: ['network.fetch:*'],
150
+ requires: { network: true },
151
+ volatile: true,
152
+ run: async (ctx, i, p) => {
153
+ const node = 'aps.modelProperties';
154
+ const rawUrn = str(i.urn) || str(p.urn);
155
+ let urn;
156
+ try {
157
+ urn = toDerivativeUrn(rawUrn);
158
+ }
159
+ catch (err) {
160
+ throw new Error(`${node}: ${err instanceof Error ? err.message : String(err)}`);
161
+ }
162
+ const region = APS_REGIONS.includes(str(p.region).toUpperCase()) ? str(p.region).toUpperCase() : 'US';
163
+ const limits = { timeoutMs: bounded(p.timeoutMs, DEFAULT_TIMEOUT_MS, 1), maxBytes: bounded(p.maxBytes, DEFAULT_MAX_BYTES, 1) };
164
+ const retry = {
165
+ maxAttempts: Math.floor(bounded(p.maxAttempts, DEFAULT_MAX_ATTEMPTS, 1)),
166
+ retryDelayMs: bounded(p.retryDelayMs, DEFAULT_RETRY_DELAY_MS, 0),
167
+ };
168
+ const token = await resolveToken(ctx, node, i.token, p);
169
+ const base = `/modelderivative/v2/designdata/${urn}/metadata`;
170
+ const metadata = await getJson(ctx, node, token, base, region, limits, retry, 'metadata');
171
+ let view;
172
+ try {
173
+ view = pickView(metadata, str(p.viewGuid));
174
+ }
175
+ catch (err) {
176
+ throw new Error(`${node}: ${err instanceof Error ? err.message : String(err)}`);
177
+ }
178
+ const query = p.forceget === true ? '?forceget=true' : '';
179
+ const props = await getJson(ctx, node, token, `${base}/${encodeURIComponent(view.guid)}/properties${query}`, region, limits, retry, 'properties');
180
+ const collection = props?.data?.collection;
181
+ if (!Array.isArray(collection))
182
+ throw new Error(`${node}: properties response has no data.collection`);
183
+ try {
184
+ return { table: propertiesToTable(collection, str(p.key) || 'externalId'), view: view.guid };
185
+ }
186
+ catch (err) {
187
+ throw new Error(`${node}: ${err instanceof Error ? err.message : String(err)}`);
188
+ }
189
+ },
190
+ },
191
+ ];
192
+ //# sourceMappingURL=aps-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-nodes.js","sourceRoot":"","sources":["../src/aps-nodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Model Derivative property collection → flow `Table` (#5634).
3
+ *
4
+ * `GET …/metadata/{guid}/properties` returns
5
+ * `{ data: { type: 'properties', collection: [{ objectid, name, externalId, properties }] } }`
6
+ * where `properties` maps a display group (`Identity Data`, `Dimensions`, …)
7
+ * to its name/value pairs. One row per object:
8
+ * - `objectid` (viewer dbId), `externalId` (for Revit the element UniqueId),
9
+ * `name`;
10
+ * - `category`: the first `Category` value found, the hidden `__category__`
11
+ * group first (the Revit viewer's property database keeps it there), then
12
+ * every other group in response order;
13
+ * - `IfcGUID`: promoted from whichever group carries it (Revit writes it
14
+ * under `IFC Parameters`), so the table joins with an IFC export's
15
+ * `GlobalId` via `table.joinByKey` without the caller knowing the group;
16
+ * - every property flattened to a `Group.Property` column.
17
+ * A column whose values are all numbers is typed `real`, all booleans
18
+ * `boolean`; anything else is `string`. Nothing is dropped: a row without an
19
+ * `externalId` keeps a null there, and the caller picks the key column.
20
+ */
21
+ import type { Table } from '@ifc-lite/flow';
22
+ /** Build the table; `key` must name one of its columns. */
23
+ export declare function propertiesToTable(collection: readonly unknown[], key: string): Table;
24
+ //# sourceMappingURL=aps-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-table.d.ts","sourceRoot":"","sources":["../src/aps-table.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAiC,KAAK,EAAE,MAAM,gBAAgB,CAAC;AA6D3E,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CA2DpF"}
@@ -0,0 +1,123 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ const FIXED = ['objectid', 'externalId', 'name', 'category', 'IfcGUID'];
5
+ function isRecord(v) {
6
+ return !!v && typeof v === 'object' && !Array.isArray(v);
7
+ }
8
+ function toCell(v) {
9
+ if (v === null || v === undefined)
10
+ return null;
11
+ if (typeof v === 'string' || typeof v === 'boolean')
12
+ return v;
13
+ if (typeof v === 'number')
14
+ return Number.isFinite(v) ? v : String(v);
15
+ if (Array.isArray(v))
16
+ return v.map((x) => (isRecord(x) || Array.isArray(x) ? JSON.stringify(x) : String(x))).join('; ');
17
+ return JSON.stringify(v);
18
+ }
19
+ /**
20
+ * Flatten a property tree to `path → value` leaves. Iterative (a JSON tree
21
+ * has no cycles, but a deep one must not cost stack), arrays kept as leaves.
22
+ */
23
+ function flatten(properties) {
24
+ const out = new Map();
25
+ const stack = [['', properties]];
26
+ while (stack.length > 0) {
27
+ const [prefix, obj] = stack.pop();
28
+ const groups = [];
29
+ for (const [k, v] of Object.entries(obj)) {
30
+ const path = prefix ? `${prefix}.${k}` : k;
31
+ if (isRecord(v))
32
+ groups.push([path, v]);
33
+ else
34
+ out.set(path, v);
35
+ }
36
+ // Reversed, so groups pop off the stack in response order.
37
+ for (let g = groups.length - 1; g >= 0; g--)
38
+ stack.push(groups[g]);
39
+ }
40
+ return out;
41
+ }
42
+ function findProperty(properties, prop, preferGroup) {
43
+ const groups = Object.entries(properties).filter((e) => isRecord(e[1]));
44
+ if (preferGroup)
45
+ groups.sort(([a], [b]) => Number(b === preferGroup) - Number(a === preferGroup));
46
+ for (const [, group] of groups) {
47
+ const v = group[prop];
48
+ if (v !== undefined && v !== null && v !== '')
49
+ return v;
50
+ }
51
+ return null;
52
+ }
53
+ function inferType(values) {
54
+ const present = values.filter((v) => v !== null);
55
+ if (present.length > 0 && present.every((v) => typeof v === 'number'))
56
+ return 'real';
57
+ if (present.length > 0 && present.every((v) => typeof v === 'boolean'))
58
+ return 'boolean';
59
+ return 'string';
60
+ }
61
+ /** Build the table; `key` must name one of its columns. */
62
+ export function propertiesToTable(collection, key) {
63
+ const fixedRows = [];
64
+ const flatRows = [];
65
+ const propColumns = [];
66
+ const seen = new Set();
67
+ for (const raw of collection) {
68
+ if (!isRecord(raw))
69
+ continue;
70
+ const item = raw;
71
+ const properties = isRecord(item.properties) ? item.properties : {};
72
+ fixedRows.push({
73
+ objectid: typeof item.objectid === 'number' ? item.objectid : null,
74
+ externalId: typeof item.externalId === 'string' && item.externalId.length > 0 ? item.externalId : null,
75
+ name: typeof item.name === 'string' ? item.name : null,
76
+ category: toCell(findProperty(properties, 'Category', '__category__')),
77
+ IfcGUID: toCell(findProperty(properties, 'IfcGUID', 'IFC Parameters')),
78
+ });
79
+ const flat = flatten(properties);
80
+ flatRows.push(flat);
81
+ for (const path of flat.keys()) {
82
+ if (seen.has(path))
83
+ continue;
84
+ seen.add(path);
85
+ propColumns.push(path);
86
+ }
87
+ }
88
+ // A top-level scalar property named like a fixed column would overwrite it.
89
+ const reserved = new Set(FIXED);
90
+ const columnName = new Map(propColumns.map((p) => [p, reserved.has(p) ? `properties.${p}` : p]));
91
+ const rows = fixedRows.map((fixed, i) => {
92
+ const row = { ...fixed };
93
+ for (const path of propColumns)
94
+ row[columnName.get(path)] = toCell(flatRows[i].get(path));
95
+ return row;
96
+ });
97
+ const columns = [
98
+ { name: 'objectid', type: 'integer' },
99
+ { name: 'externalId', type: 'identifier' },
100
+ { name: 'name', type: 'label' },
101
+ { name: 'category', type: 'label' },
102
+ { name: 'IfcGUID', type: 'identifier' },
103
+ ...propColumns.map((p) => {
104
+ const name = columnName.get(p);
105
+ return { name, type: inferType(rows.map((r) => r[name])) };
106
+ }),
107
+ ];
108
+ // Mixed columns were inferred `string`: make their cells agree.
109
+ for (const col of columns) {
110
+ if (col.type !== 'string')
111
+ continue;
112
+ for (const row of rows) {
113
+ const v = row[col.name];
114
+ if (v !== null && typeof v !== 'string')
115
+ row[col.name] = String(v);
116
+ }
117
+ }
118
+ if (!columns.some((c) => c.name === key)) {
119
+ throw new Error(`key column "${key}" is not in the table (columns: ${columns.slice(0, 12).map((c) => c.name).join(', ')}${columns.length > 12 ? ', …' : ''})`);
120
+ }
121
+ return { columns, rows, key };
122
+ }
123
+ //# sourceMappingURL=aps-table.js.map