industrial-model 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,8 @@ TypeScript SDK for querying [Cognite Flexible Data Models (FDM)](https://docs.co
13
13
  - **Pagination support** - use cursors manually or fetch all root pages with `limit: -1`.
14
14
  - **Aggregation support** - count, group, list distinct values, and aggregate numeric properties.
15
15
  - **Mutation support** - upsert model-shaped node patches and delete nodes by identity.
16
+ - **Datapoints support** - read, write, and delete time series datapoints through model-friendly option objects.
17
+ - **Files support** - upload files and retrieve pre-signed download URLs, keyed to model node identities.
16
18
  - **Runtime validation option** - parse query results with Zod schemas derived from Cognite view metadata.
17
19
  - **CJS and ESM builds** - works in Node.js and common bundler setups.
18
20
 
@@ -627,6 +629,122 @@ await core.delete([{ space: "asset-space", externalId: "pump-1" }]);
627
629
 
628
630
  Deletes are sent through Cognite apply. When more than 1000 nodes are provided, the SDK splits them into multiple Cognite calls.
629
631
 
632
+ ## Datapoints
633
+
634
+ Use `model.datapoints` for Cognite time series data. The public API talks in terms of `timeSeries`, ranges, and datapoint batches; the Cognite SDK `items` and `instanceId` payload shape stays internal.
635
+
636
+ ```ts
637
+ const { items } = await model.datapoints.retrieve({
638
+ timeSeries: [{ space: "ts-space", externalId: "temperature", targetUnit: "degC" }],
639
+ start: new Date("2024-01-01T00:00:00.000Z"),
640
+ end: new Date("2024-01-02T00:00:00.000Z"),
641
+ limit: 100,
642
+ });
643
+
644
+ items[0]?.timeSeries.externalId;
645
+ items[0]?.datapoints;
646
+ items[0]?.cursor; // next cursor, or null
647
+ ```
648
+
649
+ Pass `limit: -1` to follow datapoint cursors and return all pages for each requested time series.
650
+
651
+ ```ts
652
+ const history = await model.datapoints.retrieve({
653
+ timeSeries: [{ space: "ts-space", externalId: "temperature" }],
654
+ start: new Date("2024-01-01T00:00:00.000Z"),
655
+ limit: -1,
656
+ });
657
+ ```
658
+
659
+ Read the latest datapoint before a given timestamp:
660
+
661
+ ```ts
662
+ const latest = await model.datapoints.latest({
663
+ timeSeries: [{ space: "ts-space", externalId: "temperature" }],
664
+ ignoreUnknownIds: true,
665
+ });
666
+ ```
667
+
668
+ Write datapoints by passing an array of insert items directly:
669
+
670
+ ```ts
671
+ await model.datapoints.insert([
672
+ {
673
+ timeSeries: { space: "ts-space", externalId: "temperature" },
674
+ datapoints: [{ timestamp: new Date(), value: 42 }],
675
+ },
676
+ ]);
677
+ ```
678
+
679
+ Delete datapoints by passing an array of ranges directly:
680
+
681
+ ```ts
682
+ await model.datapoints.delete([
683
+ {
684
+ timeSeries: { space: "ts-space", externalId: "temperature" },
685
+ start: new Date("2024-01-01T00:00:00.000Z"),
686
+ end: new Date("2024-01-02T00:00:00.000Z"),
687
+ },
688
+ ]);
689
+ ```
690
+
691
+ ## Files
692
+
693
+ Use `model.files` to upload files to Cognite and retrieve pre-signed download URLs. Both operations use `space` and `externalId` node identities as keys; the Cognite `instanceId` payload shape stays internal.
694
+
695
+ Upload a file and optionally pass the binary content as a second argument to complete the upload in one call. When content is provided, Cognite uploads it inline and `uploaded` is `true` on the result.
696
+
697
+ ```ts
698
+ const result = await model.files.upload(
699
+ {
700
+ space: "file-space",
701
+ externalId: "report-2024",
702
+ name: "annual-report.pdf",
703
+ mimeType: "application/pdf",
704
+ directory: "/reports",
705
+ source: "erp",
706
+ metadata: { department: "finance", year: "2024" },
707
+ },
708
+ pdfContent, // Buffer, Blob, ArrayBuffer, ReadableStream, …
709
+ );
710
+
711
+ result.space; // "file-space"
712
+ result.externalId; // "report-2024"
713
+ result.name; // "annual-report.pdf"
714
+ result.uploaded; // true — content was provided
715
+ result.createdTime; // Date
716
+ result.lastUpdatedTime; // Date
717
+ ```
718
+
719
+ Omit the content argument to receive a pre-signed `uploadUrl` instead. Use it to push the file binary from the client side without routing it through your server:
720
+
721
+ ```ts
722
+ const result = await model.files.upload({
723
+ space: "file-space",
724
+ externalId: "report-2024",
725
+ name: "annual-report.pdf",
726
+ mimeType: "application/pdf",
727
+ });
728
+
729
+ result.uploaded; // false — content not yet uploaded
730
+ result.uploadUrl; // "https://storage.example.com/…" — use to PUT the file
731
+ ```
732
+
733
+ Retrieve pre-signed download URLs for one or more files by node identity:
734
+
735
+ ```ts
736
+ const urls = await model.files.getDownloadUrls([
737
+ { space: "file-space", externalId: "report-2024" },
738
+ { space: "file-space", externalId: "manual-v3" },
739
+ ]);
740
+
741
+ urls[0]?.downloadUrl; // pre-signed URL ready to use
742
+ urls[0]?.space;
743
+ urls[0]?.externalId;
744
+ ```
745
+
746
+ The returned array preserves the input order. Passing an empty array returns `[]` without calling Cognite.
747
+
630
748
  ## Aggregation
631
749
 
632
750
  Use `aggregate()` when you need grouped counts, distinct values, or numeric summaries without loading every instance.
@@ -826,6 +944,15 @@ Deletes are view-independent:
826
944
  await core.delete([{ space: "asset-space", externalId: "pump-1" }]);
827
945
  ```
828
946
 
947
+ `core.datapoints` exposes the same executor API as `model.datapoints` on `IndustrialModelClient`:
948
+
949
+ ```ts
950
+ const { items } = await core.datapoints.retrieve({
951
+ timeSeries: [{ space: "ts-space", externalId: "temperature" }],
952
+ limit: 100,
953
+ });
954
+ ```
955
+
829
956
  All Cognite Core view types are exported from `industrial-model` and can be imported directly for use with `IndustrialModelClient` if needed:
830
957
 
831
958
  ```ts
@@ -987,6 +1114,10 @@ Same as `model.upsert<TModel>()(options)` on `IndustrialModelClient`, with the v
987
1114
 
988
1115
  Same as `model.delete(items)` on `IndustrialModelClient`. Deletes nodes by `space` and `externalId`; no view name is required.
989
1116
 
1117
+ ### `core.datapoints`
1118
+
1119
+ Same as `model.datapoints` on `IndustrialModelClient`. All four methods — `retrieve`, `latest`, `insert`, and `delete` — are available without a view name.
1120
+
990
1121
  ### `new IndustrialModelClient(client, dataModelId, options?)`
991
1122
 
992
1123
  | Parameter | Type | Description |
@@ -1093,6 +1224,50 @@ Provide at least one of `groupBy` or `aggregate`. Omit `aggregate` to fetch dist
1093
1224
  | `max` | `{ max: "volume" }` | Maximum numeric value. |
1094
1225
  | `sum` | `{ sum: "volume" }` | Sum of a numeric property. |
1095
1226
 
1227
+ ### `model.files.upload(fileInfo, content?)`
1228
+
1229
+ Uploads a file to Cognite. `fileInfo` fields:
1230
+
1231
+ | Field | Type | Description |
1232
+ | --- | --- | --- |
1233
+ | `space` | `string` | Node space. |
1234
+ | `externalId` | `string` | Node external ID. |
1235
+ | `name` | `string` | File name. |
1236
+ | `mimeType` | `string` | Optional MIME type. |
1237
+ | `directory` | `string` | Optional directory path within the project. |
1238
+ | `source` | `string` | Optional source system identifier. |
1239
+ | `metadata` | `Record<string, string>` | Optional key-value metadata. |
1240
+
1241
+ `content` accepts any value accepted by the Cognite SDK file upload (`Buffer`, `Blob`, `ArrayBuffer`, `ReadableStream`, …). When provided, the file is uploaded inline and `uploaded` is `true`. When omitted, Cognite returns a pre-signed `uploadUrl` for a separate upload and `uploaded` is `false`.
1242
+
1243
+ Returns `FileUploadResult`:
1244
+
1245
+ ```ts
1246
+ type FileUploadResult = NodeId & {
1247
+ name: string;
1248
+ uploaded: boolean;
1249
+ mimeType?: string;
1250
+ directory?: string;
1251
+ source?: string;
1252
+ uploadedTime?: Date;
1253
+ createdTime: Date;
1254
+ lastUpdatedTime: Date;
1255
+ uploadUrl?: string;
1256
+ };
1257
+ ```
1258
+
1259
+ ### `model.files.getDownloadUrls(nodeIds)`
1260
+
1261
+ Retrieves one pre-signed download URL per node, in the same order as the input. Passing an empty array returns `[]` without calling Cognite.
1262
+
1263
+ Returns `FileDownloadUrl[]`:
1264
+
1265
+ ```ts
1266
+ type FileDownloadUrl = NodeId & {
1267
+ downloadUrl: string;
1268
+ };
1269
+ ```
1270
+
1096
1271
  ### Filter Operators
1097
1272
 
1098
1273
  | Field type | Operators |
@@ -1132,6 +1307,8 @@ Logical combinators `AND`, `OR`, and `NOT` are supported at any nesting level, i
1132
1307
  | `UpsertResult`, `UpsertResultItem` | Upsert output types. |
1133
1308
  | `DeleteExecutor`, `DeleteResult`, `DeleteResultItem` | Delete helper and output types. |
1134
1309
  | `EdgeCreationContext`, `EdgeCreationCallback`, `EdgeCreationCallbacks`, `EdgeMode` | Edge upsert helper types. |
1310
+ | `DatapointsExecutor`, `DatapointsRetrieveOptions`, `DatapointsLatestOptions`, `DatapointsLatestSeries`, `DatapointsInsertItem`, `DatapointsDeleteRange`, `DatapointsResult`, `DatapointSeriesResult`, `DatapointAggregate` | Datapoints types. |
1311
+ | `FilesExecutor`, `FileUploadInfo`, `FileUploadResult`, `FileDownloadUrl` | Files types. |
1135
1312
  | `IndustrialModelClientOptions` | Client configuration options. |
1136
1313
 
1137
1314
  **Cognite Core**
@@ -50,7 +50,92 @@ var CogniteSdkAdapter = class {
50
50
  items: response.items
51
51
  };
52
52
  }
