@zucker-framework/things 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,415 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/data-type.ts
5
+ function convertValue(type, raw) {
6
+ if (raw === null || raw === void 0) return raw;
7
+ switch (type.id) {
8
+ case "int":
9
+ case "long": {
10
+ const n = Number(raw);
11
+ return isNaN(n) ? raw : Math.trunc(n);
12
+ }
13
+ case "float":
14
+ case "double": {
15
+ const n = Number(raw);
16
+ if (isNaN(n)) return raw;
17
+ if (type.scale !== void 0) {
18
+ return parseFloat(n.toFixed(type.scale));
19
+ }
20
+ return n;
21
+ }
22
+ case "boolean": {
23
+ if (typeof raw === "boolean") return raw;
24
+ const tv = type.trueValue ?? "true";
25
+ const fv = type.falseValue ?? "false";
26
+ if (String(raw) === tv) return true;
27
+ if (String(raw) === fv) return false;
28
+ return Boolean(raw);
29
+ }
30
+ case "string":
31
+ case "password":
32
+ return String(raw);
33
+ case "date": {
34
+ if (raw instanceof Date) return raw.getTime();
35
+ const n = Number(raw);
36
+ return isNaN(n) ? new Date(String(raw)).getTime() : n;
37
+ }
38
+ case "enum": {
39
+ const match = type.elements.find((e) => e.value === raw || String(e.value) === String(raw));
40
+ return match ? match.value : raw;
41
+ }
42
+ default:
43
+ return raw;
44
+ }
45
+ }
46
+ __name(convertValue, "convertValue");
47
+
48
+ // src/metadata.ts
49
+ var PropertyAccessMode = /* @__PURE__ */ (function(PropertyAccessMode2) {
50
+ PropertyAccessMode2["READ"] = "r";
51
+ PropertyAccessMode2["WRITE"] = "w";
52
+ PropertyAccessMode2["READ_WRITE"] = "rw";
53
+ return PropertyAccessMode2;
54
+ })({});
55
+ var EventLevel = /* @__PURE__ */ (function(EventLevel2) {
56
+ EventLevel2["OTHER"] = "other";
57
+ EventLevel2["WARN"] = "warn";
58
+ EventLevel2["ERROR"] = "error";
59
+ return EventLevel2;
60
+ })({});
61
+ function mergeMetadata(base, patch) {
62
+ const mergeList = /* @__PURE__ */ __name((a, b = []) => {
63
+ const map = new Map(a.map((i) => [
64
+ i.id,
65
+ i
66
+ ]));
67
+ for (const item of b) {
68
+ map.set(item.id, item);
69
+ }
70
+ return Array.from(map.values());
71
+ }, "mergeList");
72
+ return {
73
+ properties: mergeList(base.properties, patch.properties),
74
+ functions: mergeList(base.functions, patch.functions),
75
+ events: mergeList(base.events, patch.events),
76
+ tags: mergeList(base.tags ?? [], patch.tags)
77
+ };
78
+ }
79
+ __name(mergeMetadata, "mergeMetadata");
80
+ function createMetadata(partial = {}) {
81
+ return {
82
+ properties: partial.properties ?? [],
83
+ functions: partial.functions ?? [],
84
+ events: partial.events ?? [],
85
+ tags: partial.tags ?? []
86
+ };
87
+ }
88
+ __name(createMetadata, "createMetadata");
89
+
90
+ // src/thing.ts
91
+ var ThingState = /* @__PURE__ */ (function(ThingState2) {
92
+ ThingState2["INACTIVE"] = "notActive";
93
+ ThingState2["OFFLINE"] = "offline";
94
+ ThingState2["ONLINE"] = "online";
95
+ return ThingState2;
96
+ })({});
97
+
98
+ // src/thing-manager.ts
99
+ import { Injectable } from "@nestjs/common";
100
+ function _ts_decorate(decorators, target, key, desc) {
101
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
102
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
103
+ r = Reflect.decorate(decorators, target, key, desc);
104
+ } else {
105
+ for (var i = decorators.length - 1; i >= 0; i--) {
106
+ if (d = decorators[i]) {
107
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
108
+ }
109
+ }
110
+ }
111
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
112
+ }
113
+ __name(_ts_decorate, "_ts_decorate");
114
+ var InMemoryThingManager = class {
115
+ static {
116
+ __name(this, "InMemoryThingManager");
117
+ }
118
+ types = /* @__PURE__ */ new Map();
119
+ things = /* @__PURE__ */ new Map();
120
+ async getThingType(typeId) {
121
+ return this.types.get(typeId);
122
+ }
123
+ async registerThingType(type) {
124
+ this.types.set(type.id, type);
125
+ }
126
+ async getThing(thingId) {
127
+ return this.things.get(thingId);
128
+ }
129
+ async registerThing(thing) {
130
+ const existing = this.things.get(thing.id);
131
+ this.things.set(thing.id, {
132
+ ...existing,
133
+ ...thing,
134
+ state: existing?.state ?? ThingState.INACTIVE
135
+ });
136
+ }
137
+ async unregisterThing(thingId) {
138
+ this.things.delete(thingId);
139
+ }
140
+ async updateState(thingId, state, timestamp) {
141
+ const thing = this.things.get(thingId);
142
+ if (!thing) return;
143
+ const update = {
144
+ state
145
+ };
146
+ if (state === ThingState.ONLINE) {
147
+ update.onlineTime = timestamp ?? Date.now();
148
+ } else if (state === ThingState.OFFLINE) {
149
+ update.offlineTime = timestamp ?? Date.now();
150
+ }
151
+ this.things.set(thingId, {
152
+ ...thing,
153
+ ...update
154
+ });
155
+ }
156
+ async getEffectiveMetadata(thingId) {
157
+ const thing = this.things.get(thingId);
158
+ if (!thing) return void 0;
159
+ const type = this.types.get(thing.thingTypeId);
160
+ if (!type) return void 0;
161
+ if (!thing.metadataPatch) {
162
+ return type.metadata;
163
+ }
164
+ return mergeMetadata(type.metadata, thing.metadataPatch);
165
+ }
166
+ /** 获取指定类型的所有实例 */
167
+ getThingsByType(typeId) {
168
+ return Array.from(this.things.values()).filter((t) => t.thingTypeId === typeId);
169
+ }
170
+ /** 获取所有在线实例数量 */
171
+ countOnline() {
172
+ return Array.from(this.things.values()).filter((t) => t.state === ThingState.ONLINE).length;
173
+ }
174
+ };
175
+ InMemoryThingManager = _ts_decorate([
176
+ Injectable()
177
+ ], InMemoryThingManager);
178
+
179
+ // src/message.ts
180
+ function createMessageId() {
181
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
182
+ }
183
+ __name(createMessageId, "createMessageId");
184
+ function createReportPropertyMessage(thingId, thingType, properties, extra) {
185
+ return {
186
+ messageType: "ReportProperty",
187
+ messageId: createMessageId(),
188
+ thingId,
189
+ thingType,
190
+ timestamp: Date.now(),
191
+ properties,
192
+ ...extra
193
+ };
194
+ }
195
+ __name(createReportPropertyMessage, "createReportPropertyMessage");
196
+ function createEventMessage(thingId, thingType, event, data) {
197
+ return {
198
+ messageType: "Event",
199
+ messageId: createMessageId(),
200
+ thingId,
201
+ thingType,
202
+ timestamp: Date.now(),
203
+ event,
204
+ data
205
+ };
206
+ }
207
+ __name(createEventMessage, "createEventMessage");
208
+
209
+ // src/property-log.ts
210
+ import { Injectable as Injectable2 } from "@nestjs/common";
211
+ function _ts_decorate2(decorators, target, key, desc) {
212
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
213
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
214
+ r = Reflect.decorate(decorators, target, key, desc);
215
+ } else {
216
+ for (var i = decorators.length - 1; i >= 0; i--) {
217
+ if (d = decorators[i]) {
218
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
219
+ }
220
+ }
221
+ }
222
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
223
+ }
224
+ __name(_ts_decorate2, "_ts_decorate");
225
+ function _ts_metadata(metadataKey, metadataValue) {
226
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
227
+ return Reflect.metadata(metadataKey, metadataValue);
228
+ }
229
+ }
230
+ __name(_ts_metadata, "_ts_metadata");
231
+ var DefaultThingsDataService = class {
232
+ static {
233
+ __name(this, "DefaultThingsDataService");
234
+ }
235
+ tsService;
236
+ constructor(tsService) {
237
+ this.tsService = tsService;
238
+ }
239
+ propertyMetric(thingType) {
240
+ return `thing_property_${thingType}`;
241
+ }
242
+ eventMetric(thingType, eventId) {
243
+ return `thing_event_${thingType}_${eventId}`;
244
+ }
245
+ async saveProperties(message) {
246
+ const metric = this.propertyMetric(message.thingType);
247
+ const points = Object.entries(message.properties).map(([key, value]) => ({
248
+ timestamp: message.propertySourceTimes?.[key] ?? message.timestamp,
249
+ tags: {
250
+ thingId: message.thingId,
251
+ thingType: message.thingType,
252
+ property: key
253
+ },
254
+ values: {
255
+ value: typeof value === "object" ? JSON.stringify(value) : value,
256
+ state: message.propertyStates?.[key] ?? "success"
257
+ }
258
+ }));
259
+ for (const point of points) {
260
+ const propMetric = `thing_property_${message.thingType}_${point.tags["property"]}`;
261
+ await this.tsService.save({
262
+ ...point,
263
+ tags: {
264
+ thingId: point.tags["thingId"],
265
+ thingType: point.tags["thingType"]
266
+ }
267
+ });
268
+ void propMetric;
269
+ }
270
+ if (points.length > 0) {
271
+ await this.tsService.saveBatch(points.map((p) => ({
272
+ ...p,
273
+ tags: {
274
+ ...p.tags
275
+ },
276
+ values: {
277
+ ...p.values,
278
+ metric
279
+ }
280
+ })));
281
+ }
282
+ }
283
+ async saveEvent(message) {
284
+ const metric = this.eventMetric(message.thingType, message.event);
285
+ const data = {
286
+ timestamp: message.timestamp,
287
+ tags: {
288
+ thingId: message.thingId,
289
+ thingType: message.thingType,
290
+ event: message.event
291
+ },
292
+ values: {
293
+ data: typeof message.data === "object" ? JSON.stringify(message.data) : message.data,
294
+ metric
295
+ }
296
+ };
297
+ await this.tsService.save(data);
298
+ }
299
+ async queryPropertyLogs(query) {
300
+ const tsQuery = {
301
+ metric: this.propertyMetric(query.thingType),
302
+ from: query.from,
303
+ to: query.to,
304
+ tags: {
305
+ thingId: query.thingId,
306
+ property: query.id
307
+ },
308
+ limit: query.limit,
309
+ order: query.order
310
+ };
311
+ const results = await this.tsService.query(tsQuery);
312
+ return results.map((r) => ({
313
+ property: String(r.tags["property"] ?? query.id),
314
+ value: r.values["value"],
315
+ thingId: String(r.tags["thingId"]),
316
+ thingType: String(r.tags["thingType"] ?? query.thingType),
317
+ timestamp: r.timestamp,
318
+ state: r.values["state"]
319
+ }));
320
+ }
321
+ async queryEventLogs(query) {
322
+ const tsQuery = {
323
+ metric: this.eventMetric(query.thingType, query.id),
324
+ from: query.from,
325
+ to: query.to,
326
+ tags: {
327
+ thingId: query.thingId
328
+ },
329
+ limit: query.limit,
330
+ order: query.order
331
+ };
332
+ const results = await this.tsService.query(tsQuery);
333
+ return results.map((r) => {
334
+ let data = r.values["data"];
335
+ if (typeof data === "string") {
336
+ try {
337
+ data = JSON.parse(data);
338
+ } catch {
339
+ }
340
+ }
341
+ return {
342
+ event: String(r.tags["event"] ?? query.id),
343
+ data,
344
+ thingId: String(r.tags["thingId"]),
345
+ thingType: String(r.tags["thingType"] ?? query.thingType),
346
+ timestamp: r.timestamp
347
+ };
348
+ });
349
+ }
350
+ async aggregateProperties(thingId, thingType, property, aggQuery) {
351
+ const query = {
352
+ ...aggQuery,
353
+ metric: this.propertyMetric(thingType),
354
+ tags: {
355
+ ...aggQuery.tags,
356
+ thingId,
357
+ property
358
+ }
359
+ };
360
+ return this.tsService.aggregate(query);
361
+ }
362
+ };
363
+ DefaultThingsDataService = _ts_decorate2([
364
+ Injectable2(),
365
+ _ts_metadata("design:type", Function),
366
+ _ts_metadata("design:paramtypes", [
367
+ typeof TimeSeriesService === "undefined" ? Object : TimeSeriesService
368
+ ])
369
+ ], DefaultThingsDataService);
370
+
371
+ // src/things.module.ts
372
+ import { Module } from "@nestjs/common";
373
+ function _ts_decorate3(decorators, target, key, desc) {
374
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
375
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
376
+ r = Reflect.decorate(decorators, target, key, desc);
377
+ } else {
378
+ for (var i = decorators.length - 1; i >= 0; i--) {
379
+ if (d = decorators[i]) {
380
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
381
+ }
382
+ }
383
+ }
384
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
385
+ }
386
+ __name(_ts_decorate3, "_ts_decorate");
387
+ var ThingsModule = class {
388
+ static {
389
+ __name(this, "ThingsModule");
390
+ }
391
+ };
392
+ ThingsModule = _ts_decorate3([
393
+ Module({
394
+ providers: [
395
+ InMemoryThingManager
396
+ ],
397
+ exports: [
398
+ InMemoryThingManager
399
+ ]
400
+ })
401
+ ], ThingsModule);
402
+ export {
403
+ DefaultThingsDataService,
404
+ EventLevel,
405
+ InMemoryThingManager,
406
+ PropertyAccessMode,
407
+ ThingState,
408
+ ThingsModule,
409
+ convertValue,
410
+ createEventMessage,
411
+ createMessageId,
412
+ createMetadata,
413
+ createReportPropertyMessage,
414
+ mergeMetadata
415
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@zucker-framework/things",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "LICENSE",
10
+ "NOTICE",
11
+ "THIRD_PARTY_NOTICES"
12
+ ],
13
+ "dependencies": {
14
+ "@zucker-framework/core": "1.0.0",
15
+ "@zucker-framework/event": "1.0.0",
16
+ "@zucker-framework/timeseries": "1.0.0"
17
+ },
18
+ "peerDependencies": {
19
+ "@nestjs/common": "^12.0.0",
20
+ "@nestjs/core": "^12.0.0",
21
+ "rxjs": "^7.0.0"
22
+ },
23
+ "license": "Apache-2.0",
24
+ "author": "Lind",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/Lind-pro/zucker-framework.git",
28
+ "directory": "packages/things"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "scripts": {
35
+ "build": "tsup src/index.ts --format cjs,esm --dts",
36
+ "clean": "rm -rf dist",
37
+ "lint": "eslint src/"
38
+ }
39
+ }