@astrato-io/sdk 0.0.0-stage → 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.
- package/LICENSE +93 -0
- package/README.md +362 -2
- package/index.d.ts +504 -0
- package/index.js +1 -0
- package/package.json +29 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Astrato Libraries License — Version 1.0
|
|
2
|
+
|
|
3
|
+
Copyright © Astrato Analytics Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
## 1. Scope
|
|
6
|
+
|
|
7
|
+
This license applies exclusively to software packages expressly distributed by Astrato under this license, including accompanying Astrato-owned assets and documentation (the “Software”).
|
|
8
|
+
|
|
9
|
+
It does not apply to other Astrato software or assets, including visualization bundles downloaded or extracted from the Astrato application, its asset URLs or content delivery networks, unless expressly designated by Astrato as distributed under this license. Public accessibility alone does not confer any rights under this license.
|
|
10
|
+
|
|
11
|
+
## 2. Permitted use
|
|
12
|
+
|
|
13
|
+
Subject to this license, Astrato grants you a limited, non-exclusive, non-transferable license to use, copy, configure, modify and bundle the Software to develop, deploy and operate applications that access, process, analyze or display data through the Astrato platform (“Astrato-connected applications”).
|
|
14
|
+
|
|
15
|
+
Astrato-connected applications may incorporate external data, APIs, user inputs, calculations and other sources, provided Astrato supplies a substantive data or analytical component. Nominal or incidental integration with Astrato does not qualify an otherwise independent application.
|
|
16
|
+
|
|
17
|
+
All use must be covered by a valid Astrato subscription or other written authorization and remain within its applicable user, audience and usage limits.
|
|
18
|
+
|
|
19
|
+
## 3. Embedding and distribution
|
|
20
|
+
|
|
21
|
+
You may embed the Software, including permitted modifications, into Astrato-connected applications and distribute copies reasonably necessary for their operation, including browser-delivered code and application bundles.
|
|
22
|
+
|
|
23
|
+
Authorized end users may execute those copies solely as part of the applicable Astrato-connected application and within the scope of the relevant Astrato authorization. Such distribution does not grant independent rights to extract, reuse, modify or redistribute the Software.
|
|
24
|
+
|
|
25
|
+
You must preserve applicable copyright notices, this license and required third-party notices in distributed copies or make them available through an accessible accompanying notice.
|
|
26
|
+
|
|
27
|
+
You may not distribute, sublicense, sell or offer the Software as a standalone library, toolkit or independently reusable component.
|
|
28
|
+
|
|
29
|
+
Your independently developed application code remains yours and need not be disclosed. Modifications to the Software do not transfer ownership of Astrato intellectual property.
|
|
30
|
+
|
|
31
|
+
## 4. Restrictions
|
|
32
|
+
|
|
33
|
+
You may not:
|
|
34
|
+
|
|
35
|
+
- Use the Software as a general-purpose charting, analytics, application-runtime or data-processing library independently of Astrato.
|
|
36
|
+
|
|
37
|
+
- Use nominal or incidental Astrato integration to circumvent the Astrato-connected use requirement.
|
|
38
|
+
|
|
39
|
+
- Distribute the Software separately for independent reuse outside Astrato-connected applications.
|
|
40
|
+
|
|
41
|
+
- Bypass Astrato authentication, access controls, licensing mechanisms or usage limits.
|
|
42
|
+
|
|
43
|
+
- Remove or obscure applicable copyright, ownership or attribution notices.
|
|
44
|
+
|
|
45
|
+
- Reverse engineer, decompile, disassemble or otherwise attempt to derive non-public source code or implementation details of the Software, except as permitted by applicable law.
|
|
46
|
+
|
|
47
|
+
- Use the Software, its source code, non-public implementation details or systematically extracted outputs to train, distil, develop or improve software intended to reproduce or substantially replicate the Software's proprietary functionality, including competing visualization libraries, analytical engines or application runtimes.
|
|
48
|
+
|
|
49
|
+
These restrictions do not prohibit ordinary use of AI-assisted development tools, analysis of application outputs or development of independently created application functionality, provided such activities do not involve prohibited extraction, replication or misuse of the Software.
|
|
50
|
+
|
|
51
|
+
Nothing in this section restricts independent development without misuse of the Software or acts that cannot lawfully be restricted.
|
|
52
|
+
|
|
53
|
+
## 5. Third-party components
|
|
54
|
+
|
|
55
|
+
Third-party components remain governed by their respective licenses, and nothing in this license limits rights granted directly under those licenses.
|
|
56
|
+
|
|
57
|
+
You must comply with applicable third-party obligations, including attribution, notices and source-code availability requirements where applicable.
|
|
58
|
+
|
|
59
|
+
## 6. Duration and termination
|
|
60
|
+
|
|
61
|
+
This license remains effective while the applicable Astrato subscription or written authorization remains valid and the Software is used in accordance with this license.
|
|
62
|
+
|
|
63
|
+
Astrato may terminate rights under this license for a material breach that remains uncured for 30 days after written notice. Astrato may terminate immediately for a deliberate material breach involving unauthorized distribution, circumvention of access controls or prohibited replication of the Software.
|
|
64
|
+
|
|
65
|
+
Upon expiry or termination, you must cease use and distribution of the Software and discontinue applications dependent on it, unless otherwise authorized in writing.
|
|
66
|
+
|
|
67
|
+
Previously delivered copies do not create perpetual rights. However, end users covered by a separate, continuing Astrato authorization may continue using applications within the scope of that authorization.
|
|
68
|
+
|
|
69
|
+
Copies may be retained solely for archival, backup or legal compliance purposes, without further use or distribution, except as required by law.
|
|
70
|
+
|
|
71
|
+
Termination does not affect rights independently granted under third-party licenses or provisions intended by their nature to survive termination, including restrictions on unauthorized use and protection of intellectual property.
|
|
72
|
+
|
|
73
|
+
## 7. Relationship to Astrato's standard terms
|
|
74
|
+
|
|
75
|
+
This license grants additional rights to use, modify, embed and distribute the Software beyond those provided by Astrato's standard terms, exclusively for the Astrato-connected uses described here.
|
|
76
|
+
|
|
77
|
+
To the extent that Astrato's standard terms restrict activities expressly permitted by this license, these permissions prevail solely for the Software and within the scope of this license.
|
|
78
|
+
|
|
79
|
+
All other applicable Astrato terms, contractual obligations and subscription restrictions remain effective.
|
|
80
|
+
|
|
81
|
+
A separately signed agreement expressly addressing these libraries takes precedence to the extent of any conflict.
|
|
82
|
+
|
|
83
|
+
Nothing in this license grants rights to other Astrato software, visualization bundles, application assets, services or intellectual property.
|
|
84
|
+
|
|
85
|
+
## 8. Ownership, warranties and governing law
|
|
86
|
+
|
|
87
|
+
Astrato and its licensors retain all ownership and intellectual property rights in the Software. No rights are granted except those expressly stated in this license or required by applicable law.
|
|
88
|
+
|
|
89
|
+
Except as provided in an applicable written agreement or required by law, the Software is provided “as is,” without warranties of any kind, express or implied, including merchantability, fitness for a particular purpose and non-infringement.
|
|
90
|
+
|
|
91
|
+
Where an applicable Astrato customer agreement governs the use of the Software, its provisions concerning service access, support, warranties, liability and governing law apply.
|
|
92
|
+
|
|
93
|
+
Otherwise, this license is governed by the laws of England and Wales, and the courts of England and Wales have exclusive jurisdiction, subject to mandatory applicable law.
|
package/README.md
CHANGED
|
@@ -1,3 +1,363 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @astrato-io/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser SDK for connecting custom applications to Astrato APIs and runtime services.
|
|
4
|
+
|
|
5
|
+
Use this package for authentication, Astrato API requests, data and metadata access, events, variables, and group-scoped selection state. Visualization rendering is provided separately by `@astrato-io/visualizations`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @astrato-io/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The package is distributed as ESM and requires a browser environment with `fetch`, `Headers`, and `URL`. RxJS is installed as a package dependency and is used by the public observable APIs.
|
|
14
|
+
|
|
15
|
+
## Create A Client
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createAstratoClient } from '@astrato-io/sdk';
|
|
19
|
+
|
|
20
|
+
const client = createAstratoClient({
|
|
21
|
+
baseUrl: 'https://your-astrato-instance.example/',
|
|
22
|
+
tenantId: 'tenant-id',
|
|
23
|
+
auth: {
|
|
24
|
+
token: 'personal-access-token',
|
|
25
|
+
},
|
|
26
|
+
visualizationEngine: true,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`baseUrl` is required. `tenantId` is used as the default tenant for data and metadata requests.
|
|
31
|
+
|
|
32
|
+
Authentication can use a fixed token, an asynchronous token provider, or custom headers:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
const client = createAstratoClient({
|
|
36
|
+
baseUrl,
|
|
37
|
+
tenantId,
|
|
38
|
+
auth: {
|
|
39
|
+
getAccessToken: async () => refreshAccessToken(),
|
|
40
|
+
headers: {
|
|
41
|
+
'x-client-name': 'customer-portal',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When a token is available, the SDK sends it as `Authorization: Bearer <token>` unless an authorization header was supplied explicitly.
|
|
48
|
+
|
|
49
|
+
## Embedded iframe bridge
|
|
50
|
+
|
|
51
|
+
An application embedded in a trusted host can use the same SDK client without receiving host credentials. The host establishes a versioned `MessagePort` connection with one iframe and handles requests through an allowlisted callback. This is an explicit bootstrap: merely being inside an iframe never enables the bridge.
|
|
52
|
+
|
|
53
|
+
In the host, create the bridge before the iframe application starts. The `request` callback must validate the relative SDK path, HTTP method, tenant, semantic layer, and operation against the host's own authorization rules, then execute the approved operation with credentials retained in the host. Do not forward child-supplied headers to a privileged network client without a deliberate allowlist. If the bridge is attached after the iframe has already loaded, pass `initialLoadComplete: true`; this lets the bridge distinguish the first load from a later navigation.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createAstratoIframeHostBridge } from '@astrato-io/sdk';
|
|
57
|
+
|
|
58
|
+
const bridge = createAstratoIframeHostBridge({
|
|
59
|
+
iframe,
|
|
60
|
+
childOrigin: 'null', // for an intentionally opaque sandbox; otherwise use the exact child origin
|
|
61
|
+
context: {
|
|
62
|
+
tenantId,
|
|
63
|
+
semanticLayer: { id: semanticLayerId, datasourceType },
|
|
64
|
+
capabilities: ['query', 'metadata'],
|
|
65
|
+
},
|
|
66
|
+
request: (request, signal) => executeAllowedSdkRequest(request, signal),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Destroy when the iframe is removed or its authority changes.
|
|
70
|
+
bridge.destroy();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The iframe's trusted application bootstrap supplies the exact parent origin and connects once. After that, shared application runtime code can discover the established connection synchronously and create the ordinary visualization-enabled client:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { connectAstratoIframeBridge, getAstratoIframeBridge } from '@astrato-io/sdk';
|
|
77
|
+
|
|
78
|
+
await connectAstratoIframeBridge({ parentOrigin: trustedParentOrigin });
|
|
79
|
+
|
|
80
|
+
const connection = getAstratoIframeBridge();
|
|
81
|
+
if (!connection) throw new Error('Astrato iframe bridge is unavailable.');
|
|
82
|
+
const client = connection.createClient();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`createClient()` never falls back to `fetch`. The bridge rejects absolute URLs and credential-bearing headers, bounds request and response sizes, and supports request aborts, timeouts, and disconnect cleanup. Host requests run up to `maxConcurrentRequests` at once (16 by default); excess requests wait in arrival order, with at most 32 active and queued requests combined. The request timeout includes time spent waiting. An aborted or timed-out active request keeps its concurrency slot until the host callback settles, so the host limit remains accurate even if that callback ignores abort. Host errors are returned as generic codes so private details are not sent to the iframe. The host callback remains the authorization boundary; the context's `capabilities` list is descriptive. The protocol version is exported as `ASTRATO_IFRAME_BRIDGE_PROTOCOL_VERSION` and incompatible versions do not connect. Destroying or replacing a connection destroys its SDK clients and invalidates their metadata caches.
|
|
86
|
+
|
|
87
|
+
## Data And Metadata
|
|
88
|
+
|
|
89
|
+
The direct data service uses the current Astrato data and schema APIs. Query payloads are passed through without hidden filter, variable, or selection changes.
|
|
90
|
+
|
|
91
|
+
Semantic layer metadata is loaded from the tenant-scoped data view composite endpoint:
|
|
92
|
+
`/api/message/tenants/{tenantId}/schema/dataviews/{semanticLayerId}/composite`. This response provides engine field keys, semantic item keys and view connectivity without reconstructing identity from schema, view or field names.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const metadata = await client.services.data.getSemanticLayerMetadata?.('semantic-layer-id');
|
|
96
|
+
|
|
97
|
+
const result = await client.hostServices.engine.getData({
|
|
98
|
+
semanticLayerId: 'semantic-layer-id',
|
|
99
|
+
query: {
|
|
100
|
+
dimensions: [],
|
|
101
|
+
measures: [],
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
When the visualization engine executes a query, it retains the latest successful query metadata for each visualization
|
|
107
|
+
object ID. Use that metadata to show the executed SQL, duration, result size, vendor query link, or real warehouse query
|
|
108
|
+
insights without issuing the data query again:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const metadata = client.hostServices.engine.getLastQueryMetadata?.('sales-by-manufacturer');
|
|
112
|
+
|
|
113
|
+
const insights =
|
|
114
|
+
metadata?.semanticLayerId && metadata.startTime !== undefined
|
|
115
|
+
? await client.hostServices.engine.getQueryInsights?.({
|
|
116
|
+
queryID: metadata.queryID,
|
|
117
|
+
semanticLayerId: metadata.semanticLayerId,
|
|
118
|
+
startTime: metadata.startTime,
|
|
119
|
+
})
|
|
120
|
+
: undefined;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Query metadata is isolated by the request context's `objectId`. If multiple requests for one object overlap, only the
|
|
124
|
+
newest request may update its metadata. Query-insights availability and response details depend on the connected warehouse.
|
|
125
|
+
|
|
126
|
+
The visualization engine maps `semanticLayerId` to the current engine API's
|
|
127
|
+
legacy `databaseID` wire field. The low-level `client.services.data.query()`
|
|
128
|
+
method remains transparent and posts its `query` value without rewriting it.
|
|
129
|
+
|
|
130
|
+
Request context can be configured once on the client and overridden per query:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const client = createAstratoClient({
|
|
134
|
+
baseUrl,
|
|
135
|
+
tenantId,
|
|
136
|
+
auth: { token },
|
|
137
|
+
context: {
|
|
138
|
+
datasourceType: 'snowflake',
|
|
139
|
+
sheetId: 'sheet-id',
|
|
140
|
+
workbookState: 'draft',
|
|
141
|
+
workbookVersionContainerId: 'workbook-version-container-id',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Authenticated Requests And Customization
|
|
147
|
+
|
|
148
|
+
Use `client.request()` for Astrato endpoints that do not have a dedicated service method. The request uses the configured base URL, authentication, transport events, and error logging:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
const response = await client.request({
|
|
152
|
+
method: 'GET',
|
|
153
|
+
url: 'api/example',
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`createAstratoClient()` also accepts a custom `transport`, `logger`, event bus, data endpoint overrides, and host-service implementations. These options allow applications to integrate their own networking, diagnostics, or platform services without changing visualization code.
|
|
158
|
+
|
|
159
|
+
## Visualization Engine
|
|
160
|
+
|
|
161
|
+
Current Astrato visualization wrappers require a small synchronous engine surface. Enable the SDK's visualization engine explicitly when the client is used with `@astrato-io/visualizations`:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const client = createAstratoClient({
|
|
165
|
+
baseUrl,
|
|
166
|
+
tenantId,
|
|
167
|
+
auth: { token },
|
|
168
|
+
visualizationEngine: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await client.hostServices.engine.fields.getFieldMetadata?.({
|
|
172
|
+
semanticLayerId: 'semantic-layer-id',
|
|
173
|
+
tenantId,
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Metadata snapshots are reused within one client for five minutes, with at most 16 completed
|
|
178
|
+
snapshots retained. Pending requests for the same tenant, semantic layer, datasource, and
|
|
179
|
+
authentication authority share one load. Configure `metadataCache` on `createAstratoClient()`
|
|
180
|
+
when a different bound is appropriate. Call `client.hostServices.engine.fields.invalidateMetadata()`
|
|
181
|
+
after a model change; pass the same metadata request to invalidate one layer, or omit it to clear
|
|
182
|
+
all snapshots. `refreshMetadata(request)` clears and reloads one layer. A page reload creates a
|
|
183
|
+
new client and therefore starts with an empty cache.
|
|
184
|
+
|
|
185
|
+
The default engine resolves authentication once per metadata operation or query and binds its
|
|
186
|
+
requests to the same headers. Concurrent operations share pending authentication resolution.
|
|
187
|
+
Callbacks that return a new token on each invocation are supported. When any SDK request observes
|
|
188
|
+
changed authentication headers, the default engine clears its cached and active metadata. Load
|
|
189
|
+
metadata again before reading synchronous field or connectivity APIs. Queries resolve authentication
|
|
190
|
+
before transformation so they cannot substitute fields from the previous authority's metadata.
|
|
191
|
+
A metadata operation or engine query rejects if a subsequent SDK operation observes changed
|
|
192
|
+
authentication before it completes; rejected query results do not update query metadata. An older
|
|
193
|
+
authentication lookup that finishes after a newer successful lookup rejects if its credentials differ from
|
|
194
|
+
the current ones. Ordinary requests continue resolving their own authentication.
|
|
195
|
+
Applications that provide their own transport authority without SDK authentication must call
|
|
196
|
+
`invalidateMetadata()` when that authority changes. Query results are never cached.
|
|
197
|
+
|
|
198
|
+
Destroying a client invalidates metadata only in an engine created by that client. An engine supplied
|
|
199
|
+
through `hostServices.engine` remains owned by its caller. Grouped-dimension choices survive cache
|
|
200
|
+
hits; a fresh metadata load initializes them from the model again.
|
|
201
|
+
|
|
202
|
+
The asynchronous metadata call loads the engine snapshot. Field getters and connectivity checks then read that snapshot synchronously, as required by the visualization wrappers. Fields are identified by engine `fieldKey` values; the SDK does not parse field keys into view/name references.
|
|
203
|
+
Semantic dimensions, measures and calculations use the keys and related views supplied by the composite response. View names are retained only where existing visualization APIs still request view-level connectivity.
|
|
204
|
+
|
|
205
|
+
## Using Visualizations With The SDK
|
|
206
|
+
|
|
207
|
+
Install both packages and import the visualization stylesheet once in the host application:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm install @astrato-io/sdk @astrato-io/visualizations
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import '@astrato-io/visualizations/styles.css';
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Create one SDK client and one visualization runtime for the application. The visualization engine must be enabled so the runtime can load data and semantic-layer metadata through the SDK:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { createAstratoClient } from '@astrato-io/sdk';
|
|
221
|
+
import { createVisualizationRuntime, registerAstratoElements, registry } from '@astrato-io/visualizations';
|
|
222
|
+
|
|
223
|
+
const tenantId = 'tenant-id';
|
|
224
|
+
const semanticLayerId = 'semantic-layer-id';
|
|
225
|
+
|
|
226
|
+
const client = createAstratoClient({
|
|
227
|
+
baseUrl: 'https://your-astrato-instance.example/',
|
|
228
|
+
tenantId,
|
|
229
|
+
auth: { token: 'personal-access-token' },
|
|
230
|
+
visualizationEngine: true,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const runtime = createVisualizationRuntime({
|
|
234
|
+
client,
|
|
235
|
+
registry,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
runtime.createInstance({
|
|
239
|
+
id: 'sales-by-manufacturer',
|
|
240
|
+
type: 'bar',
|
|
241
|
+
groupId: 'dashboard',
|
|
242
|
+
dataBinding: {
|
|
243
|
+
tenantId,
|
|
244
|
+
semanticLayerId,
|
|
245
|
+
datasourceType: 'snowflake',
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
registerAstratoElements({ runtime });
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Render the runtime-owned instance with the framework-neutral web component:
|
|
253
|
+
|
|
254
|
+
```html
|
|
255
|
+
<astrato-visualization instance-id="sales-by-manufacturer"></astrato-visualization>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The element must have non-zero dimensions:
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
astrato-visualization {
|
|
262
|
+
display: block;
|
|
263
|
+
width: 100%;
|
|
264
|
+
height: 400px;
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
If `vSpec` is omitted, the runtime creates an empty default visualization. To create a useful initial visualization, use `compileVisualizationDefinition()` from `@astrato-io/visualizations/definitions` with semantic-layer metadata loaded through `client.hostServices.engine.fields.getFieldMetadata()`. Pass the compiled vSpec to `runtime.createInstance()`. Definitions use exact engine keys and return structured validation issues when compilation fails.
|
|
269
|
+
|
|
270
|
+
Applications that manage DOM lifecycle directly can mount without a web component:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
const container = document.getElementById('chart');
|
|
274
|
+
|
|
275
|
+
if (!container) {
|
|
276
|
+
throw new Error('Missing chart container.');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const handle = await runtime.mount(container, 'sales-by-manufacturer');
|
|
280
|
+
const renderResult = await handle.ready;
|
|
281
|
+
|
|
282
|
+
console.log(renderResult.status);
|
|
283
|
+
|
|
284
|
+
handle.unmount();
|
|
285
|
+
runtime.removeInstance('sales-by-manufacturer');
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Instances with the same `groupId` share selection state. Removing a web component unmounts it automatically; remove runtime instances when they are no longer needed and call `client.destroy()` when the application disposes the SDK client.
|
|
289
|
+
|
|
290
|
+
See the `@astrato-io/visualizations` package README for supported visualization IDs, complete definition schemas, operations, getters, metadata discovery, and custom-element options.
|
|
291
|
+
|
|
292
|
+
## Host Services
|
|
293
|
+
|
|
294
|
+
The client exposes the same host services through `client.services` and `client.hostServices`. SDK and transport events
|
|
295
|
+
are available separately through `client.events`.
|
|
296
|
+
|
|
297
|
+
| Service | Purpose |
|
|
298
|
+
| ------------ | ------------------------------------------------------------------------------------- |
|
|
299
|
+
| `data` | Queries, semantic layer metadata, datasource metadata, aggregations, and capabilities |
|
|
300
|
+
| `engine` | Optional visualization-compatible query and field adapter |
|
|
301
|
+
| `selections` | Group-scoped applied and pending selection state |
|
|
302
|
+
| `variables` | Variable lookup and value resolution |
|
|
303
|
+
| `model` | Visualization model save compatibility hooks |
|
|
304
|
+
|
|
305
|
+
Applications can replace individual services through `hostServices`:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
const client = createAstratoClient({
|
|
309
|
+
baseUrl,
|
|
310
|
+
hostServices: {
|
|
311
|
+
variables: customVariableService,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Data and engine defaults fail clearly when required configuration is missing. Selection and variable defaults use in-memory state.
|
|
317
|
+
|
|
318
|
+
## Selection State
|
|
319
|
+
|
|
320
|
+
Selections are isolated by `groupId`. Value and associative selections use their kind together with the engine field key
|
|
321
|
+
as identity, so both kinds can coexist for one field.
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
client.services.selections.selectValues('dashboard', {
|
|
325
|
+
field: { fieldKey: '[Orders].[Country]' },
|
|
326
|
+
operator: 'inList',
|
|
327
|
+
values: ['Sweden'],
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const subscription = client.services.selections.onActiveSelectionChangeApplied('dashboard').subscribe((selection) => {
|
|
331
|
+
console.log(selection.applied);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
subscription.unsubscribe();
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Selection mode defaults to `auto`. In `manual` mode, changes remain pending until `applyPendingSelections(groupId)` is called.
|
|
338
|
+
|
|
339
|
+
## Persistence Behavior
|
|
340
|
+
|
|
341
|
+
The SDK does not persist visualization or workbook state by default.
|
|
342
|
+
|
|
343
|
+
- `model.saveProperties()` and `model.saveLocalVSpec()` are compatibility no-ops.
|
|
344
|
+
- No implicit `localStorage` or IndexedDB storage is used.
|
|
345
|
+
|
|
346
|
+
## Events And Cleanup
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
const unsubscribe = client.events.on('transport:error', (event) => {
|
|
350
|
+
console.error(event.payload);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
unsubscribe();
|
|
354
|
+
client.destroy();
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
`destroy()` clears SDK event subscriptions. Applications should also unsubscribe from service observables they own.
|
|
358
|
+
|
|
359
|
+
## Package Build
|
|
360
|
+
|
|
361
|
+
The package contains a minified ESM bundle, a single public TypeScript declaration file, this README, package metadata, the supplied LICENSE document, and any notice documents. It contains no source maps. RxJS remains external so application bundlers can deduplicate and tree-shake it.
|
|
362
|
+
|
|
363
|
+
The JavaScript implementation and public API types are visible to package users. Minification does not make client-side code confidential. Credentials and access rules must remain in the host application and Astrato APIs.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
export declare const ASTRATO_IFRAME_BRIDGE_PROTOCOL_VERSION = 1;
|
|
4
|
+
|
|
5
|
+
export declare type AstratoActiveSelection = AstratoSelectionState;
|
|
6
|
+
|
|
7
|
+
export declare interface AstratoAuthProvider {
|
|
8
|
+
token?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
getAccessToken?: () => string | Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare interface AstratoBigQueryEngineReason {
|
|
14
|
+
code?: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export declare interface AstratoBigQueryInsights {
|
|
19
|
+
projectID?: string;
|
|
20
|
+
location?: string;
|
|
21
|
+
jobID?: string;
|
|
22
|
+
cacheHit?: boolean;
|
|
23
|
+
creationTimeMs?: number;
|
|
24
|
+
startTimeMs?: number;
|
|
25
|
+
endTimeMs?: number;
|
|
26
|
+
jobTotalBytesProcessed?: number;
|
|
27
|
+
totalSlotMs?: number;
|
|
28
|
+
totalSlotDurationMs?: number;
|
|
29
|
+
reservationId?: string;
|
|
30
|
+
numChildJobs?: number;
|
|
31
|
+
parentJobId?: string;
|
|
32
|
+
statementType?: string;
|
|
33
|
+
billingTier?: number;
|
|
34
|
+
totalBytesProcessed?: number;
|
|
35
|
+
totalBytesBilled?: number;
|
|
36
|
+
totalBytesProcessedAccuracy?: string;
|
|
37
|
+
numDmlAffectedRows?: number;
|
|
38
|
+
ddlOperationPerformed?: string;
|
|
39
|
+
performanceInsights?: AstratoBigQueryPerformanceInsights;
|
|
40
|
+
biEngineMode?: string;
|
|
41
|
+
biEngineReasons?: AstratoBigQueryEngineReason[];
|
|
42
|
+
undeclaredQueryParameterNames?: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export declare interface AstratoBigQueryPerformanceInsights {
|
|
46
|
+
avgPreviousExecutionMs?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export declare interface AstratoClient {
|
|
50
|
+
baseUrl: string;
|
|
51
|
+
services: AstratoHostServices;
|
|
52
|
+
hostServices: AstratoHostServices;
|
|
53
|
+
logger: AstratoLogger;
|
|
54
|
+
events: AstratoEventBus;
|
|
55
|
+
request<TResponse = unknown, TBody = unknown>(request: AstratoRequest<TBody>): Promise<TResponse>;
|
|
56
|
+
reportError(error: unknown, context?: AstratoLogContext): void;
|
|
57
|
+
destroy(): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export declare interface AstratoClientEndpoints {
|
|
61
|
+
data?: AstratoDataServiceEndpoints;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export declare interface AstratoDataRequestContext {
|
|
65
|
+
tenantId?: string;
|
|
66
|
+
datasourceType?: string;
|
|
67
|
+
workbookVersionContainerId?: string;
|
|
68
|
+
sheetId?: string;
|
|
69
|
+
objectId?: string;
|
|
70
|
+
role?: string;
|
|
71
|
+
userId?: string;
|
|
72
|
+
workbookState?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare interface AstratoDataResponse<TResponse = unknown> {
|
|
76
|
+
response: TResponse;
|
|
77
|
+
correlationID?: string;
|
|
78
|
+
telemetry?: {
|
|
79
|
+
duration?: number;
|
|
80
|
+
start?: number;
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
};
|
|
83
|
+
[key: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare interface AstratoDataService {
|
|
87
|
+
query(request: AstratoQueryRequest): Promise<AstratoQueryResult>;
|
|
88
|
+
getQueryInsights?(request: AstratoQueryInsightsRequest): Promise<AstratoQueryInsightsResponse>;
|
|
89
|
+
getSemanticLayerMetadata?(semanticLayerId: string, tenantId?: string): Promise<AstratoFieldMetadata | unknown>;
|
|
90
|
+
getDatasource?(datasourceId: string, tenantId?: string): Promise<unknown>;
|
|
91
|
+
getAggregations?(datasourceType: string, tenantId?: string): Promise<unknown[]>;
|
|
92
|
+
getCapabilities?(datasourceType: string, tenantId?: string): Promise<Record<string, unknown>>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export declare interface AstratoDataServiceEndpoints {
|
|
96
|
+
aggregations?: (tenantId: string, datasourceType: string) => string;
|
|
97
|
+
capabilities?: (tenantId: string, datasourceType: string) => string;
|
|
98
|
+
datasource?: (tenantId: string, datasourceId: string) => string;
|
|
99
|
+
queryInsights?: (tenantId: string) => string;
|
|
100
|
+
semanticLayerMetadata?: (tenantId: string, semanticLayerId: string) => string;
|
|
101
|
+
query?: string | ((tenantId: string) => string);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export declare interface AstratoEngineFieldsService {
|
|
105
|
+
getFieldMetadata?(request?: unknown): Promise<AstratoFieldMetadata>;
|
|
106
|
+
/** Evicts cached semantic-layer metadata. Omit the request to evict all snapshots. */
|
|
107
|
+
invalidateMetadata?(request?: unknown): void;
|
|
108
|
+
/** Evicts and reloads semantic-layer metadata for the supplied request. */
|
|
109
|
+
refreshMetadata?(request: unknown): Promise<AstratoFieldMetadata>;
|
|
110
|
+
getFields(request?: unknown): unknown[];
|
|
111
|
+
getValidFields(request?: unknown): unknown[];
|
|
112
|
+
getViewsFields(request?: unknown): unknown[];
|
|
113
|
+
getDimensionsList(request?: unknown): unknown[];
|
|
114
|
+
getMeasuresList(request?: unknown): unknown[];
|
|
115
|
+
getGroupedDimensions(request?: unknown): AstratoGroupedDimensions;
|
|
116
|
+
setGroupedDimensions?(state: AstratoGroupedDimensions): void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export declare interface AstratoEngineService {
|
|
120
|
+
readonly capabilities: Record<string, unknown>;
|
|
121
|
+
readonly databaseInfo: {
|
|
122
|
+
items: unknown[];
|
|
123
|
+
[key: string]: unknown;
|
|
124
|
+
};
|
|
125
|
+
readonly localeSettings: Record<string, unknown>;
|
|
126
|
+
readonly fields: AstratoEngineFieldsService;
|
|
127
|
+
transformQuery?(request: AstratoQueryRequest): Promise<unknown> | unknown;
|
|
128
|
+
getData(request: AstratoQueryRequest): Promise<AstratoQueryResult>;
|
|
129
|
+
getLastQueryMetadata?(objectId?: string): AstratoQueryMetadata | undefined;
|
|
130
|
+
getQueryInsights?(request: AstratoQueryInsightsRequest): Promise<AstratoQueryInsightsResponse>;
|
|
131
|
+
areFieldsConnected(filterField: unknown, targetField: unknown): boolean;
|
|
132
|
+
areViewsConnected(views1: string[], views2: string[]): boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export declare interface AstratoEvent<TName extends AstratoEventName = AstratoEventName> {
|
|
136
|
+
name: TName;
|
|
137
|
+
payload: AstratoEventPayloadMap[TName];
|
|
138
|
+
timestamp: number;
|
|
139
|
+
scope?: AstratoEventScope;
|
|
140
|
+
source?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export declare interface AstratoEventBus {
|
|
144
|
+
readonly events$: Observable<AstratoEvent>;
|
|
145
|
+
emit<TName extends AstratoEventName>(name: TName, payload: AstratoEventPayloadMap[TName], options?: AstratoEventOptions): void;
|
|
146
|
+
on<TName extends AstratoEventName>(name: TName, handler: AstratoEventHandler<TName>, options?: AstratoEventSubscriptionOptions): () => void;
|
|
147
|
+
onAny(handler: AstratoEventHandler, options?: AstratoEventSubscriptionOptions): () => void;
|
|
148
|
+
clear(): void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export declare type AstratoEventHandler<TName extends AstratoEventName = AstratoEventName> = (event: AstratoEvent<TName>) => void;
|
|
152
|
+
|
|
153
|
+
export declare type AstratoEventName = Extract<keyof AstratoEventPayloadMap, string>;
|
|
154
|
+
|
|
155
|
+
export declare interface AstratoEventOptions {
|
|
156
|
+
scope?: AstratoEventScope;
|
|
157
|
+
source?: string;
|
|
158
|
+
timestamp?: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export declare interface AstratoEventPayloadMap {
|
|
162
|
+
[eventName: string]: unknown;
|
|
163
|
+
'sdk:error': {
|
|
164
|
+
context: AstratoLogContext;
|
|
165
|
+
error: unknown;
|
|
166
|
+
};
|
|
167
|
+
'transport:error': {
|
|
168
|
+
error: unknown;
|
|
169
|
+
method: string;
|
|
170
|
+
url: string;
|
|
171
|
+
};
|
|
172
|
+
'transport:request': {
|
|
173
|
+
method: string;
|
|
174
|
+
url: string;
|
|
175
|
+
};
|
|
176
|
+
'transport:response': {
|
|
177
|
+
method: string;
|
|
178
|
+
url: string;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export declare interface AstratoEventScope {
|
|
183
|
+
groupId?: string;
|
|
184
|
+
instanceId?: string;
|
|
185
|
+
sessionId?: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export declare interface AstratoEventSubscriptionOptions {
|
|
189
|
+
scope?: AstratoEventScope;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export declare interface AstratoFieldMetadata {
|
|
193
|
+
fields?: unknown[];
|
|
194
|
+
validFields?: unknown[];
|
|
195
|
+
viewsFields?: unknown[];
|
|
196
|
+
dimensions?: unknown[];
|
|
197
|
+
measures?: unknown[];
|
|
198
|
+
groupedDimensions?: AstratoGroupedDimensions;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export declare interface AstratoFieldReference {
|
|
202
|
+
fieldKey: string;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export declare type AstratoGroupedDimensions = Record<string, number>;
|
|
206
|
+
|
|
207
|
+
export declare interface AstratoHostServices {
|
|
208
|
+
data: AstratoDataService;
|
|
209
|
+
engine: AstratoEngineService;
|
|
210
|
+
model: AstratoModelService;
|
|
211
|
+
selections: AstratoSelectionService;
|
|
212
|
+
variables: AstratoVariableService;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export declare type AstratoHttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
216
|
+
|
|
217
|
+
export declare interface AstratoIframeBridgeConnection {
|
|
218
|
+
readonly context: AstratoIframeBridgeContext;
|
|
219
|
+
/** Creates the ordinary SDK client using only the established port transport. */
|
|
220
|
+
createClient(): AstratoClient;
|
|
221
|
+
destroy(): void;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export declare interface AstratoIframeBridgeContext {
|
|
225
|
+
tenantId: string;
|
|
226
|
+
semanticLayer: {
|
|
227
|
+
id: string;
|
|
228
|
+
datasourceType?: string;
|
|
229
|
+
};
|
|
230
|
+
/** Describes host-granted operations. The host request callback remains the authority. */
|
|
231
|
+
capabilities: readonly string[];
|
|
232
|
+
requestContext?: AstratoDataRequestContext;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export declare class AstratoIframeBridgeError extends Error {
|
|
236
|
+
readonly code: AstratoIframeBridgeErrorCode;
|
|
237
|
+
constructor(code: AstratoIframeBridgeErrorCode, message: string);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export declare type AstratoIframeBridgeErrorCode = 'aborted' | 'closed' | 'host_error' | 'invalid_request' | 'invalid_response' | 'limit' | 'protocol' | 'timeout';
|
|
241
|
+
|
|
242
|
+
export declare interface AstratoIframeBridgeRequest {
|
|
243
|
+
method: AstratoHttpMethod;
|
|
244
|
+
url: string;
|
|
245
|
+
headers: Record<string, string>;
|
|
246
|
+
body?: unknown;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export declare interface AstratoIframeHostBridge {
|
|
250
|
+
destroy(): void;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export declare interface AstratoIframeHostBridgeOptions {
|
|
254
|
+
iframe: HTMLIFrameElement;
|
|
255
|
+
/** Exact child origin, or "null" for an intentionally sandboxed opaque iframe. */
|
|
256
|
+
childOrigin: string;
|
|
257
|
+
context: AstratoIframeBridgeContext;
|
|
258
|
+
request: (request: AstratoIframeBridgeRequest, signal: AbortSignal) => Promise<unknown>;
|
|
259
|
+
timeoutMs?: number;
|
|
260
|
+
maxConcurrentRequests?: number;
|
|
261
|
+
/** Set true when attaching after this iframe has already fired its initial load event. */
|
|
262
|
+
initialLoadComplete?: boolean;
|
|
263
|
+
/** Useful when the embedding document is not the current global window. */
|
|
264
|
+
hostWindow?: Window;
|
|
265
|
+
/** Uses the host realm's MessageChannel by default. */
|
|
266
|
+
channelFactory?: () => MessageChannel;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export declare type AstratoLogContext = Record<string, unknown>;
|
|
270
|
+
|
|
271
|
+
export declare interface AstratoLogger {
|
|
272
|
+
debug(message: string, context?: AstratoLogContext): void;
|
|
273
|
+
info(message: string, context?: AstratoLogContext): void;
|
|
274
|
+
warn(message: string, context?: AstratoLogContext): void;
|
|
275
|
+
error(message: string, context?: AstratoLogContext): void;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export declare interface AstratoMetadataCacheOptions {
|
|
279
|
+
/** Maximum number of completed metadata snapshots retained by one engine client. */
|
|
280
|
+
maxEntries?: number;
|
|
281
|
+
/** Time-to-live for a completed metadata snapshot in milliseconds. */
|
|
282
|
+
ttlMs?: number;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export declare interface AstratoModelService {
|
|
286
|
+
saveProperties(propertiesOrUpdater: unknown, config?: unknown): Promise<void>;
|
|
287
|
+
saveLocalVSpec(vSpec: unknown): Promise<void>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export declare interface AstratoQueryInsightsRequest {
|
|
291
|
+
semanticLayerId: string;
|
|
292
|
+
queryID: string;
|
|
293
|
+
startTime?: number;
|
|
294
|
+
tenantId?: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export declare interface AstratoQueryInsightsResponse {
|
|
298
|
+
cacheUsed: boolean;
|
|
299
|
+
metaDataQuery: boolean;
|
|
300
|
+
creditsBestCase: number;
|
|
301
|
+
creditsWorstCase: number;
|
|
302
|
+
creditsFairValue: number;
|
|
303
|
+
rowsScanned: number;
|
|
304
|
+
joinIssue: boolean;
|
|
305
|
+
unionIssue: boolean;
|
|
306
|
+
memoryIssue: boolean;
|
|
307
|
+
pruningIssue: boolean;
|
|
308
|
+
bigquery?: AstratoBigQueryInsights;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export declare interface AstratoQueryMetadata {
|
|
312
|
+
queryID: string;
|
|
313
|
+
duration?: number;
|
|
314
|
+
startTime?: number;
|
|
315
|
+
statement?: string;
|
|
316
|
+
queryLink?: string;
|
|
317
|
+
rows?: number;
|
|
318
|
+
semanticLayerId?: string;
|
|
319
|
+
vendor?: string;
|
|
320
|
+
warnings?: unknown[];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export declare interface AstratoQueryRequest {
|
|
324
|
+
tenantId?: string;
|
|
325
|
+
semanticLayerId?: string;
|
|
326
|
+
query: unknown;
|
|
327
|
+
context?: AstratoDataRequestContext;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export declare type AstratoQueryResult<TQueryResult = unknown> = AstratoDataResponse<TQueryResult>;
|
|
331
|
+
|
|
332
|
+
export declare interface AstratoRequest<TBody = unknown> {
|
|
333
|
+
method?: AstratoHttpMethod;
|
|
334
|
+
url: string;
|
|
335
|
+
headers?: HeadersInit;
|
|
336
|
+
body?: TBody;
|
|
337
|
+
signal?: AbortSignal;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export declare type AstratoRequestExecutor = <TResponse = unknown, TBody = unknown>(request: AstratoRequest<TBody>) => Promise<TResponse>;
|
|
341
|
+
|
|
342
|
+
export declare interface AstratoSelection {
|
|
343
|
+
field: AstratoFieldReference;
|
|
344
|
+
values: AstratoSelectionValue[];
|
|
345
|
+
operator?: AstratoSelectionOperator;
|
|
346
|
+
kind?: Exclude<AstratoSelectionKind, 'ALL'>;
|
|
347
|
+
sourceInstanceId?: string;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export declare type AstratoSelectionKind = 'VALUE' | 'ASSOCIATIVE' | 'ALL';
|
|
351
|
+
|
|
352
|
+
export declare type AstratoSelectionMode = 'auto' | 'manual';
|
|
353
|
+
|
|
354
|
+
export declare type AstratoSelectionOperator = 'equals' | 'notEquals' | 'inList' | 'notInList' | 'gt' | 'gte' | 'lt' | 'lte' | 'set' | 'notSet' | 'between' | 'notBetween' | 'like' | 'notLike' | 'contains' | 'notContains' | 'geointersects' | 'in' | 'notIn' | 'range';
|
|
355
|
+
|
|
356
|
+
export declare interface AstratoSelectionService {
|
|
357
|
+
readonly singleSelectFields$: Observable<AstratoSingleSelectField[]>;
|
|
358
|
+
selectionMode: AstratoSelectionMode;
|
|
359
|
+
getSelectionMode(groupId?: string): AstratoSelectionMode;
|
|
360
|
+
setSelectionMode(selectionMode: AstratoSelectionMode, groupId?: string): void;
|
|
361
|
+
getSelection(groupId: string): AstratoSelectionState;
|
|
362
|
+
getActiveSelection(kind?: AstratoSelectionKind, groupId?: string): AstratoActiveSelection;
|
|
363
|
+
selectValues(groupId: string, selection: AstratoSelection, options?: AstratoSelectionUpdateOptions): void;
|
|
364
|
+
declineSelection(groupId: string, selection: AstratoFieldReference | AstratoSelection): void;
|
|
365
|
+
removeSelection(groupId: string, selection: AstratoFieldReference | AstratoSelection): void;
|
|
366
|
+
setNewSelectionState(groupId: string, selection: AstratoSelectionState): void;
|
|
367
|
+
applyPendingSelections(groupId?: string): void;
|
|
368
|
+
clearPendingSelections(groupId?: string): void;
|
|
369
|
+
clearAllSelections(groupId?: string): void;
|
|
370
|
+
onActiveSelectionChange(groupId?: string): Observable<AstratoActiveSelection>;
|
|
371
|
+
onActiveSelectionChangeApplied(groupId?: string): Observable<AstratoActiveSelection>;
|
|
372
|
+
onActiveSelectionChangePending(groupId?: string): Observable<AstratoActiveSelection>;
|
|
373
|
+
isFieldLocked(field: AstratoFieldReference, groupId?: string): boolean;
|
|
374
|
+
isFieldLocked$(field: AstratoFieldReference, groupId?: string): Observable<boolean>;
|
|
375
|
+
setFieldLocked(field: AstratoFieldReference, locked: boolean, groupId?: string): void;
|
|
376
|
+
toggleFieldLocked(field: AstratoFieldReference, groupId?: string): void;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export declare interface AstratoSelectionState {
|
|
380
|
+
applied: AstratoSelection[];
|
|
381
|
+
pending: AstratoSelection[];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export declare interface AstratoSelectionUpdateOptions {
|
|
385
|
+
state?: 'applied' | 'pending';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export declare type AstratoSelectionValue = string | number | boolean | null;
|
|
389
|
+
|
|
390
|
+
export declare interface AstratoSingleSelectField {
|
|
391
|
+
field: AstratoFieldReference;
|
|
392
|
+
enabled: boolean;
|
|
393
|
+
defaultValue?: AstratoSelectionValue;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export declare interface AstratoTransport {
|
|
397
|
+
request<TResponse = unknown, TBody = unknown>(request: AstratoRequest<TBody>): Promise<TResponse>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export declare class AstratoTransportError extends Error {
|
|
401
|
+
readonly responseBody?: unknown;
|
|
402
|
+
readonly status?: number;
|
|
403
|
+
constructor(message: string, options?: AstratoTransportErrorOptions);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export declare interface AstratoTransportErrorOptions {
|
|
407
|
+
status?: number;
|
|
408
|
+
responseBody?: unknown;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export declare interface AstratoTransportOptions {
|
|
412
|
+
baseUrl?: string;
|
|
413
|
+
defaultHeaders?: HeadersInit;
|
|
414
|
+
fetch?: typeof fetch;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export declare interface AstratoVariable {
|
|
418
|
+
id: string;
|
|
419
|
+
name: string;
|
|
420
|
+
value: unknown;
|
|
421
|
+
contextId?: string;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export declare interface AstratoVariableService {
|
|
425
|
+
getVariables(contextId?: string): Promise<AstratoVariable[]>;
|
|
426
|
+
resolveValue(nameOrId: string, contextId?: string): Promise<unknown>;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export declare const connectAstratoIframeBridge: (options: ConnectAstratoIframeBridgeOptions) => Promise<AstratoIframeBridgeConnection>;
|
|
430
|
+
|
|
431
|
+
export declare interface ConnectAstratoIframeBridgeOptions {
|
|
432
|
+
/** The exact trusted parent origin. Wildcards and opaque parent origins are not accepted. */
|
|
433
|
+
parentOrigin: string;
|
|
434
|
+
timeoutMs?: number;
|
|
435
|
+
/** Uses the current global window by default. */
|
|
436
|
+
guestWindow?: Window;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export declare const createAstratoClient: (options: CreateAstratoClientOptions) => AstratoClient;
|
|
440
|
+
|
|
441
|
+
export declare interface CreateAstratoClientOptions {
|
|
442
|
+
baseUrl: string;
|
|
443
|
+
tenantId?: string;
|
|
444
|
+
context?: AstratoDataRequestContext;
|
|
445
|
+
auth?: AstratoAuthProvider;
|
|
446
|
+
transport?: AstratoTransport;
|
|
447
|
+
/** Enables the SDK's default visualization-facing engine host service. */
|
|
448
|
+
visualizationEngine?: boolean;
|
|
449
|
+
/** Bounds the per-client visualization metadata snapshot cache. */
|
|
450
|
+
metadataCache?: AstratoMetadataCacheOptions;
|
|
451
|
+
hostServices?: PartialAstratoHostServices;
|
|
452
|
+
logger?: AstratoLogger;
|
|
453
|
+
events?: AstratoEventBus;
|
|
454
|
+
endpoints?: AstratoClientEndpoints;
|
|
455
|
+
sessionId?: string;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export declare const createAstratoEventBus: () => AstratoEventBus;
|
|
459
|
+
|
|
460
|
+
export declare const createAstratoIframeHostBridge: (options: AstratoIframeHostBridgeOptions) => AstratoIframeHostBridge;
|
|
461
|
+
|
|
462
|
+
export declare const createConsoleLogger: () => AstratoLogger;
|
|
463
|
+
|
|
464
|
+
export declare const createDirectDataService: (request: AstratoRequestExecutor, options?: CreateDirectDataServiceOptions) => AstratoDataService;
|
|
465
|
+
|
|
466
|
+
export declare interface CreateDirectDataServiceOptions {
|
|
467
|
+
context?: AstratoDataRequestContext;
|
|
468
|
+
endpoints?: AstratoDataServiceEndpoints;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export declare const createFetchTransport: (options?: AstratoTransportOptions) => AstratoTransport;
|
|
472
|
+
|
|
473
|
+
export declare const createMemorySelectionService: (initialSelectionMode?: AstratoSelectionMode) => AstratoSelectionService;
|
|
474
|
+
|
|
475
|
+
export declare const createMemoryVariableService: (initialVariables?: AstratoVariable[]) => AstratoVariableService;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Creates the visualization-facing engine service from the SDK's direct data
|
|
479
|
+
* service. Composite metadata is loaded asynchronously once and exposed through
|
|
480
|
+
* the synchronous field and connectivity methods required by current wrappers.
|
|
481
|
+
*/
|
|
482
|
+
export declare const createVisualizationEngineService: (data: AstratoDataService, options?: CreateVisualizationEngineServiceOptions) => AstratoEngineService;
|
|
483
|
+
|
|
484
|
+
export declare interface CreateVisualizationEngineServiceOptions {
|
|
485
|
+
context?: AstratoDataRequestContext;
|
|
486
|
+
metadataCache?: AstratoMetadataCacheOptions;
|
|
487
|
+
/** Identifies the current auth authority before a metadata cache lookup. */
|
|
488
|
+
metadataCacheAuthority?: () => string | Promise<string>;
|
|
489
|
+
/**
|
|
490
|
+
* Binds engine metadata/query requests to one authentication snapshot; takes precedence over metadataCacheAuthority.
|
|
491
|
+
* Register onAuthorityChange to report subsequent authentication changes, including from ordinary requests.
|
|
492
|
+
*/
|
|
493
|
+
metadataLoadContext?: (onAuthorityChange: (authority: string) => void) => Promise<{
|
|
494
|
+
authority: string;
|
|
495
|
+
data: AstratoDataService;
|
|
496
|
+
isCurrent(): boolean;
|
|
497
|
+
}>;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export declare const getAstratoIframeBridge: () => AstratoIframeBridgeConnection | undefined;
|
|
501
|
+
|
|
502
|
+
export declare type PartialAstratoHostServices = Partial<AstratoHostServices>;
|
|
503
|
+
|
|
504
|
+
export { }
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Subject as mt,filter as Ne,BehaviorSubject as ce}from"rxjs";const Ge=(e,t)=>t?!(t.groupId&&e?.groupId!==t.groupId||t.instanceId&&e?.instanceId!==t.instanceId||t.sessionId&&e?.sessionId!==t.sessionId):!0,ht=(e,t)=>e.name===t,wt=()=>{const e=new mt,t=new Set,r=n=>(t.add(n),()=>{n.unsubscribe(),t.delete(n)});return{events$:e.asObservable(),emit:(n,a,i={})=>{e.next({name:n,payload:a,scope:i.scope,source:i.source,timestamp:i.timestamp??Date.now()})},on:(n,a,i)=>r(e.pipe(Ne(l=>ht(l,n)&&Ge(l.scope,i?.scope))).subscribe(a)),onAny:(n,a)=>r(e.pipe(Ne(i=>Ge(i.scope,a?.scope))).subscribe(i=>{n(i)})),clear:()=>{t.forEach(n=>{n.unsubscribe()}),t.clear()}}},vt={aggregations:(e,t)=>`api/message/tenants/${encodeURIComponent(e)}/data/aggrfunctions/${encodeURIComponent(t)}`,capabilities:(e,t)=>`api/message/tenants/${encodeURIComponent(e)}/data/capabilities/${encodeURIComponent(t)}`,datasource:(e,t)=>`api/message/tenants/${encodeURIComponent(e)}/schema/datasources/${encodeURIComponent(t)}`,queryInsights:e=>`api/message/tenants/${encodeURIComponent(e)}/data/queryinsights`,semanticLayerMetadata:(e,t)=>`api/message/tenants/${encodeURIComponent(e)}/schema/dataviews/${encodeURIComponent(t)}/composite`,query:e=>`api/message/tenants/${encodeURIComponent(e)}/data/execute/query`},bt={objectId:"x-metadata-object-id",role:"x-metadata-role-type",sheetId:"x-metadata-sheet-id",tenantId:"x-metadata-tenant-id",userId:"x-metadata-user-id",workbookState:"x-metadata-workbook-state",workbookVersionContainerId:"x-metadata-workbook-id"},Qe=e=>{const t={};return Object.entries(bt).forEach(([r,n])=>{const a=e[r];a&&(t[n]=a)}),t},At=(e,t)=>({...t,...e.context,tenantId:e.tenantId??e.context?.tenantId??t?.tenantId}),It=(e,t)=>typeof e=="function"?e(t):e,de=(e,t)=>e??t?.tenantId;class Et extends Error{constructor(){super("Astrato data service capacity interrupted the query. Retry it later."),this.code="service_capacity",this.name="AstratoDataCapacityError"}}const St=e=>{const t=e&&typeof e=="object"&&"response"in e?e.response:e;if(t&&typeof t=="object"&&"meta"in t){const r=t.meta;if(r&&typeof r=="object"&&"truncated"in r&&r.truncated===!0&&"truncationReason"in r&&r.truncationReason==="service_capacity")throw new Et}return e},Dt=e=>Array.isArray(e)?e:e&&typeof e=="object"&&"response"in e&&Array.isArray(e.response)?e.response:[],nt=e=>{if(e&&typeof e=="object"&&"response"in e){const t=e.response;return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}return e&&typeof e=="object"&&!Array.isArray(e)?e:{}},Lt=["cacheUsed","metaDataQuery","joinIssue","unionIssue","memoryIssue","pruningIssue"],Tt=["creditsBestCase","creditsWorstCase","creditsFairValue","rowsScanned"],Ct=e=>Lt.every(t=>typeof e[t]=="boolean")&&Tt.every(t=>typeof e[t]=="number"&&Number.isFinite(e[t])),Mt=e=>{const t=nt(e);if(!Ct(t))throw new Error("Astrato SDK query insights response is invalid.");return t},He=(e,t={})=>{const r={...vt,...t.endpoints};return{query:async n=>{const a=At(n,t.context);if(!a.tenantId)throw new Error("Astrato SDK data query requires tenantId in client options or query request.");const i=await e({body:n.query,headers:Qe(a),method:"POST",url:It(r.query,a.tenantId)});return St(i)},getQueryInsights:async n=>{const a=de(n.tenantId,t.context);if(!a)throw new Error("Astrato SDK query insights requires tenantId in client options or insights request.");const i=await e({body:{databaseID:n.semanticLayerId,queryID:n.queryID,...n.startTime===void 0?{}:{startTime:n.startTime}},headers:Qe({...t.context,tenantId:a}),method:"POST",url:r.queryInsights(a)});return Mt(i)},getSemanticLayerMetadata:async(n,a)=>{const i=de(a,t.context);if(!i)throw new Error("Astrato SDK semantic layer metadata requires tenantId in client options or request.");return e({method:"GET",url:r.semanticLayerMetadata(i,n)})},getDatasource:async(n,a)=>{const i=de(a,t.context);if(!i)throw new Error("Astrato SDK datasource metadata requires tenantId in client options or request.");return e({method:"GET",url:r.datasource(i,n)})},getAggregations:async(n,a)=>{const i=de(a,t.context);if(!i)throw new Error("Astrato SDK aggregation metadata requires tenantId in client options or request.");const l=await e({method:"GET",url:r.aggregations(i,n)});return Dt(l)},getCapabilities:async(n,a)=>{const i=de(a,t.context);if(!i)throw new Error("Astrato SDK capability metadata requires tenantId in client options or request.");const l=await e({method:"GET",url:r.capabilities(i,n)});return nt(l)}}},xt="default",H=e=>e??xt,We=()=>({applied:[],pending:[]}),ue=e=>({...e,field:{...e.field},kind:e.kind??"VALUE",values:[...e.values]}),we=e=>({applied:e.applied.map(ue),pending:e.pending.map(ue)}),Je=(e,t)=>t==="ALL"?!0:(e.kind??"VALUE")===t,Me=(e,t)=>({applied:e.applied.filter(r=>Je(r,t)).map(ue),pending:e.pending.filter(r=>Je(r,t)).map(ue)}),ae=e=>e.fieldKey,Kt=e=>e.kind??"VALUE",Ee=e=>`${Kt(e)}:${ae(e.field)}`,Xe=(e,t)=>{if("field"in t){const n=Ee(t);return e.filter(a=>Ee(a)!==n)}const r=ae(t);return e.filter(n=>ae(n.field)!==r)},Ie=(e,t)=>{const r=ue(t),n=Ee(r);let a=!1;const i=e.map(l=>Ee(l)!==n?l:(a=!0,r));return a?i:[...i,r]},Ze=e=>e.reduce(Ie,[]),kt=(e="auto")=>{const t=new Map,r=new Map,n=new Map,a=new Map,i=new Map,l=new ce([]),u=o=>{const c=H(o);let p=t.get(c);return p||(p=We(),t.set(c,p)),p},m=o=>{const c=H(o);let p=n.get(c);if(!p){const y=Me(u(c),"ALL");p={active:new ce(we(y)),applied:new ce(we(y)),pending:new ce(we(y))},n.set(c,p)}return p},f=(o,c)=>{const p=H(o),y=Me(u(p),"ALL"),w=m(p);w.active.next(y),(c==="applied"||c==="both")&&w.applied.next(y),(c==="pending"||c==="both")&&w.pending.next(y)},g=o=>r.get(H(o))??e,h=(o,c)=>{r.set(H(c),o)},I=(o,c)=>a.get(H(c))?.has(ae(o))??!1,E=o=>{const c=u(o);c.pending.length!==0&&(c.applied=c.pending.reduce(Ie,c.applied),c.pending=[],f(o,"both"))},b=o=>{const c=u(o);c.pending.length!==0&&(c.pending=[],f(o,"pending"))},P=o=>{t.set(H(o),We()),f(o,"both")},S=(o,c)=>`${H(o)}:${ae(c)}`,T=(o,c)=>{const p=H(c),y=S(p,o);let w=i.get(y);return w||(w=new ce(!1),i.set(y,w)),w},x=(o,c,p)=>{const y=H(p);let w=a.get(y);w||(w=new Set,a.set(y,w));const K=ae(o);c?w.add(K):w.delete(K),T(o,y).next(c)};return{singleSelectFields$:l.asObservable(),get selectionMode(){return g()},set selectionMode(o){h(o)},getSelectionMode:o=>g(o),setSelectionMode:(o,c)=>{h(o,c)},getSelection:o=>we(u(o)),getActiveSelection:(o="VALUE",c)=>Me(u(c),o),selectValues:(o,c,p)=>{const y=u(o);if(I(c.field,o))return;if((p?.state??(g(o)==="manual"?"pending":"applied"))==="pending"){y.pending=Ie(y.pending,c),f(o,"pending");return}y.applied=Ie(y.applied,c),f(o,"applied")},declineSelection:(o,c)=>{const p=u(o);p.pending=Xe(p.pending,c),f(o,"pending")},removeSelection:(o,c)=>{const p=u(o);p.applied=Xe(p.applied,c),f(o,"applied")},setNewSelectionState:(o,c)=>{t.set(H(o),{applied:Ze(c.applied),pending:Ze(c.pending)}),f(o,"both")},applyPendingSelections:o=>{if(o){E(o);return}Array.from(t.keys()).forEach(E)},clearPendingSelections:o=>{if(o){b(o);return}Array.from(t.keys()).forEach(b)},clearAllSelections:o=>{if(o){P(o);return}t.clear(),n.forEach((c,p)=>{f(p,"both")})},onActiveSelectionChange:o=>m(o).active.asObservable(),onActiveSelectionChangeApplied:o=>m(o).applied.asObservable(),onActiveSelectionChangePending:o=>m(o).pending.asObservable(),isFieldLocked:(o,c)=>I(o,c),isFieldLocked$:(o,c)=>T(o,c).asObservable(),setFieldLocked:x,toggleFieldLocked:(o,c)=>{x(o,!I(o,c),c)}}},Ft=(e=[])=>{const t=new Map;return e.forEach(r=>{t.set(r.id,r)}),{getVariables:r=>Promise.resolve(Array.from(t.values()).filter(n=>!r||n.contextId===r)),resolveValue:(r,n)=>{const a=Array.from(t.values()).find(i=>(i.id===r||i.name===r)&&(!n||i.contextId===n));return Promise.resolve(a?.value)}}},jt=()=>({saveProperties:async(e,t)=>{},saveLocalVSpec:async e=>{}}),zt=()=>({query:()=>Promise.reject(new Error("Astrato SDK data service is not configured. Provide a data service.")),getSemanticLayerMetadata:()=>Promise.reject(new Error("Astrato SDK semantic layer metadata service is not configured. Provide a data service.")),getAggregations:()=>Promise.reject(new Error("Astrato SDK aggregation metadata service is not configured. Provide a data service.")),getCapabilities:()=>Promise.reject(new Error("Astrato SDK capability metadata service is not configured. Provide a data service.")),getDatasource:()=>Promise.reject(new Error("Astrato SDK datasource metadata service is not configured. Provide a data service."))}),V=()=>new Error("Astrato SDK engine service is not configured. Provide an engine service."),Pt=()=>({get capabilities(){throw V()},get databaseInfo(){throw V()},get localeSettings(){throw V()},fields:{getFieldMetadata:()=>Promise.reject(V()),getDimensionsList:()=>{throw V()},getFields:()=>{throw V()},getGroupedDimensions:()=>{throw V()},getMeasuresList:()=>{throw V()},getValidFields:()=>{throw V()},getViewsFields:()=>{throw V()}},transformQuery:()=>{throw V()},getData:()=>Promise.reject(V()),getLastQueryMetadata:()=>{throw V()},getQueryInsights:()=>Promise.reject(V()),areFieldsConnected:()=>{throw V()},areViewsConnected:()=>{throw V()}}),$t=(e={})=>({data:e.data??zt(),engine:e.engine??Pt(),model:e.model??jt(),selections:e.selections??kt(),variables:e.variables??Ft()}),D=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),O=(e,t)=>{const r=e?.[t];return typeof r=="string"?r:void 0},ot=e=>D(e)&&typeof e.name=="string"&&typeof e.view=="string",Ot=(e,t)=>{const r=e[t];return Array.isArray(r)?r:void 0},ve=(e,t)=>{const r=e[t];if(r==null)return[];if(!Array.isArray(r)||!r.every(D))throw new Error(`Astrato SDK visualization engine expected composite metadata.${t} to be an object array.`);return r},xe=(e,t,r)=>{const n=O(e,t);if(!n)throw new Error(`Astrato SDK visualization engine expected ${r}.${t} to be a non-empty string.`);return n},Ye=e=>{if(!D(e))return;const t=e.fieldKey??e.key;return typeof t=="string"?t:void 0},Rt=(e,t)=>{const r=new Map(e.map(i=>[i,i])),n=i=>{const l=r.get(i);if(!l||l===i)return i;const u=n(l);return r.set(i,u),u},a=(i,l)=>{if(!r.has(i)||!r.has(l))return;const u=n(i),m=n(l);u!==m&&r.set(m,u)};return t.forEach(i=>{const l=O(i,"viewID1"),u=O(i,"viewID2");l&&u&&a(l,u)}),new Map(e.map(i=>[i,n(i)]))},et=e=>{if(!Array.isArray(e.views))throw"schemas"in e?new Error("Astrato SDK visualization engine requires the tenant-scoped data view composite endpoint; legacy schemas metadata is not supported."):new Error("Astrato SDK visualization engine expected composite metadata.views to be an object array.");const t=ve(e,"views"),r=ve(e,"joins"),n=ve(e,"items"),a=[],i=new Map,l=new Map,u=[];return t.forEach((m,f)=>{const g=xe(m,"id",`composite metadata.views[${f}]`),h=xe(m,"name",`composite metadata.views[${f}]`),I=l.get(h);if(I&&I!==g)throw new Error(`Astrato SDK visualization engine expected composite view name "${h}" to be unique.`);u.push(g),l.set(h,g),ve(m,"fields").forEach((E,b)=>{const P=xe(E,"key",`composite metadata.views[${f}].fields[${b}]`);if(i.has(P))throw new Error(`Astrato SDK visualization engine expected field key "${P}" to be unique.`);const S={field:E,view:m,viewId:g};a.push(S),i.set(P,S)})}),{componentByViewId:Rt(u,r),fieldEntries:a,fieldEntriesByKey:i,items:n,viewIdsByName:l}},Se=(e,t,r={throwOnUnknownFieldKey:!0})=>{if(ot(t)&&!Ye(t))throw new Error("Astrato SDK visualization engine requires fieldKey/key field identifiers. Legacy { view, name } field identifiers are not supported.");const n=Ye(t);if(!n)return;const a=e.fieldEntriesByKey.get(n);if(!a&&r.throwOnUnknownFieldKey!==!1)throw new Error(`Astrato SDK visualization engine could not find fieldKey "${n}".`);return a},ee=(e,t)=>e.componentByViewId.get(t),Vt=(e,t)=>{if(!t)return e.fieldEntries;const r=Se(e,t);if(!r)return[];const n=ee(e,r.viewId);return e.fieldEntries.filter(a=>ee(e,a.viewId)===n)},$e=(e,t)=>{const r=new Set;return t.forEach(n=>{const a=e.viewIdsByName.get(n),i=a?ee(e,a):void 0;i&&r.add(i)}),r},Ut=(e,t)=>{if(!t.length)return e.fieldEntries;const r=$e(e,t);return r.size?e.fieldEntries.filter(n=>{const a=ee(e,n.viewId);return a?r.has(a):!1}):[]},_t=(e,t,r)=>{const n=$e(e,t),a=$e(e,r);return Array.from(n).some(i=>a.has(i))},Bt=(e,t,r)=>{const n=Se(e,t),a=Se(e,r);return!n||!a?!1:ee(e,n.viewId)===ee(e,a.viewId)},De=(e,t,r)=>{const n=e.items.filter(l=>t.includes(String(l.type)));if(!r)return n;const a=Se(e,r);if(!a)return[];const i=ee(e,a.viewId);return n.filter(l=>(Ot(l,"views")??[]).some(m=>{if(typeof m!="string")return!1;const f=e.viewIdsByName.get(m);return f?ee(e,f)===i:!1}))},Oe="anytype",qt="boolean",Nt="geography",Gt="numeric",Qt="string",Ht=new Set(["date","time","timestamp","timestamp_tz"]),Wt=new Set(["decimal","float","int"]),Jt=e=>{const t=e?.toLowerCase();return t?Wt.has(t)?Gt:t==="string"||Ht.has(t)?Qt:t==="boolean"?qt:t==="geography"?Nt:Oe:Oe},Xt=(e,t)=>{const r=e.dataType?.toLowerCase();return!r||r===Oe?!0:r===Jt(t)},Zt=(e,t)=>{const r=O(e.view,"schemaSource"),n=O(e.field,"dataType");return{...e.field,aggregators:t.filter(a=>Xt(a,n)),...r?{schema:r}:{},view:O(e.view,"name")}},at=(e,t)=>e.map(r=>Zt(r,t)),Re=(e,t,r)=>at(Vt(e,r),t),Yt=(e,t,r)=>at(Ut(e,r),t),it=e=>e.flatMap(t=>{if(!D(t)||!D(t.dimension))return[];const r=t.dimension.group;if(!D(r)||!Array.isArray(r.fieldKeys)||r.fieldKeys.length===0)return[];const n=Array.from(new Set([O(t,"id"),O(t,"key")].filter(a=>!!a)));return n.length?[{identifiers:n,levelCount:r.fieldKeys.length}]:[]}),er=e=>Object.fromEntries(it(e).flatMap(({identifiers:t})=>t.map(r=>[r,0]))),tr=(e,t)=>{const r=Re(e,t),n=De(e,["dimension"]);return{dimensions:n,fields:r,groupedDimensions:er(n),measures:De(e,["measure","calculation"]),validFields:r,viewsFields:r}},rr=e=>typeof e.fieldKey=="string"||typeof e.key=="string",oe=e=>D(e)&&typeof e.semanticLayerId=="string"?e.semanticLayerId:void 0,nr=e=>{if(!D(e)||!D(e.query))return;const t=D(e.query.query)?e.query.query:e.query;return typeof t.databaseID=="string"?t.databaseID:void 0},Ke=e=>{if(!D(e))return;if(typeof e.tenantId=="string")return e.tenantId;const t=e.context;return D(t)&&typeof t.tenantId=="string"?t.tenantId:void 0},be=e=>{if(!D(e))return;if(typeof e.datasourceType=="string")return e.datasourceType;const t=e.context;return D(t)&&typeof t.datasourceType=="string"?t.datasourceType:void 0},or=e=>{if(!D(e))return;const t=e.context;return D(t)&&typeof t.objectId=="string"?t.objectId:void 0},ke=e=>!D(e)||rr(e)||ot(e)?e:e.selectedField,ar=e=>{if(Array.isArray(e))return e;if(!D(e))return[];const t=e.selectedFields;return Array.isArray(t)?t:[]},ir=e=>{if(Array.isArray(e))return e.filter(r=>typeof r=="string");if(!D(e))return[];const t=e.views;return Array.isArray(t)?t.filter(r=>typeof r=="string"):typeof e.view=="string"?[e.view]:[]},sr=e=>D(e)&&D(e.query),cr=e=>{if(sr(e.query))return e.query;if(!D(e.query))return{query:e.query};const t=e.query.databaseID??e.semanticLayerId;return{query:{...e.query,...t?{databaseID:t}:{}}}},dr=16,lr=300*1e3,M=e=>Array.isArray(e)?e.map(t=>M(t)):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([t,r])=>[t,M(r)])):e,Fe=(e,t)=>e.scope.tenantId===t.tenantId&&e.scope.semanticLayerId===t.semanticLayerId&&(t.datasourceType===void 0||e.scope.datasourceType===t.datasourceType),ur=e=>O(e,"datasourceID"),je=(e,t)=>{const r=e?.[t];return typeof r=="number"&&Number.isFinite(r)?r:void 0},fr=(e,t,r)=>{const n=D(e)?e:void 0,a=n&&D(n.response)?n.response:n,i=O(a,"queryID");if(!i)return;const l=n&&D(n.telemetry)?n.telemetry:void 0,u=a&&D(a.meta)?a.meta:void 0,m=a&&Array.isArray(a.warnings)?a.warnings:void 0,f=je(l,"duration"),g=je(l,"start"),h=O(a,"statement"),I=O(a,"queryLink"),E=je(u,"rows"),b=oe(t);return{queryID:i,...f===void 0?{}:{duration:f},...g===void 0?{}:{startTime:g},...h===void 0?{}:{statement:h},...I===void 0?{}:{queryLink:I},...E===void 0?{}:{rows:E},...b===void 0?{}:{semanticLayerId:b},...r===void 0?{}:{vendor:r},...m===void 0?{}:{warnings:[...m]}}},gr=e=>({...e,...e.warnings?{warnings:[...e.warnings]}:{}}),Ae=()=>new Error("Astrato SDK visualization engine metadata is not loaded. Call engine.fields.getFieldMetadata({ semanticLayerId }) or provide a custom engine host service."),pr=(e,t)=>!t&&!e.context?e:{...e,context:{...t,...e.context,tenantId:e.tenantId??e.context?.tenantId??t?.tenantId}},yr=(e,t={})=>{let r,n,a,i,l=[],u={},m,f={},g=Promise.resolve(),h,I,E=0;const b=new Map,P=new Set,S=t.metadataCache?.maxEntries??dr,T=t.metadataCache?.ttlMs??lr;if(!Number.isSafeInteger(S)||S<=0)throw new Error("Astrato SDK visualization engine metadata cache maxEntries must be a positive integer.");if(!Number.isFinite(T)||T<=0)throw new Error("Astrato SDK visualization engine metadata cache ttlMs must be positive.");const x=new Map,o=new Map,c=()=>{if(!n)throw Ae();return n},p=()=>{if(!a)throw Ae();return a},y=async(s,d,A,v)=>{const C=be(d);if(C)return C;const j=ur(s);if(!j||!v.getDatasource)return;const F=await v.getDatasource(j,A),z=D(F)?O(F,"driverName"):void 0;if(!z)throw new Error(`Astrato SDK visualization engine expected datasource "${j}" metadata to include driverName.`);return z},w=(s,d)=>{if(!s)throw new Error(`Astrato SDK visualization engine requires datasource type to load ${d}. Provide datasourceType in the field metadata request or ensure composite metadata includes datasourceID and data.getDatasource is configured.`);return s},K=async(s,d,A)=>{if(!A.getSemanticLayerMetadata)throw new Error("Astrato SDK visualization engine requires data.getSemanticLayerMetadata(semanticLayerId).");const v=Ke(s),C=await A.getSemanticLayerMetadata(d,v);if(!D(C))throw new Error("Astrato SDK visualization engine expected composite semantic layer metadata to be an object.");const j=et(C),F=await y(C,s,v,A),[z,B]=await Promise.all([A.getAggregations?A.getAggregations(w(F,"aggregation metadata"),v):void 0,A.getCapabilities?A.getCapabilities(w(F,"capability metadata"),v):void 0]),Q=z?z.filter(D):[],W=D(B)?B:{};return{rawMetadata:M(C),aggregations:M(Q),capabilities:M(W),datasourceType:F,fieldMetadata:M(tr(j,Q))}},_=()=>{r=void 0,n=void 0,a=void 0,i=void 0,l=[],u={},m=void 0,f={},h=void 0},k=()=>{for(;Array.from(b.values()).filter(s=>s.snapshot).length>S;){const s=Array.from(b.values()).find(d=>d.snapshot);if(!s)return;b.delete(s.key)}},$=(s,d)=>({tenantId:Ke(s)??t.context?.tenantId,semanticLayerId:d,datasourceType:be(s)??t.context?.datasourceType}),G=(s,d)=>({...D(s)?s:{},semanticLayerId:d.semanticLayerId,...d.tenantId===void 0?{}:{tenantId:d.tenantId},...d.datasourceType===void 0?{}:{datasourceType:d.datasourceType}}),ie=(s,d)=>JSON.stringify([s,d.tenantId??"",d.semanticLayerId,d.datasourceType??""]),ye=(s,d)=>b.get(ie(s,d)),me=(s,d)=>{const A=M(d.rawMetadata),v=et(A),C=M(d.aggregations),j=M(d.fieldMetadata);return r=s.scope.semanticLayerId,n=A,a=v,i=j,l=C,u=M(d.capabilities),m=d.datasourceType,f=M(s.groupedDimensions),i.groupedDimensions={...f},h=s,M(i)},Z=s=>(I!==void 0&&I!==s&&(b.forEach(d=>{d.invalidated=!0}),b.clear(),_()),I=s,s),Ue=s=>{const d=oe(s);if(!d){E+=1,P.forEach(v=>{v.invalidated=!0}),b.forEach(v=>{v.invalidated=!0}),b.clear(),_();return}const A=$(s,d);P.forEach(v=>{v.scope&&Fe({scope:v.scope},A)&&(v.invalidated=!0)}),b.forEach(v=>{Fe(v,A)&&(v.invalidated=!0,b.delete(v.key))}),h&&Fe(h,A)&&(h.invalidated=!0,_())},gt=async(s,d)=>{const A=E,v=t.metadataLoadContext?await t.metadataLoadContext(Z):void 0,C=v?v.authority:t.metadataCacheAuthority?await t.metadataCacheAuthority():"default",j=()=>{if(d.invalidated||A!==E)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.")};if(j(),v&&!v.isCurrent())throw new Error("Astrato SDK visualization engine authentication changed during metadata load; retry.");const F=Z(C),z=()=>{if(I!==F||v&&!v.isCurrent())throw new Error("Astrato SDK visualization engine authentication changed during metadata load; retry.")},B=async()=>{const N=!v&&t.metadataCacheAuthority?await t.metadataCacheAuthority():F;j(),v||Z(N),z()};z();const Q=oe(s);if(!Q){if(i)return M(i);throw Ae()}const W=d.scope??$(s,Q),q=G(s,W);let U=ye(F,W);if(U?.snapshot&&U.expiresAt<=Date.now()&&(U.invalidated=!0,b.delete(U.key),U=void 0),!U){const N=ie(F,W),R={key:N,authority:F,generation:E,scope:W,groupedDimensions:{},expiresAt:0,invalidated:!1,promise:Promise.resolve(void 0)},Ce=g.then(async()=>{if(d.invalidated||R.invalidated)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.");z();const se=await K(q,Q,v?.data??e);if(await B(),d.invalidated||R.invalidated)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.");if(R.generation!==E)throw R.invalidated=!0,new Error("Astrato SDK visualization engine metadata was invalidated; retry.");return R.snapshot=se,R.expiresAt=Date.now()+T,R.groupedDimensions=M(se.fieldMetadata.groupedDimensions??{}),R.invalidated||(b.delete(N),b.set(N,R),k()),me(R,se)}).catch(se=>{throw b.get(N)===R&&b.delete(N),se});return R.promise=Ce,b.set(N,R),g=Ce.then(()=>{},()=>{}),Ce}const J=U;if(J.snapshot){const N=J.snapshot;b.delete(J.key),b.set(J.key,J);const R=g.then(async()=>{if(await B(),d.invalidated||J.invalidated)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.");if(J.generation!==E)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.");return me(J,N)});return g=R.then(()=>{},()=>{}),R}return J.promise.then(N=>M(N))},_e=async s=>{const d=oe(s),A={scope:d?$(s,d):h?.scope,invalidated:!1};P.add(A);try{const v=await gt(s,A);if(A.invalidated)throw new Error("Astrato SDK visualization engine metadata was invalidated; retry.");return v}finally{P.delete(A)}},he=s=>{const d=oe(s);if(!i)throw Ae();if(d&&r!==d)throw new Error(`Astrato SDK visualization engine metadata is loaded for "${r}", not "${d}". Call engine.fields.getFieldMetadata({ semanticLayerId }) before reading field lists.`);if(h){const A=d?$(s,d):{tenantId:Ke(s)??t.context?.tenantId,semanticLayerId:h.scope.semanticLayerId,datasourceType:be(s)??t.context?.datasourceType},v=h.snapshot?.datasourceType??h.scope.datasourceType;if(A.tenantId!==void 0&&h.scope.tenantId!==A.tenantId)throw new Error(`Astrato SDK visualization engine metadata is loaded for tenant "${h.scope.tenantId}", not "${A.tenantId}". Call engine.fields.getFieldMetadata({ semanticLayerId, tenantId }) before reading field lists.`);if(A.datasourceType!==void 0&&v!==A.datasourceType)throw new Error(`Astrato SDK visualization engine metadata is loaded for datasource "${v}", not "${A.datasourceType}". Call engine.fields.getFieldMetadata({ semanticLayerId, datasourceType }) before reading field lists.`)}return i},re=(s,d)=>(he(s),d()),pt=s=>{const d=he(),A=it(d.dimensions??[]),v={...f};A.forEach(({identifiers:C,levelCount:j})=>{const F=C.map(q=>f[q]).find(q=>q!==void 0),z=C.flatMap(q=>{const U=s[q];if(U===void 0)return[];if(!Number.isInteger(U)||U<0||U>=j)throw new Error(`Astrato SDK visualization engine expected grouped dimension "${q}" level to be an integer between 0 and ${j-1}.`);return U===F?[]:[U]}),B=new Set(z);if(B.size>1)throw new Error(`Astrato SDK visualization engine received conflicting levels for grouped dimension identifiers ${C.map(q=>`"${q}"`).join(", ")}.`);const[Q]=B,W=Q??F??0;C.forEach(q=>{v[q]=W})}),f=v,i={...d,groupedDimensions:{...f}},h&&(h.groupedDimensions=M(f))},yt=s=>{if(!a||!D(s))return s;const d=O(s,"itemKey");if(!d)return s;const A=a.items.find(W=>O(W,"key")===d),v=A?.dimension;if(!A||!D(v)||!D(v.group))return s;const C=v.group.fieldKeys;if(!Array.isArray(C))return s;const j=O(A,"id"),F=(j?f[j]:void 0)??f[d]??0,z=C[F];if(typeof z!="string")return s;const B={...s,fieldKey:z},Q=O(a.fieldEntriesByKey.get(z)?.field,"name");return delete B.itemKey,Q&&(B.label=Q),B},Be=s=>D(s)?D(s.query)?{...s,query:Be(s.query)}:Array.isArray(s.dimensions)?{...s,dimensions:s.dimensions.map(yt)}:s:s,qe=s=>{if(i){he(s);const d=nr(s);d&&he({...s,semanticLayerId:d})}return cr({...s,query:Be(s.query)})};return{get capabilities(){return p(),M(u)},get databaseInfo(){const s=c();return M({...s,items:p().items})},get localeSettings(){const s=c().locale;return D(s)?M(s):{}},fields:{getFieldMetadata:_e,invalidateMetadata:Ue,refreshMetadata:async s=>{if(!oe(s))throw new Error("Astrato SDK metadata refresh requires semanticLayerId.");return Ue(s),_e(s)},getFields:s=>M(re(s,()=>Re(p(),l,ke(s)))),getDimensionsList:s=>M(re(s,()=>De(p(),["dimension"],ke(s)))),getGroupedDimensions:s=>M(re(s,()=>({...f}))),setGroupedDimensions:pt,getMeasuresList:s=>M(re(s,()=>De(p(),["measure","calculation"],ke(s)))),getValidFields:s=>M(re(s,()=>{const[d]=ar(s);return Re(p(),l,d)})),getViewsFields:s=>M(re(s,()=>Yt(p(),l,ir(s))))},transformQuery:qe,getData:async s=>{const d=pr(s,t.context),A=or(d),v=(o.get(A)??0)+1;o.set(A,v);const C=t.metadataLoadContext?await t.metadataLoadContext(Z):void 0;if(C){if(!C.isCurrent())throw new Error("Astrato SDK visualization engine authentication changed before query execution; retry.");Z(C.authority)}const j=be(d)??m,F=await(C?.data??e).query({...d,query:qe(d)});if(C&&!C.isCurrent())throw new Error("Astrato SDK visualization engine authentication changed during query execution; retry.");if(o.get(A)===v){const z=fr(F,d,j);z&&x.set(A,z)}return F},getLastQueryMetadata:s=>{const d=x.get(s);return d?gr(d):void 0},getQueryInsights:async s=>{if(!e.getQueryInsights)throw new Error("Astrato SDK visualization engine query insights service is not configured.");return e.getQueryInsights(s)},areFieldsConnected:(s,d)=>Bt(p(),s,d),areViewsConnected:(s,d=[])=>_t(p(),s,d)}},mr=()=>({debug:(e,t)=>{console.debug(e,t)},error:(e,t)=>{console.error(e,t)},info:(e,t)=>{console.info(e,t)},warn:(e,t)=>{console.warn(e,t)}});class hr extends Error{constructor(t,r={}){super(t),this.name="AstratoTransportError",this.responseBody=r.responseBody,this.status=r.status}}const wr=(e,t)=>e?new URL(t,e.endsWith("/")?e:`${e}/`).toString():t,vr=(e,t)=>{if(e!=null)return typeof e=="string"||typeof FormData<"u"&&e instanceof FormData||typeof URLSearchParams<"u"&&e instanceof URLSearchParams?e:(t.has("content-type")||t.set("content-type","application/json"),JSON.stringify(e))},br=async e=>{const t=await e.text();if(!t)return;if((e.headers.get("content-type")??"").includes("application/json"))try{return JSON.parse(t)}catch(n){if(e.ok)throw n;return t}return t},Ar=(e={})=>({request:async t=>{const r=e.fetch??globalThis.fetch?.bind(globalThis);if(!r)throw new Error("Fetch is not available. Provide a custom Astrato transport.");const n=new Headers(e.defaultHeaders);new Headers(t.headers).forEach((l,u)=>{n.set(u,l)});const a=await r(wr(e.baseUrl,t.url),{body:vr(t.body,n),headers:n,method:t.method??"GET",signal:t.signal}),i=await br(a);if(!a.ok)throw new hr(`Astrato request failed with status ${a.status}`,{responseBody:i,status:a.status});return i}}),Ir=e=>{let t=!1;return e.forEach(()=>{t=!0}),t},Er=e=>{let t,r=0,n=0,a=0;const i={isCurrent:l=>l.authority===`sdk-auth-${r}`,resolve:async()=>{const l=++n,u=new Headers(e?.headers);if(e){const f=e.token??await e.getAccessToken?.();f&&!u.has("authorization")&&u.set("authorization",`Bearer ${f}`)}const m=JSON.stringify(Array.from(u.entries()));if(l<a&&m!==t)throw new Error("Astrato SDK authentication changed during credential resolution; retry.");return a=Math.max(a,l),m!==t&&(r+=1,t=m,i.onAuthorityChange?.(`sdk-auth-${r}`)),{headers:u,authority:`sdk-auth-${r}`}}};return i},tt=(e,t)=>({request:async r=>{const{headers:n}=await t.resolve(),a=new Headers(n);new Headers(r.headers).forEach((l,u)=>{a.set(u,l)});const i={...r};return Ir(a)&&(i.headers=a),e.request(i)}}),Sr=e=>{const t=e.logger??mr(),r=!e.events,n=e.events??wt(),a=e.transport??Ar({baseUrl:e.baseUrl}),i=Er(e.auth),l=tt(a,i),u=async(S,T=l)=>{n.emit("transport:request",{method:S.method??"GET",url:S.url},{scope:{sessionId:e.sessionId}});try{const x=await T.request(S);return n.emit("transport:response",{method:S.method??"GET",url:S.url},{scope:{sessionId:e.sessionId}}),x}catch(x){throw t.error("Astrato SDK request failed",{error:x,method:S.method??"GET",url:S.url}),n.emit("transport:error",{error:x,method:S.method??"GET",url:S.url},{scope:{sessionId:e.sessionId}}),x}},m={...e.context,tenantId:e.tenantId??e.context?.tenantId},f={context:m,endpoints:e.endpoints?.data},g=e.hostServices?.data??He(u,f),h=async()=>{const S=await i.resolve(),T=tt(a,{resolve:async()=>S});return{authority:S.authority,isCurrent:()=>i.isCurrent(S),data:e.hostServices?.data??He(x=>u(x,T),f)}};let I;const E=!e.hostServices?.engine&&!!e.visualizationEngine,b=e.hostServices?.engine??(e.visualizationEngine?yr(g,{context:m,metadataCache:e.metadataCache,metadataLoadContext:S=>(i.onAuthorityChange=S,I??(I=h().finally(()=>{I=void 0})),I)}):void 0),P=$t({...e.hostServices,data:g,...b?{engine:b}:{}});return t.debug("Astrato SDK client created",{baseUrl:e.baseUrl}),{baseUrl:e.baseUrl,events:n,hostServices:P,logger:t,services:P,request:S=>u(S),reportError:(S,T={})=>{t.error("Astrato SDK error",{...T,error:S}),n.emit("sdk:error",{context:T,error:S},{scope:{sessionId:e.sessionId}})},destroy:()=>{E&&(i.onAuthorityChange=void 0,b?.fields.invalidateMetadata?.()),r&&n.clear()}}},fe=1,rt=512e3,st=4e6,Ve=32,ct=3e4,ge="astrato-sdk-iframe";class Te extends Error{constructor(t,r){super(r),this.code=t,this.name="AstratoIframeBridgeError"}}const Y=e=>({kind:ge,version:fe,...e}),X=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),dt=e=>{try{const t=new URL(e);return(t.protocol==="https:"||t.protocol==="http:")&&t.origin===e}catch{return!1}},lt=e=>Number.isFinite(e)&&e>=1&&e<=12e4,pe=(e,t,r)=>{const n=new WeakSet,a=new TextEncoder;let i=0,l=0;const u=g=>{if(l+=g,l>t)throw L("limit")},m=(g,h)=>{if(++i>1e5||h>32)throw L("limit");if(g===void 0&&h>0){u(4);return}if(g===null||typeof g=="boolean"){u(1);return}if(typeof g=="string"){if(g.length>t)throw L("limit");u(a.encode(g).length+2);return}if(typeof g=="number"&&Number.isFinite(g)){u(1);return}if(typeof g!="object"||n.has(g))throw L(r);if(n.add(g),u(2),Array.isArray(g))for(const I of g)m(I,h+1);else{const I=Object.getPrototypeOf(g);if(I!==null&&Object.getPrototypeOf(I)!==null)throw L(r);for(const[E,b]of Object.entries(g))if(b!==void 0){if(E.length>t)throw L("limit");u(a.encode(E).length+3),m(b,h+1)}}n.delete(g)};m(e,0);const f=JSON.stringify(e);if(f===void 0)throw L(r);if(a.encode(f).length>t)throw L("limit");return JSON.parse(f)},ne=e=>new Te("invalid_request",e),Dr=["GET","POST","PATCH","PUT","DELETE"],Lr=new Set(["authorization","cookie","proxy-authorization","x-api-key"]),ut=e=>{if(!X(e))throw ne("Bridge request is invalid.");const t=e.method,r=e.url,n=e.headers;if(typeof t!="string"||!Dr.includes(t))throw ne("Bridge request method is invalid.");if(typeof r!="string"||!r||r.length>2048||r.startsWith("/")||/^[a-z][a-z\d+.-]*:/i.test(r)||/[\\#]/.test(r)||Array.from(r).some(l=>l.charCodeAt(0)<32)||/(?:^|\/)(?:\.|%2e){1,2}(?:\/|$)/i.test(r.split("?",1)[0]))throw ne("Bridge request URL must be a relative SDK path.");if(!X(n)||Object.keys(n).length>32)throw ne("Bridge request headers are invalid.");const a={};for(const[l,u]of Object.entries(n)){if(!/^[!#$%&'*+.^_`|~\dA-Za-z-]+$/.test(l)||Lr.has(l.toLowerCase())||typeof u!="string"||u.length>2048||/[\r\n]/.test(u))throw ne("Bridge request headers are invalid.");a[l.toLowerCase()]=u}const i={method:t,url:r,headers:a};"body"in e&&(i.body=pe(e.body,rt,"invalid_request"));try{pe(i,rt,"invalid_request")}catch(l){throw l instanceof Te?l:ne("Bridge request body must be JSON.")}return i},Tr=["datasourceType","workbookVersionContainerId","sheetId","objectId","role","userId","workbookState"],ft=e=>{if(!X(e)||!X(e.semanticLayer)||!Array.isArray(e.capabilities))throw L("protocol");const t=e.tenantId,r=e.semanticLayer,n=r.id,a=r.datasourceType,i=e.capabilities;if(typeof t!="string"||!t||t.length>256||typeof n!="string"||!n||n.length>256||a!==void 0&&(typeof a!="string"||a.length>128)||i.length>32||!i.every(m=>typeof m=="string"&&m.length<=128))throw L("protocol");const l={};if(e.requestContext!==void 0){if(!X(e.requestContext))throw L("protocol");for(const m of Tr){const f=e.requestContext[m];if(f!==void 0){if(typeof f!="string"||f.length>256)throw L("protocol");l[m]=f}}}const u={tenantId:t,semanticLayer:{id:n,...a===void 0?{}:{datasourceType:a}},capabilities:[...i],...Object.keys(l).length?{requestContext:l}:{}};return pe(u,16e3,"protocol"),Object.freeze(u.semanticLayer),Object.freeze(u.capabilities),u.requestContext&&Object.freeze(u.requestContext),Object.freeze(u)},Cr=e=>e==="aborted"||e==="closed"||e==="host_error"||e==="invalid_request"||e==="invalid_response"||e==="limit"||e==="protocol"||e==="timeout"?e:void 0,Mr=e=>{switch(e){case"host_error":return"Astrato host request failed.";case"protocol":return"Astrato iframe bridge protocol is incompatible.";case"invalid_response":return"Astrato iframe bridge response is invalid.";case"timeout":return"Astrato iframe bridge request timed out.";case"aborted":return"Astrato iframe bridge request was aborted.";case"closed":return"Astrato iframe bridge is closed.";case"limit":return"Astrato iframe bridge limit exceeded.";default:return"Astrato iframe bridge request is invalid."}},L=e=>new Te(e,Mr(e)),Le=(e,t)=>{try{e.postMessage(t)}catch{}},kr=e=>{const t=e.hostWindow??window,r=e.timeoutMs??ct,n=e.maxConcurrentRequests??16;if(e.childOrigin!=="null"&&!dt(e.childOrigin))throw new Error("Invalid child origin.");const a=ft(e.context);if(!lt(r))throw new Error("Invalid iframe bridge timeout.");if(!Number.isInteger(n)||n<1||n>Ve)throw new Error("Invalid iframe bridge concurrency limit.");let i=!1,l=!1,u=e.initialLoadComplete??!1,m;const f=new Map,g=new Set,h=[],I=o=>{for(const c of f.values())clearTimeout(c.timer),c.responded=!0,c.controller.abort(),g.add(c);for(const{controller:c,timer:p}of h)clearTimeout(p),c.abort();f.clear(),h.length=0,m&&(o&&Le(m,Y({type:"disconnect"})),m.onmessage=null,m.onmessageerror=null,m.close(),m=void 0)},E=(o,c)=>{m===o&&!i&&Le(o,Y(c))},b=()=>{for(;!i&&f.size+g.size<n&&h.length>0;){const o=h.shift();if(!o)break;if(m!==o.port){clearTimeout(o.timer),o.controller.abort();continue}f.set(o.id,o),Promise.resolve().then(()=>o.controller.signal.aborted?void 0:e.request(o.request,o.controller.signal)).then(c=>{if(!(f.get(o.id)!==o&&!g.has(o))){if(clearTimeout(o.timer),f.get(o.id)===o&&f.delete(o.id),g.delete(o),!o.responded)try{const p=c===void 0?void 0:pe(c,st,"invalid_response");E(o.port,{type:"response",id:o.id,ok:!0,value:p})}catch{E(o.port,{type:"response",id:o.id,ok:!1,error:"invalid_response"})}b()}},()=>{f.get(o.id)!==o&&!g.has(o)||(clearTimeout(o.timer),f.get(o.id)===o&&f.delete(o.id),g.delete(o),o.responded||E(o.port,{type:"response",id:o.id,ok:!1,error:"host_error"}),b())})}},P=(o,c)=>{if(i||m!==o)return;const p=c.data;if(!X(p)||p.kind!==ge)return;if(p.version!==fe){I(!0);return}if(p.type==="disconnect"){I(!1);return}if(typeof p.id!="string"||!/^[\w-]{1,128}$/.test(p.id))return;const y=p.id;if(p.type==="cancel"){const k=f.get(y);if(k)clearTimeout(k.timer),k.responded=!0,k.controller.abort();else{const $=h.findIndex(G=>G.id===y);if($!==-1){const[G]=h.splice($,1);clearTimeout(G.timer),G.controller.abort()}}return}if(p.type!=="request")return;if(f.has(y)||h.some(k=>k.id===y)){E(o,{type:"response",id:y,ok:!1,error:"protocol"});return}let w;try{w=ut(p.request)}catch(k){const $=k instanceof Te?k.code:"invalid_request";E(o,{type:"response",id:y,ok:!1,error:$});return}if(f.size+g.size+h.length>=Ve){E(o,{type:"response",id:y,ok:!1,error:"limit"});return}const K=new AbortController,_=setTimeout(()=>{const k=f.get(y);if(k?.controller===K)k.responded=!0;else{const $=h.findIndex(G=>G.id===y&&G.controller===K);if($===-1)return;h.splice($,1)}K.abort(),E(o,{type:"response",id:y,ok:!1,error:"timeout"}),b()},r);h.push({id:y,request:w,controller:K,timer:_,port:o,responded:!1}),b()},S=o=>{if(i||o.source!==e.iframe.contentWindow||o.origin!==e.childOrigin)return;const c=o.data;if(!X(c)||c.kind!==ge||c.type!=="ready"||typeof c.nonce!="string"||!/^[\w-]{16,128}$/.test(c.nonce))return;if(c.version!==fe){e.iframe.contentWindow?.postMessage(Y({type:"incompatible",nonce:c.nonce}),e.childOrigin==="null"?"*":e.childOrigin);return}I(!0);const p=e.channelFactory?.()??new MessageChannel;m=p.port1;const y=m;y.onmessage=w=>P(y,w),y.onmessageerror=()=>I(!0),y.start();try{e.iframe.contentWindow?.postMessage(Y({type:"connected",nonce:c.nonce,context:a}),e.childOrigin==="null"?"*":e.childOrigin,[p.port2]),l=!0}catch{I(!1),p.port2.close()}},T=()=>{i||(i=!0,t.removeEventListener("message",S),t.removeEventListener("pagehide",T),e.iframe.removeEventListener("load",x),I(!0))},x=()=>{u?l&&T():u=!0};return t.addEventListener("message",S),t.addEventListener("pagehide",T),e.iframe.addEventListener("load",x),{destroy:T}};let te,ze,le,Pe;const Fr=()=>te,jr=e=>{if(!dt(e.parentOrigin))return Promise.reject(new Error("Invalid trusted parent origin."));const t=e.guestWindow??window;if(t.parent===t)return Promise.reject(new Error("Astrato iframe bridge requires a parent frame."));const r=e.timeoutMs??ct;return lt(r)?te?ze===e.parentOrigin?Promise.resolve(te):Promise.reject(L("protocol")):le?Pe===e.parentOrigin?le:Promise.reject(L("protocol")):(Pe=e.parentOrigin,le=new Promise((n,a)=>{const i=Array.from(crypto.getRandomValues(new Uint8Array(16)),g=>g.toString(16).padStart(2,"0")).join("");let l=!1;const u=(g,h)=>{l||(l=!0,clearTimeout(f),t.removeEventListener("message",m),le=void 0,Pe=void 0,g?n(g):a(h??L("protocol")))},m=g=>{if(g.source!==t.parent||g.origin!==e.parentOrigin)return;const h=g.data;if(!X(h)||h.kind!==ge||h.nonce!==i)return;if(h.type==="incompatible"){u(void 0,L("protocol"));return}if(h.type!=="connected")return;if(h.version!==fe){u(void 0,L("protocol"));return}if(g.ports.length!==1||!g.ports[0]){u(void 0,L("protocol"));return}let I;try{I=ft(h.context)}catch{u(void 0,L("protocol"));return}const E=g.ports[0];let b=!1,P=0;const S=new Set,T=new Map,x=y=>{if(!b){b=!0,t.removeEventListener("pagehide",p),y&&Le(E,Y({type:"disconnect"})),E.onmessage=null,E.onmessageerror=null,E.close();for(const w of T.values())w.cleanup(),w.reject(L("closed"));T.clear();for(const w of S)w.destroy();S.clear(),te===c&&(te=void 0,ze=void 0)}},o={request:y=>{if(b)return Promise.reject(L("closed"));if(T.size>=Ve)return Promise.reject(L("limit"));if(y.signal?.aborted)return Promise.reject(L("aborted"));let w;try{const _={};new Headers(y.headers).forEach((k,$)=>{_[$]=k}),w=ut({method:y.method??"GET",url:y.url,headers:_,...y.body===void 0?{}:{body:y.body}})}catch(_){return Promise.reject(_)}const K=`${Date.now()}-${++P}`;return new Promise((_,k)=>{const $=me=>{const Z=T.get(K);Z&&(T.delete(K),Z.cleanup(),Le(E,Y({type:"cancel",id:K})),k(L(me)))},G=setTimeout(()=>$("timeout"),r),ie=()=>$("aborted"),ye=()=>{clearTimeout(G),y.signal?.removeEventListener("abort",ie)};y.signal?.addEventListener("abort",ie,{once:!0}),T.set(K,{resolve:_,reject:k,cleanup:ye});try{E.postMessage(Y({type:"request",id:K,request:w}))}catch{T.delete(K),ye(),k(L("closed"))}})}},c={context:I,createClient:()=>{if(b)throw L("closed");const y=Sr({baseUrl:"",tenantId:I.tenantId,context:{...I.requestContext,tenantId:I.tenantId,datasourceType:I.semanticLayer.datasourceType},transport:o,visualizationEngine:!0}),w=y.destroy;return y.destroy=()=>{S.delete(y),w()},S.add(y),y},destroy:()=>x(!0)},p=()=>x(!0);t.addEventListener("pagehide",p),E.onmessage=y=>{const w=y.data;if(!X(w)||w.kind!==ge){x(!1);return}if(w.version!==fe){x(!1);return}if(w.type==="disconnect"){x(!1);return}if(w.type!=="response"||typeof w.id!="string"){x(!1);return}const K=T.get(w.id);if(K)if(T.delete(w.id),K.cleanup(),w.ok===!0&&!("error"in w))try{K.resolve(w.value===void 0?void 0:pe(w.value,st,"invalid_response"))}catch{K.reject(L("invalid_response"))}else w.ok===!1&&!("value"in w)&&Cr(w.error)?K.reject(L(w.error)):K.reject(L("invalid_response"))},E.onmessageerror=()=>x(!1),E.start(),te?.destroy(),te=c,ze=e.parentOrigin,u(c)},f=setTimeout(()=>u(void 0,L("timeout")),r);t.addEventListener("message",m);try{t.parent.postMessage(Y({type:"ready",nonce:i}),e.parentOrigin)}catch{u(void 0,L("closed"))}}),le):Promise.reject(new Error("Invalid iframe bridge timeout."))};export{fe as ASTRATO_IFRAME_BRIDGE_PROTOCOL_VERSION,Te as AstratoIframeBridgeError,hr as AstratoTransportError,jr as connectAstratoIframeBridge,Sr as createAstratoClient,wt as createAstratoEventBus,kr as createAstratoIframeHostBridge,mr as createConsoleLogger,He as createDirectDataService,Ar as createFetchTransport,kt as createMemorySelectionService,Ft as createMemoryVariableService,yr as createVisualizationEngineService,Fr as getAstratoIframeBridge};
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrato-io/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Browser SDK for connecting custom applications to Astrato APIs and runtime services.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"registry": "https://registry.npmjs.org/",
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./index.js",
|
|
12
|
+
"module": "./index.js",
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./index.d.ts",
|
|
17
|
+
"import": "./index.js",
|
|
18
|
+
"default": "./index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"index.js",
|
|
23
|
+
"index.d.ts",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"rxjs": "^7.8.1"
|
|
29
|
+
},
|
|
30
|
+
"license": "SEE LICENSE IN LICENSE"
|
|
31
|
+
}
|