53
+ async retrieveDatapoints(options) {
54
+ const { items, ...rest } = options;
55
+ const sdkItems = items.map(({ space, externalId, ...itemRest }) => ({
56
+ ...itemRest,
57
+ instanceId: { space, externalId }
58
+ }));
59
+ const response = await this.client.datapoints.retrieve({
60
+ ...rest,
61
+ items: sdkItems
62
+ });
63
+ return { items: response.map(mapDatapointResult) };
64
+ }
65
+ async retrieveLatestDatapoints(items, options) {
66
+ const sdkItems = items.map(({ space, externalId, before }) => ({
67
+ instanceId: { space, externalId },
68
+ ...before !== void 0 ? { before } : {}
69
+ }));
70
+ const response = await this.client.datapoints.retrieveLatest(
71
+ sdkItems,
72
+ options
73
+ );
74
+ return { items: response.map(mapDatapointResult) };
75
+ }
76
+ async insertDatapoints(items) {
77
+ const sdkItems = items.map(({ space, externalId, datapoints }) => ({
78
+ instanceId: { space, externalId },
79
+ datapoints
80
+ }));
81
+ await this.client.datapoints.insert(
82
+ sdkItems
83
+ );
84
+ }
85
+ async deleteDatapoints(items) {
86
+ const sdkItems = items.map(({ space, externalId, inclusiveBegin, exclusiveEnd }) => ({
87
+ instanceId: { space, externalId },
88
+ inclusiveBegin,
89
+ ...exclusiveEnd !== void 0 ? { exclusiveEnd } : {}
90
+ }));
91
+ await this.client.datapoints.delete(
92
+ sdkItems
93
+ );
94
+ }
95
+ async uploadFile(fileInfo, content) {
96
+ const { instanceId, ...rest } = fileInfo;
97
+ const response = await this.client.files.upload(
98
+ { ...rest, instanceId },
99
+ content,
100
+ false,
101
+ content !== void 0
102
+ );
103
+ return mapFileResult(response);
104
+ }
105
+ async getFileDownloadUrls(ids) {
106
+ const response = await this.client.files.getDownloadUrls(
107
+ ids
108
+ );
109
+ return response.map((item) => ({
110
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
111
+ downloadUrl: item.downloadUrl
112
+ }));
113
+ }
53
114
  };
