industrial-model 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,7 +41,99 @@ var CogniteSdkAdapter = class {
41
41
  items: response.items
42
42
  };
43
43
  }
44
+ async applyInstances(request) {
45
+ const apply = this.client.instances.apply;
46
+ const response = await apply(request);
47
+ return {
48
+ items: response.items
49
+ };
50
+ }
51
+ async retrieveDatapoints(options) {
52
+ const { items, ...rest } = options;
53
+ const sdkItems = items.map(({ space, externalId, ...itemRest }) => ({
54
+ ...itemRest,
55
+ instanceId: { space, externalId }
56
+ }));
57
+ const response = await this.client.datapoints.retrieve({
58
+ ...rest,
59
+ items: sdkItems
60
+ });
61
+ return { items: response.map(mapDatapointResult) };
62
+ }
63
+ async retrieveLatestDatapoints(items, options) {
64
+ const sdkItems = items.map(({ space, externalId, before }) => ({
65
+ instanceId: { space, externalId },
66
+ ...before !== void 0 ? { before } : {}
67
+ }));
68
+ const response = await this.client.datapoints.retrieveLatest(
69
+ sdkItems,
70
+ options
71
+ );
72
+ return { items: response.map(mapDatapointResult) };
73
+ }
74
+ async insertDatapoints(items) {
75
+ const sdkItems = items.map(({ space, externalId, datapoints }) => ({
76
+ instanceId: { space, externalId },
77
+ datapoints
78
+ }));
79
+ await this.client.datapoints.insert(
80
+ sdkItems
81
+ );
82
+ }
83
+ async deleteDatapoints(items) {
84
+ const sdkItems = items.map(({ space, externalId, inclusiveBegin, exclusiveEnd }) => ({
85
+ instanceId: { space, externalId },
86
+ inclusiveBegin,
87
+ ...exclusiveEnd !== void 0 ? { exclusiveEnd } : {}
88
+ }));
89
+ await this.client.datapoints.delete(
90
+ sdkItems
91
+ );
92
+ }
93
+ async uploadFile(fileInfo, content) {
94
+ const { instanceId, ...rest } = fileInfo;
95
+ const response = await this.client.files.upload(
96
+ { ...rest, instanceId },
97
+ content,
98
+ false,
99
+ content !== void 0
100
+ );
101
+ return mapFileResult(response);
102
+ }
103
+ async getFileDownloadUrls(ids) {
104
+ const response = await this.client.files.getDownloadUrls(
105
+ ids
106
+ );
107
+ return response.map((item) => ({
108
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
109
+ downloadUrl: item.downloadUrl
110
+ }));
111
+ }
44
112
  };
113
+ function mapFileResult(item) {
114
+ return {
115
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
116
+ name: item.name,
117
+ uploaded: item.uploaded,
118
+ createdTime: item.createdTime,
119
+ lastUpdatedTime: item.lastUpdatedTime,
120
+ ...item.uploadedTime !== void 0 ? { uploadedTime: item.uploadedTime } : {},
121
+ ...item.mimeType !== void 0 ? { mimeType: item.mimeType } : {},
122
+ ...item.directory !== void 0 ? { directory: item.directory } : {},
123
+ ...item.source !== void 0 ? { source: item.source } : {},
124
+ ...item.uploadUrl !== void 0 ? { uploadUrl: item.uploadUrl } : {}
125
+ };
126
+ }
127
+ function mapDatapointResult(item) {
128
+ return {
129
+ ...item.instanceId?.space !== void 0 ? { space: item.instanceId.space } : {},
130
+ ...item.instanceId?.externalId !== void 0 ? { externalId: item.instanceId.externalId } : {},
131
+ isString: item.isString ?? false,
132
+ ...item.unit !== void 0 ? { unit: item.unit } : {},
133
+ datapoints: item.datapoints ?? [],
134
+ ...item.nextCursor !== void 0 ? { nextCursor: item.nextCursor } : {}
135
+ };
136
+ }
45
137
 
46
138
  // src/constants.ts
47
139
  var NESTED_SEP = "|";
@@ -52,6 +144,15 @@ var MAX_DEPENDENCY_DEPTH = 3;
52
144
  var AGGREGATE_LIMIT = 1e3;
53
145
  var MAX_GROUP_BY = 5;
54
146
 