115
+ function mapFileResult(item) {
116
+ return {
117
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
118
+ name: item.name,
119
+ uploaded: item.uploaded,
120
+ createdTime: item.createdTime,
121
+ lastUpdatedTime: item.lastUpdatedTime,
122
+ ...item.uploadedTime !== void 0 ? { uploadedTime: item.uploadedTime } : {},
123
+ ...item.mimeType !== void 0 ? { mimeType: item.mimeType } : {},
124
+ ...item.directory !== void 0 ? { directory: item.directory } : {},
125
+ ...item.source !== void 0 ? { source: item.source } : {},
126
+ ...item.uploadUrl !== void 0 ? { uploadUrl: item.uploadUrl } : {}
127
+ };
128
+ }
129
+ function mapDatapointResult(item) {
130
+ return {
131
+ ...item.instanceId?.space !== void 0 ? { space: item.instanceId.space } : {},
132
+ ...item.instanceId?.externalId !== void 0 ? { externalId: item.instanceId.externalId } : {},
133
+ isString: item.isString ?? false,
134
+ ...item.unit !== void 0 ? { unit: item.unit } : {},
135
+ datapoints: item.datapoints ?? [],
136
+ ...item.nextCursor !== void 0 ? { nextCursor: item.nextCursor } : {}
137
+ };
138
+ }
54
139
 
55
140
  // src/constants.ts
56
141
  var NESTED_SEP = "|";
@@ -61,6 +146,15 @@ var MAX_DEPENDENCY_DEPTH = 3;
61
146
  var AGGREGATE_LIMIT = 1e3;
62
147
  var MAX_GROUP_BY = 5;
63
148
 
149
+ // src/utils/array.ts
150
+ function chunks(items, size) {
151
+ const result = [];
152
+ for (let index = 0; index < items.length; index += size) {
153
+ result.push(items.slice(index, index + size));
154
+ }
155
+ return result;
156
+ }
157
+
64
158
  // src/utils/query.ts
65
159
  function mapNodesAndEdges(queryResult, _query) {
66
160
  return queryResult.items;
@@ -717,6 +811,63 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
717
811
  return [];
718
812
  }
719
813
  };
814
+ var dateSchema2 = zod.z.date();
815
+ var nodeIdSchema2 = zod.z.object({ space: zod.z.string(), externalId: zod.z.string() }).loose();
816
+ var retrieveOptionsSchema = zod.z.object({
817
+ timeSeries: zod.z.array(nodeIdSchema2),
818
+ start: dateSchema2.optional(),
819
+ end: dateSchema2.optional()
820
+ }).loose();
821
+ var deleteRangeSchema = zod.z.object({
822
+ timeSeries: zod.z.object({ space: zod.z.string(), externalId: zod.z.string() }).loose(),
823
+ start: dateSchema2,
824
+ end: dateSchema2.optional()
825
+ }).loose();
826
+ function formatIssues(error, prefix) {
827
+ return error.issues.map((issue) => {
828
+ const parts = prefix ? [prefix, ...issue.path] : [...issue.path];
829
+ return `${parts.map(String).join(".")}: ${issue.message}`;
830
+ });
831
+ }
832
+ var DatapointsValidator = class {
833
+ validateRetrieve(options) {
834
+ const result = retrieveOptionsSchema.safeParse(options);
835
+ if (!result.success) {
836
+ const messages = formatIssues(result.error);
837
+ throw new Error(`Invalid datapoints options:
838
+ - ${messages.join("\n- ")}`);
839
+ }
840
+ }
841
+ validateDelete(ranges) {
842
+ const result = zod.z.array(deleteRangeSchema).safeParse(ranges);
843
+ if (!result.success) {
844
+ const messages = formatIssues(result.error, "ranges");
845
+ throw new Error(`Invalid datapoints options:
846
+ - ${messages.join("\n- ")}`);
847
+ }
848
+ }
849
+ };
850
+ var nodeIdSchema3 = zod.z.object({
851
+ space: zod.z.string().min(1),
852
+ externalId: zod.z.string().min(1)
853
+ }).loose();
854
+ var deleteItemsSchema = zod.z.array(nodeIdSchema3);
855
+ function formatIssues2(error, prefix) {
856
+ return error.issues.map((issue) => {
857
+ const parts = [prefix, ...issue.path];
858
+ return `${parts.map(String).join(".")}: ${issue.message}`;
859
+ });
860
+ }
861
+ var DeleteValidator = class {
862
+ validateItems(items) {
863
+ const result = deleteItemsSchema.safeParse(items);
864
+ if (!result.success) {
865
+ const messages = formatIssues2(result.error, "items");
866
+ throw new Error(`Invalid delete options:
867
+ - ${messages.join("\n- ")}`);
868
+ }
869
+ }
870
+ };
720
871
  var nodeMetadataSchema = {
721
872
  instanceType: zod.z.literal("node").optional(),
722
873
  space: zod.z.string(),
@@ -1120,6 +1271,178 @@ var AggregateResultMapper = class {
1120
1271
  }
1121
1272
  };