147
+ // src/utils/array.ts
148
+ function chunks(items, size) {
149
+ const result = [];
150
+ for (let index = 0; index < items.length; index += size) {
151
+ result.push(items.slice(index, index + size));
152
+ }
153
+ return result;
154
+ }
155
+
55
156
  // src/utils/query.ts
56
157
  function mapNodesAndEdges(queryResult, _query) {
57
158
  return queryResult.items;
@@ -708,6 +809,63 @@ ${errors.map((error) => `- ${error}`).join("\n")}`
708
809
  return [];
709
810
  }
710
811
  };
812
+ var dateSchema2 = z.date();
813
+ var nodeIdSchema2 = z.object({ space: z.string(), externalId: z.string() }).loose();
814
+ var retrieveOptionsSchema = z.object({
815
+ timeSeries: z.array(nodeIdSchema2),
816
+ start: dateSchema2.optional(),
817
+ end: dateSchema2.optional()
818
+ }).loose();
819
+ var deleteRangeSchema = z.object({
820
+ timeSeries: z.object({ space: z.string(), externalId: z.string() }).loose(),
821
+ start: dateSchema2,
822
+ end: dateSchema2.optional()
823
+ }).loose();
824
+ function formatIssues(error, prefix) {
825
+ return error.issues.map((issue) => {
826
+ const parts = prefix ? [prefix, ...issue.path] : [...issue.path];
827
+ return `${parts.map(String).join(".")}: ${issue.message}`;
828
+ });
829
+ }
830
+ var DatapointsValidator = class {
831
+ validateRetrieve(options) {
832
+ const result = retrieveOptionsSchema.safeParse(options);
833
+ if (!result.success) {
834
+ const messages = formatIssues(result.error);
835
+ throw new Error(`Invalid datapoints options:
836
+ - ${messages.join("\n- ")}`);
837
+ }
838
+ }
839
+ validateDelete(ranges) {
840
+ const result = z.array(deleteRangeSchema).safeParse(ranges);
841
+ if (!result.success) {
842
+ const messages = formatIssues(result.error, "ranges");
843
+ throw new Error(`Invalid datapoints options:
844
+ - ${messages.join("\n- ")}`);
845
+ }
846
+ }
847
+ };
848
+ var nodeIdSchema3 = z.object({
849
+ space: z.string().min(1),
850
+ externalId: z.string().min(1)
851
+ }).loose();
852
+ var deleteItemsSchema = z.array(nodeIdSchema3);
853
+ function formatIssues2(error, prefix) {
854
+ return error.issues.map((issue) => {
855
+ const parts = [prefix, ...issue.path];
856
+ return `${parts.map(String).join(".")}: ${issue.message}`;
857
+ });
858
+ }
859
+ var DeleteValidator = class {
860
+ validateItems(items) {
861
+ const result = deleteItemsSchema.safeParse(items);
862
+ if (!result.success) {
863
+ const messages = formatIssues2(result.error, "items");
864
+ throw new Error(`Invalid delete options:
865
+ - ${messages.join("\n- ")}`);
866
+ }
867
+ }
868
+ };
711
869
  var nodeMetadataSchema = {
712
870
  instanceType: z.literal("node").optional(),
713
871
  space: z.string(),
@@ -794,6 +952,85 @@ ${result.error.issues.map((issue) => `- ${issue.path.map(String).join(".")}: ${i
794
952
  return (isList ? z.array(nestedSchema) : nestedSchema).optional();
795
953
  }
796
954
  };