1122
1273
 
1274
+ // src/mappers/datapoints-mapper.ts
1275
+ var CHUNK_SIZE = 100;
1276
+ var DatapointsMapper = class {
1277
+ constructor(cognite) {
1278
+ this.cognite = cognite;
1279
+ this.validator = new DatapointsValidator();
1280
+ }
1281
+ async retrieve(options) {
1282
+ this.validator.validateRetrieve(options);
1283
+ const cogniteOptions = this.toCogniteOptions(options);
1284
+ const allPages = options.limit === -1;
1285
+ if (!allPages) {
1286
+ const result = await this.cognite.retrieveDatapoints(cogniteOptions);
1287
+ return this.mapResult(result.items, cogniteOptions.items, options.aggregate);
1288
+ }
1289
+ const accumulated = /* @__PURE__ */ new Map();
1290
+ const itemByKey = new Map(cogniteOptions.items.map((item) => [this.toKey(item), item]));
1291
+ let currentItems = cogniteOptions.items;
1292
+ while (currentItems.length > 0) {
1293
+ const result = await this.cognite.retrieveDatapoints({
1294
+ ...cogniteOptions,
1295
+ items: currentItems
1296
+ });
1297
+ const nextItems = [];
1298
+ for (const resultItem of result.items) {
1299
+ const key = this.toKey(resultItem);
1300
+ let acc = accumulated.get(key);
1301
+ if (!acc) {
1302
+ acc = { ...resultItem, datapoints: [] };
1303
+ accumulated.set(key, acc);
1304
+ }
1305
+ acc.datapoints.push(...resultItem.datapoints);
1306
+ if (resultItem.nextCursor) {
1307
+ acc.nextCursor = resultItem.nextCursor;
1308
+ } else {
1309
+ delete acc.nextCursor;
1310
+ }
1311
+ if (resultItem.nextCursor) {
1312
+ const original = itemByKey.get(key);
1313
+ nextItems.push({
1314
+ ...original,
1315
+ space: resultItem.space ?? original?.space ?? "",
1316
+ externalId: resultItem.externalId ?? original?.externalId ?? "",
1317
+ cursor: resultItem.nextCursor
1318
+ });
1319
+ }
1320
+ }
1321
+ currentItems = nextItems;
1322
+ }
1323
+ return this.mapResult(
1324
+ Array.from(accumulated.values()),
1325
+ cogniteOptions.items,
1326
+ options.aggregate
1327
+ );
1328
+ }
1329
+ async retrieveLatest(options) {
1330
+ const items = options.timeSeries.map(
1331
+ ({ space, externalId, before }) => ({
1332
+ space,
1333
+ externalId,
1334
+ ...before !== void 0 ? { before } : {}
1335
+ })
1336
+ );
1337
+ const latestOptions = options.ignoreUnknownIds !== void 0 ? { ignoreUnknownIds: options.ignoreUnknownIds } : void 0;
1338
+ const result = await this.cognite.retrieveLatestDatapoints(items, latestOptions);
1339
+ return this.mapResult(result.items, items);
1340
+ }
1341
+ async insert(items) {
1342
+ const cogniteItems = items.map(({ timeSeries, datapoints }) => ({
1343
+ space: timeSeries.space,
1344
+ externalId: timeSeries.externalId,
1345
+ datapoints
1346
+ }));
1347
+ for (const chunk of chunks(cogniteItems, CHUNK_SIZE)) {
1348
+ await this.cognite.insertDatapoints(chunk);
1349
+ }
1350
+ }
1351
+ async delete(ranges) {
1352
+ this.validator.validateDelete(ranges);
1353
+ const cogniteItems = ranges.map(({ timeSeries, start, end }) => ({
1354
+ space: timeSeries.space,
1355
+ externalId: timeSeries.externalId,
1356
+ inclusiveBegin: start,
1357
+ ...end !== void 0 ? { exclusiveEnd: end } : {}
1358
+ }));
1359
+ for (const chunk of chunks(cogniteItems, CHUNK_SIZE)) {
1360
+ await this.cognite.deleteDatapoints(chunk);
1361
+ }
1362
+ }
1363
+ toKey(item) {
1364
+ return `${item.space ?? ""}:${item.externalId ?? ""}`;
1365
+ }
1366
+ toCogniteOptions(options) {
1367
+ const { timeSeries, aggregate, ...rest } = options;
1368
+ return {
1369
+ ...rest,
1370
+ ...aggregate !== void 0 ? { aggregates: [aggregate] } : {},
1371
+ items: timeSeries.map(({ space, externalId }) => ({ space, externalId }))
1372
+ };
1373
+ }
1374
+ mapResult(items, requestedItems, aggregate) {
1375
+ const requestedByKey = new Map(requestedItems.map((item) => [this.toKey(item), item]));
1376
+ const mappedItems = items.filter((item) => !item.isString).map((item) => {
1377
+ const requested = requestedByKey.get(this.toKey(item));
1378
+ const datapoints = aggregate !== void 0 ? item.datapoints.map((dp) => ({
1379
+ timestamp: dp.timestamp,
1380
+ value: dp[aggregate] ?? 0
1381
+ })) : item.datapoints;
1382
+ return {
1383
+ timeSeries: {
1384
+ space: item.space ?? requested?.space ?? "",
1385
+ externalId: item.externalId ?? requested?.externalId ?? ""
1386
+ },
1387
+ ...item.unit !== void 0 ? { unit: item.unit } : {},
1388
+ datapoints,
1389
+ cursor: item.nextCursor ?? null
1390
+ };
1391
+ });
1392
+ return { items: mappedItems };
1393
+ }
1394
+ };
1395
+
1396
+ // src/mappers/files-mapper.ts
1397
+ var FilesMapper = class {
1398
+ constructor(cognite) {
1399
+ this.cognite = cognite;
1400
+ }
1401
+ async upload(fileInfo, content) {
1402
+ const { space, externalId, ...rest } = fileInfo;
1403
+ const result = await this.cognite.uploadFile(
1404
+ { instanceId: { space, externalId }, ...rest },
1405
+ content
1406
+ );
1407
+ return this.mapUploadResult(result, { space, externalId });
1408
+ }
1409
+ async getDownloadUrls(nodeIds) {
1410
+ if (nodeIds.length === 0) return [];
1411
+ const ids = nodeIds.map(({ space, externalId }) => ({ instanceId: { space, externalId } }));
1412
+ const result = await this.cognite.getFileDownloadUrls(ids);
1413
+ return result.map(
1414
+ (item, i) => this.mapDownloadUrl(item, nodeIds[i] ?? { space: "", externalId: "" })
1415
+ );
1416
+ }
1417
+ toNodeId(item, fallback) {
1418
+ return {
1419
+ space: item.instanceId?.space ?? fallback.space,
1420
+ externalId: item.instanceId?.externalId ?? fallback.externalId
1421
+ };
1422
+ }
1423
+ mapUploadResult(item, fallback) {
1424
+ const nodeId = this.toNodeId(item, fallback);
1425
+ return {
1426
+ ...nodeId,
1427
+ name: item.name,
1428
+ uploaded: item.uploaded,
1429
+ createdTime: item.createdTime,
1430
+ lastUpdatedTime: item.lastUpdatedTime,
1431
+ ...item.uploadedTime !== void 0 ? { uploadedTime: item.uploadedTime } : {},
1432
+ ...item.mimeType !== void 0 ? { mimeType: item.mimeType } : {},
1433
+ ...item.directory !== void 0 ? { directory: item.directory } : {},
1434
+ ...item.source !== void 0 ? { source: item.source } : {},
1435
+ ...item.uploadUrl !== void 0 ? { uploadUrl: item.uploadUrl } : {}
1436
+ };
1437
+ }
1438
+ mapDownloadUrl(item, fallback) {
1439
+ return {
1440
+ ...this.toNodeId(item, fallback),
1441
+ downloadUrl: item.downloadUrl
1442
+ };
1443
+ }
1444
+ };
1445
+
1123
1446
  // src/mappers/sort-mapper.ts
1124
1447
  var SortMapper = class {
1125
1448
  map(sort, rootView) {
@@ -1709,6 +2032,17 @@ var ViewMapper = class {
1709
2032
  var APPLY_ITEM_LIMIT = 1e3;
1710
2033
  var IndustrialModelClient = class {
1711
2034
  constructor(client, dataModelId, options = {}) {
2035
+ this.deleteValidator = new DeleteValidator();
2036
+ this.files = {
2037
+ upload: (fileInfo, content) => this.filesMapper.upload(fileInfo, content),
2038
+ getDownloadUrls: (nodeIds) => this.filesMapper.getDownloadUrls(nodeIds)
2039
+ };
2040
+ this.datapoints = {
2041
+ retrieve: (options) => this.datapointsMapper.retrieve(options),
2042
+ latest: (options) => this.datapointsMapper.retrieveLatest(options),
2043
+ insert: (items) => this.datapointsMapper.insert(items),
2044
+ delete: (ranges) => this.datapointsMapper.delete(ranges)
2045
+ };
1712
2046
  const cognite = createCogniteAdapter(client);
1713
2047
  this.cognite = cognite;
1714
2048
  const viewMapper = new ViewMapper(cognite, dataModelId);
@@ -1717,6 +2051,8 @@ var IndustrialModelClient = class {
1717
2051
  this.upsertMapper = new UpsertMapper(viewMapper, cognite);
1718
2052
  this.aggregateResultMapper = new AggregateResultMapper();
1719
2053
  this.resultMapper = new QueryResultMapper(viewMapper);
2054
+ this.datapointsMapper = new DatapointsMapper(cognite);
2055
+ this.filesMapper = new FilesMapper(cognite);
1720
2056
  this.resultValidator = new QueryResultValidator(viewMapper);
1721
2057
  this.validateResults = options.validateResults ?? false;
1722
2058
  }
@@ -1733,14 +2069,12 @@ var IndustrialModelClient = class {
1733
2069
  return execute;
1734
2070
  }
1735
2071
  async delete(items) {
1736
- const deleteItems = items.map((item) => {
1737
- assertNodeId2(item);
1738
- return {
1739
- instanceType: "node",
1740
- space: item.space,
1741
- externalId: item.externalId
1742
- };
1743
- });
2072
+ this.deleteValidator.validateItems(items);
2073
+ const deleteItems = items.map((item) => ({
2074
+ instanceType: "node",
2075
+ space: item.space,
2076
+ externalId: item.externalId
2077
+ }));
1744
2078
  const response = await this.applyInstancesInChunks({
1745
2079
  items: [],
1746
2080
  delete: deleteItems
@@ -1832,18 +2166,6 @@ var IndustrialModelClient = class {
1832
2166
  return appendNodesAndEdges(result, nestedResults);
1833
2167
  }
1834
2168
  };
1835
- function chunks(items, size) {
1836
- const result = [];
1837
- for (let index = 0; index < items.length; index += size) {
1838
- result.push(items.slice(index, index + size));
1839
- }
1840
- return result;
1841
- }
1842
- function assertNodeId2(value) {
1843
- if (value == null || typeof value !== "object" || Array.isArray(value) || typeof value.space !== "string" || value.space?.length === 0 || typeof value.externalId !== "string" || value.externalId?.length === 0) {
1844
- throw new Error("Invalid delete options:\n- items: expected NodeId values");
1845
- }
1846
- }
1847
2169
 
1848
2170
  // src/cognite-core/client.ts
1849
2171
  var COGNITE_CORE_DATA_MODEL = {
@@ -1874,6 +2196,9 @@ var CogniteCoreClient = class {
1874
2196
  delete(items) {
1875
2197
  return this.model.delete(items);
1876
2198
  }
2199
+ get datapoints() {
2200
+ return this.model.datapoints;
2201
+ }
1877
2202
  };
1878
2203
 
1879
2204
  exports.COGNITE_CORE_DATA_MODEL = COGNITE_CORE_DATA_MODEL;