955
+ var strictNodeIdSchema = z.object({
956
+ space: z.string().min(1),
957
+ externalId: z.string().min(1)
958
+ }).strict();
959
+ var nodeIdLikeSchema = z.object({
960
+ space: z.string().min(1),
961
+ externalId: z.string().min(1)
962
+ }).loose();
963
+ var optionsSchema = z.object({
964
+ viewExternalId: z.string().min(1),
965
+ items: z.array(z.record(z.string(), z.unknown())),
966
+ onEdgeCreation: z.record(z.string(), z.function()).optional(),
967
+ replace: z.boolean().optional(),
968
+ edgeMode: z.enum(["append", "replace"]).optional()
969
+ }).strict();
970
+ function issuePath3(path) {
971
+ return path.length === 0 ? "upsert" : path.map(String).join(".");
972
+ }
973
+ function formatZodIssues3(error, path) {
974
+ return error.issues.map((issue) => `${issuePath3([...path, ...issue.path])}: ${issue.message}`);
975
+ }
976
+ function relationValueSchema(property) {
977
+ if (isReverseDirectRelation(property) && property.targetsList === true) {
978
+ return z.never();
979
+ }
980
+ return z.union([nodeIdLikeSchema, z.array(nodeIdLikeSchema)]);
981
+ }
982
+ var UpsertValidator = class {
983
+ validate(options, rootView) {
984
+ const errors = [];
985
+ const optionsResult = optionsSchema.safeParse(options);
986
+ if (!optionsResult.success) {
987
+ errors.push(...formatZodIssues3(optionsResult.error, []));
988
+ }
989
+ if (options.viewExternalId !== rootView.externalId) {
990
+ errors.push(
991
+ `viewExternalId: expected "${rootView.externalId}", received "${options.viewExternalId}"`
992
+ );
993
+ }
994
+ for (const [index, item] of options.items.entries()) {
995
+ errors.push(
996
+ ...this.validateItem(item, rootView, ["items", index])
997
+ );
998
+ }
999
+ if (errors.length > 0) {
1000
+ throw new Error(`Invalid upsert options:
1001
+ ${errors.map((error) => `- ${error}`).join("\n")}`);
1002
+ }
1003
+ }
1004
+ validateItem(item, view, path) {
1005
+ const errors = [];
1006
+ const identityResult = strictNodeIdSchema.safeParse({
1007
+ space: item.space,
1008
+ externalId: item.externalId
1009
+ });
1010
+ if (!identityResult.success) {
1011
+ errors.push(...formatZodIssues3(identityResult.error, path));
1012
+ }
1013
+ for (const [name, value] of Object.entries(item)) {
1014
+ if (name === "space" || name === "externalId") continue;
1015
+ const property = view.properties[name];
1016
+ if (!property) {
1017
+ errors.push(`${issuePath3([...path, name])}: unknown view property`);
1018
+ continue;
1019
+ }
1020
+ if (isViewPropertyDefinition(property)) {
1021
+ const schema = property.type.type === "direct" ? property.type.list === true ? z.array(nodeIdLikeSchema) : nodeIdLikeSchema : propertyValueSchema(property);
1022
+ const result = schema.safeParse(value);
1023
+ if (!result.success) errors.push(...formatZodIssues3(result.error, [...path, name]));
1024
+ continue;
1025
+ }
1026
+ if (isReverseDirectRelation(property) || isEdgeConnection(property)) {
1027
+ const result = relationValueSchema(property).safeParse(value);
1028
+ if (!result.success) errors.push(...formatZodIssues3(result.error, [...path, name]));
1029
+ }
1030
+ }
1031
+ return errors;
1032
+ }
1033
+ };
797
1034
 
798
1035
  // src/mappers/filter-mapper.ts
799
1036
  var LEAF_OPS = /* @__PURE__ */ new Set([
@@ -1032,6 +1269,178 @@ var AggregateResultMapper = class {
1032
1269
  }
1033
1270
  };
1034
1271
 
1272
+ // src/mappers/datapoints-mapper.ts
1273
+ var CHUNK_SIZE = 100;
1274
+ var DatapointsMapper = class {
1275
+ constructor(cognite) {
1276
+ this.cognite = cognite;
1277
+ this.validator = new DatapointsValidator();
1278
+ }
1279
+ async retrieve(options) {
1280
+ this.validator.validateRetrieve(options);
1281
+ const cogniteOptions = this.toCogniteOptions(options);
1282
+ const allPages = options.limit === -1;
1283
+ if (!allPages) {
1284
+ const result = await this.cognite.retrieveDatapoints(cogniteOptions);
1285
+ return this.mapResult(result.items, cogniteOptions.items, options.aggregate);
1286
+ }
1287
+ const accumulated = /* @__PURE__ */ new Map();
1288
+ const itemByKey = new Map(cogniteOptions.items.map((item) => [this.toKey(item), item]));
1289
+ let currentItems = cogniteOptions.items;
1290
+ while (currentItems.length > 0) {
1291
+ const result = await this.cognite.retrieveDatapoints({
1292
+ ...cogniteOptions,
1293
+ items: currentItems
1294
+ });
1295
+ const nextItems = [];
1296
+ for (const resultItem of result.items) {
1297
+ const key = this.toKey(resultItem);
1298
+ let acc = accumulated.get(key);
1299
+ if (!acc) {
1300
+ acc = { ...resultItem, datapoints: [] };
1301
+ accumulated.set(key, acc);
1302
+ }
1303
+ acc.datapoints.push(...resultItem.datapoints);
1304
+ if (resultItem.nextCursor) {
1305
+ acc.nextCursor = resultItem.nextCursor;
1306
+ } else {
1307
+ delete acc.nextCursor;
1308
+ }
1309
+ if (resultItem.nextCursor) {
1310
+ const original = itemByKey.get(key);
1311
+ nextItems.push({
1312
+ ...original,
1313
+ space: resultItem.space ?? original?.space ?? "",
1314
+ externalId: resultItem.externalId ?? original?.externalId ?? "",
1315
+ cursor: resultItem.nextCursor
1316
+ });
1317
+ }
1318
+ }
1319
+ currentItems = nextItems;
1320
+ }
1321
+ return this.mapResult(
1322
+ Array.from(accumulated.values()),
1323
+ cogniteOptions.items,
1324
+ options.aggregate
1325
+ );
1326
+ }
1327
+ async retrieveLatest(options) {
1328
+ const items = options.timeSeries.map(
1329
+ ({ space, externalId, before }) => ({
1330
+ space,
1331
+ externalId,
1332
+ ...before !== void 0 ? { before } : {}
1333
+ })
1334
+ );
1335
+ const latestOptions = options.ignoreUnknownIds !== void 0 ? { ignoreUnknownIds: options.ignoreUnknownIds } : void 0;
1336
+ const result = await this.cognite.retrieveLatestDatapoints(items, latestOptions);
1337
+ return this.mapResult(result.items, items);
1338
+ }
1339
+ async insert(items) {
1340
+ const cogniteItems = items.map(({ timeSeries, datapoints }) => ({
1341
+ space: timeSeries.space,
1342
+ externalId: timeSeries.externalId,
1343
+ datapoints
1344
+ }));
1345
+ for (const chunk of chunks(cogniteItems, CHUNK_SIZE)) {
1346
+ await this.cognite.insertDatapoints(chunk);
1347
+ }
1348
+ }
1349
+ async delete(ranges) {
1350
+ this.validator.validateDelete(ranges);
1351
+ const cogniteItems = ranges.map(({ timeSeries, start, end }) => ({
1352
+ space: timeSeries.space,
1353
+ externalId: timeSeries.externalId,
1354
+ inclusiveBegin: start,
1355
+ ...end !== void 0 ? { exclusiveEnd: end } : {}
1356
+ }));
1357
+ for (const chunk of chunks(cogniteItems, CHUNK_SIZE)) {
1358
+ await this.cognite.deleteDatapoints(chunk);
1359
+ }
1360
+ }
1361
+ toKey(item) {
1362
+ return `${item.space ?? ""}:${item.externalId ?? ""}`;
1363
+ }
1364
+ toCogniteOptions(options) {
1365
+ const { timeSeries, aggregate, ...rest } = options;
1366
+ return {
1367
+ ...rest,
1368
+ ...aggregate !== void 0 ? { aggregates: [aggregate] } : {},
1369
+ items: timeSeries.map(({ space, externalId }) => ({ space, externalId }))
1370
+ };
1371
+ }
1372
+ mapResult(items, requestedItems, aggregate) {
1373
+ const requestedByKey = new Map(requestedItems.map((item) => [this.toKey(item), item]));
1374
+ const mappedItems = items.filter((item) => !item.isString).map((item) => {
1375
+ const requested = requestedByKey.get(this.toKey(item));
1376
+ const datapoints = aggregate !== void 0 ? item.datapoints.map((dp) => ({
1377
+ timestamp: dp.timestamp,
1378
+ value: dp[aggregate] ?? 0
1379
+ })) : item.datapoints;
1380
+ return {
1381
+ timeSeries: {
1382
+ space: item.space ?? requested?.space ?? "",
1383
+ externalId: item.externalId ?? requested?.externalId ?? ""
1384
+ },
1385
+ ...item.unit !== void 0 ? { unit: item.unit } : {},
1386
+ datapoints,
1387
+ cursor: item.nextCursor ?? null
1388
+ };
1389
+ });
1390
+ return { items: mappedItems };
1391
+ }
1392
+ };
1393
+
1394
+ // src/mappers/files-mapper.ts
1395
+ var FilesMapper = class {
1396
+ constructor(cognite) {
1397
+ this.cognite = cognite;
1398
+ }
1399
+ async upload(fileInfo, content) {
1400
+ const { space, externalId, ...rest } = fileInfo;
1401
+ const result = await this.cognite.uploadFile(
1402
+ { instanceId: { space, externalId }, ...rest },
1403
+ content
1404
+ );
1405
+ return this.mapUploadResult(result, { space, externalId });
1406
+ }
1407
+ async getDownloadUrls(nodeIds) {
1408
+ if (nodeIds.length === 0) return [];
1409
+ const ids = nodeIds.map(({ space, externalId }) => ({ instanceId: { space, externalId } }));
1410
+ const result = await this.cognite.getFileDownloadUrls(ids);
1411
+ return result.map(
1412
+ (item, i) => this.mapDownloadUrl(item, nodeIds[i] ?? { space: "", externalId: "" })
1413
+ );
1414
+ }
1415
+ toNodeId(item, fallback) {
1416
+ return {
1417
+ space: item.instanceId?.space ?? fallback.space,
1418
+ externalId: item.instanceId?.externalId ?? fallback.externalId
1419
+ };
1420
+ }
1421
+ mapUploadResult(item, fallback) {
1422
+ const nodeId = this.toNodeId(item, fallback);
1423
+ return {
1424
+ ...nodeId,
1425
+ name: item.name,
1426
+ uploaded: item.uploaded,
1427
+ createdTime: item.createdTime,
1428
+ lastUpdatedTime: item.lastUpdatedTime,
1429
+ ...item.uploadedTime !== void 0 ? { uploadedTime: item.uploadedTime } : {},
1430
+ ...item.mimeType !== void 0 ? { mimeType: item.mimeType } : {},
1431
+ ...item.directory !== void 0 ? { directory: item.directory } : {},
1432
+ ...item.source !== void 0 ? { source: item.source } : {},
1433
+ ...item.uploadUrl !== void 0 ? { uploadUrl: item.uploadUrl } : {}
1434
+ };
1435
+ }
1436
+ mapDownloadUrl(item, fallback) {
1437
+ return {
1438
+ ...this.toNodeId(item, fallback),
1439
+ downloadUrl: item.downloadUrl
1440
+ };
1441
+ }
1442
+ };
1443
+
1035
1444
  // src/mappers/sort-mapper.ts
1036
1445
  var SortMapper = class {
1037
1446
  map(sort, rootView) {
@@ -1361,6 +1770,216 @@ var QueryResultMapper = class {
1361
1770
  }
1362
1771
  };
1363
1772
 
1773
+ // src/mappers/upsert-mapper.ts
1774
+ var IDENTITY_KEYS = /* @__PURE__ */ new Set(["space", "externalId"]);
1775
+ var EDGE_QUERY_LIMIT = 1e3;
1776
+ var UpsertMapper = class {
1777
+ constructor(viewMapper, cognite) {
1778
+ this.viewMapper = viewMapper;
1779
+ this.cognite = cognite;
1780
+ this.validator = new UpsertValidator();
1781
+ }
1782
+ async map(options) {
1783
+ const rootView = await this.viewMapper.getView(options.viewExternalId);
1784
+ this.validator.validate(options, rootView);
1785
+ const edgeMode = options.edgeMode ?? "append";
1786
+ const mappedItems = options.items.map(
1787
+ (item) => this.mapItem(item, rootView, options.onEdgeCreation, edgeMode)
1788
+ );
1789
+ const items = mappedItems.flatMap((item) => item.writes);
1790
+ const edgeReplacements = mappedItems.flatMap((item) => item.edgeReplacements);
1791
+ const deleteItems = edgeMode === "replace" ? await this.mapEdgeReplacementDeletes(edgeReplacements) : [];
1792
+ return {
1793
+ items,
1794
+ ...deleteItems.length > 0 ? { delete: deleteItems } : {},
1795
+ ...options.replace === true ? { replace: true } : {}
1796
+ };
1797
+ }
1798
+ mapItem(item, rootView, onEdgeCreation, edgeMode) {
1799
+ const node = { space: item.space, externalId: item.externalId };
1800
+ const nodeProperties = {};
1801
+ const inferredItems = [];
1802
+ const edgeReplacements = [];
1803
+ for (const [name, value] of Object.entries(item)) {
1804
+ if (IDENTITY_KEYS.has(name)) continue;
1805
+ const property = rootView.properties[name];
1806
+ if (!property) continue;
1807
+ if (isViewPropertyDefinition(property)) {
1808
+ nodeProperties[name] = normalizeViewPropertyValue(value, property);
1809
+ } else if (isReverseDirectRelation(property)) {
1810
+ inferredItems.push(...this.mapReverseDirectRelation(node, value, property));
1811
+ } else if (isEdgeConnection(property)) {
1812
+ const desiredEdges = this.mapEdgeConnection(node, name, value, property, onEdgeCreation);
1813
+ inferredItems.push(...desiredEdges);
1814
+ if (edgeMode === "replace") {
1815
+ edgeReplacements.push({ rootNode: node, propertyName: name, property, desiredEdges });
1816
+ }
1817
+ }
1818
+ }
1819
+ const applyNode = {
1820
+ instanceType: "node",
1821
+ ...node
1822
+ };
1823
+ if (Object.keys(nodeProperties).length > 0) {
1824
+ applyNode.sources = [{ source: toViewReference(rootView), properties: nodeProperties }];
1825
+ }
1826
+ return { writes: [applyNode, ...inferredItems], edgeReplacements };
1827
+ }
1828
+ async mapEdgeReplacementDeletes(replacements) {
1829
+ const deletes = await Promise.all(
1830
+ replacements.map(async (replacement) => {
1831
+ const existingEdges = await this.queryExistingEdges(replacement);
1832
+ const desiredEdgeKeys = new Set(replacement.desiredEdges.map((edge) => instanceKey(edge)));
1833
+ return existingEdges.filter((edge) => !desiredEdgeKeys.has(instanceKey(edge))).map((edge) => ({
1834
+ instanceType: "edge",
1835
+ space: edge.space,
1836
+ externalId: edge.externalId
1837
+ }));
1838
+ })
1839
+ );
1840
+ return uniqueDeletes(deletes.flat());
1841
+ }
1842
+ async queryExistingEdges(replacement) {
1843
+ const rootKey = `${replacement.propertyName}Root`;
1844
+ const edgeKey = `${replacement.propertyName}Edges`;
1845
+ const direction = replacement.property.direction ?? "outwards";
1846
+ const query = {
1847
+ with: {
1848
+ [rootKey]: {
1849
+ nodes: {
1850
+ filter: {
1851
+ instanceReferences: [replacement.rootNode]
1852
+ }
1853
+ },
1854
+ limit: 1
1855
+ },
1856
+ [edgeKey]: {
1857
+ edges: {
1858
+ from: rootKey,
1859
+ maxDistance: 1,
1860
+ direction,
1861
+ filter: {
1862
+ equals: { property: ["edge", "type"], value: replacement.property.type }
1863
+ }
1864
+ },
1865
+ limit: EDGE_QUERY_LIMIT
1866
+ }
1867
+ },
1868
+ select: {
1869
+ [rootKey]: {},
1870
+ [edgeKey]: {}
1871
+ }
1872
+ };
1873
+ const edges = [];
1874
+ let cursor;
1875
+ do {
1876
+ const response = await this.cognite.queryInstances({
1877
+ ...query,
1878
+ ...cursor ? { cursors: { [edgeKey]: cursor } } : {}
1879
+ });
1880
+ edges.push(
1881
+ ...(response.items[edgeKey] ?? []).filter(
1882
+ (item) => item.instanceType === "edge"
1883
+ )
1884
+ );
1885
+ cursor = response.nextCursor[edgeKey];
1886
+ } while (cursor);
1887
+ return edges;
1888
+ }
1889
+ mapReverseDirectRelation(node, value, property) {
1890
+ const targets = asNodeIdArray(value);
1891
+ return targets.map((target) => ({
1892
+ instanceType: "node",
1893
+ ...target,
1894
+ sources: [
1895
+ {
1896
+ source: property.through.source,
1897
+ properties: {
1898
+ [property.through.identifier]: normalizeReverseDirectRelationValue(node, property)
1899
+ }
1900
+ }
1901
+ ]
1902
+ }));
1903
+ }
1904
+ mapEdgeConnection(node, propertyName, value, property, onEdgeCreation) {
1905
+ const direction = property.direction ?? "outwards";
1906
+ return asNodeIdArray(value).map((target) => {
1907
+ const startNode = direction === "inwards" ? target : node;
1908
+ const endNode = direction === "inwards" ? node : target;
1909
+ const edgeType = toNodeId(property.type, `edge type for "${propertyName}"`);
1910
+ const createEdgeId = onEdgeCreation?.[propertyName];
1911
+ if (!createEdgeId) {
1912
+ throw new Error(
1913
+ `Invalid upsert options:
1914
+ - onEdgeCreation.${propertyName}: required when ingesting edge connection "${propertyName}"`
1915
+ );
1916
+ }
1917
+ const edgeId = createEdgeId({
1918
+ startNode,
1919
+ endNode,
1920
+ edgeType
1921
+ });
1922
+ assertNodeId(edgeId, `onEdgeCreation(${propertyName})`);
1923
+ return {
1924
+ instanceType: "edge",
1925
+ ...edgeId,
1926
+ type: property.type,
1927
+ startNode,
1928
+ endNode
1929
+ };
1930
+ });
1931
+ }
1932
+ };
1933
+ function normalizeReverseDirectRelationValue(node, property) {
1934
+ return property.targetsList === true ? [node] : node;
1935
+ }
1936
+ function normalizeViewPropertyValue(value, property) {
1937
+ if (property.type.type !== "direct") return normalizePropertyValue(value);
1938
+ if (Array.isArray(value)) return value.map((item) => toNodeId(item));
1939
+ return toNodeId(value);
1940
+ }
1941
+ function normalizePropertyValue(value) {
1942
+ if (value instanceof Date) return value.toISOString();
1943
+ if (Array.isArray(value)) return value.map(normalizePropertyValue);
1944
+ if (isPlainObject(value)) {
1945
+ return Object.fromEntries(
1946
+ Object.entries(value).map(([key, nestedValue]) => [key, normalizePropertyValue(nestedValue)])
1947
+ );
1948
+ }
1949
+ return value;
1950
+ }
1951
+ function asNodeIdArray(value) {
1952
+ return Array.isArray(value) ? value.map((item) => toNodeId(item)) : [toNodeId(value)];
1953
+ }
1954
+ function toNodeId(value, label = "relation reference") {
1955
+ if (!isPlainObject(value) || typeof value.space !== "string" || typeof value.externalId !== "string") {
1956
+ throw new Error(`Invalid upsert options:
1957
+ - ${label}: expected a NodeId`);
1958
+ }
1959
+ return { space: value.space, externalId: value.externalId };
1960
+ }
1961
+ function instanceKey(instance) {
1962
+ return `${instance.space}\0${instance.externalId}`;
1963
+ }
1964
+ function uniqueDeletes(deletes) {
1965
+ const seen = /* @__PURE__ */ new Set();
1966
+ return deletes.filter((item) => {
1967
+ const key = instanceKey(item);
1968
+ if (seen.has(key)) return false;
1969
+ seen.add(key);
1970
+ return true;
1971
+ });
1972
+ }
1973
+ function assertNodeId(value, label) {
1974
+ if (!isPlainObject(value) || typeof value.space !== "string" || typeof value.externalId !== "string") {
1975
+ throw new Error(`Invalid upsert options:
1976
+ - ${label}: expected a NodeId`);
1977
+ }
1978
+ }
1979
+ function isPlainObject(value) {
1980
+ return value != null && typeof value === "object" && !Array.isArray(value);
1981
+ }
1982
+
1364
1983
  // src/mappers/view-mapper.ts
1365
1984
  var ViewMapper = class {
1366
1985
  constructor(cognite, dataModelId) {
@@ -1408,15 +2027,30 @@ var ViewMapper = class {
1408
2027
  };
1409
2028
 
1410
2029
  // src/client.ts
2030
+ var APPLY_ITEM_LIMIT = 1e3;
1411
2031
  var IndustrialModelClient = class {
1412
2032
  constructor(client, dataModelId, options = {}) {
2033
+ this.deleteValidator = new DeleteValidator();
2034
+ this.files = {
2035
+ upload: (fileInfo, content) => this.filesMapper.upload(fileInfo, content),
2036
+ getDownloadUrls: (nodeIds) => this.filesMapper.getDownloadUrls(nodeIds)
2037
+ };
2038
+ this.datapoints = {
2039
+ retrieve: (options) => this.datapointsMapper.retrieve(options),
2040
+ latest: (options) => this.datapointsMapper.retrieveLatest(options),
2041
+ insert: (items) => this.datapointsMapper.insert(items),
2042
+ delete: (ranges) => this.datapointsMapper.delete(ranges)
2043
+ };
1413
2044
  const cognite = createCogniteAdapter(client);
1414
2045
  this.cognite = cognite;
1415
2046
  const viewMapper = new ViewMapper(cognite, dataModelId);
1416
2047
  this.queryMapper = new QueryMapper(viewMapper, cognite);
1417
2048
  this.aggregateMapper = new AggregateMapper(viewMapper, cognite);
2049
+ this.upsertMapper = new UpsertMapper(viewMapper, cognite);
1418
2050
  this.aggregateResultMapper = new AggregateResultMapper();
1419
2051
  this.resultMapper = new QueryResultMapper(viewMapper);
2052
+ this.datapointsMapper = new DatapointsMapper(cognite);
2053
+ this.filesMapper = new FilesMapper(cognite);
1420
2054
  this.resultValidator = new QueryResultValidator(viewMapper);
1421
2055
  this.validateResults = options.validateResults ?? false;
1422
2056
  }
@@ -1428,6 +2062,52 @@ var IndustrialModelClient = class {
1428
2062
  const execute = (options) => this.aggregateInternal(options);
1429
2063
  return execute;
1430
2064
  }
2065
+ upsert() {
2066
+ const execute = (options) => this.upsertInternal(options);
2067
+ return execute;
2068
+ }
2069
+ async delete(items) {
2070
+ this.deleteValidator.validateItems(items);
2071
+ const deleteItems = items.map((item) => ({
2072
+ instanceType: "node",
2073
+ space: item.space,
2074
+ externalId: item.externalId
2075
+ }));
2076
+ const response = await this.applyInstancesInChunks({
2077
+ items: [],
2078
+ delete: deleteItems
2079
+ });
2080
+ return { items: response.items };
2081
+ }
2082
+ async upsertInternal(options) {
2083
+ const cogniteRequest = await this.upsertMapper.map(options);
2084
+ const response = await this.applyInstancesInChunks(cogniteRequest);
2085
+ return { items: response.items };
2086
+ }
2087
+ async applyInstancesInChunks(request) {
2088
+ const deleteItems = request.delete ?? [];
2089
+ const totalItems = request.items.length + deleteItems.length;
2090
+ if (totalItems === 0) return { items: [] };
2091
+ if (totalItems <= APPLY_ITEM_LIMIT) {
2092
+ return this.cognite.applyInstances(request);
2093
+ }
2094
+ const responses = [];
2095
+ for (const deleteChunk of chunks(deleteItems, APPLY_ITEM_LIMIT)) {
2096
+ const response = await this.cognite.applyInstances({
2097
+ items: [],
2098
+ delete: deleteChunk
2099
+ });
2100
+ responses.push(...response.items);
2101
+ }
2102
+ for (const itemChunk of chunks(request.items, APPLY_ITEM_LIMIT)) {
2103
+ const response = await this.cognite.applyInstances({
2104
+ items: itemChunk,
2105
+ ...request.replace === true ? { replace: true } : {}
2106
+ });
2107
+ responses.push(...response.items);
2108
+ }
2109
+ return { items: responses };
2110
+ }
1431
2111
  async aggregateInternal(options) {
1432
2112
  const cogniteRequest = await this.aggregateMapper.map(options);
1433
2113
  const response = await this.cognite.aggregateInstances(cogniteRequest);
@@ -1506,6 +2186,17 @@ var CogniteCoreClient = class {
1506
2186
  const execute = (options = {}) => aggregate({ ...options, viewExternalId });
1507
2187
  return execute;
1508
2188
  }
2189
+ upsert(viewExternalId) {
2190
+ const upsert = this.model.upsert();
2191
+ const execute = (options) => upsert({ ...options, viewExternalId });
2192
+ return execute;
2193
+ }
2194
+ delete(items) {
2195
+ return this.model.delete(items);
2196
+ }
2197
+ get datapoints() {
2198
+ return this.model.datapoints;
2199
+ }
1509
2200
  };
1510
2201
 
1511
2202
  export { COGNITE_CORE_DATA_MODEL, CogniteCoreClient };