@kmlckj/licos-platform-sdk 0.8.6 → 0.10.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
@@ -161,25 +161,62 @@ Use `uns.openPath(path)` when business code targets an existing enterprise UNS
161
161
  path without creating or changing `UNS.json`:
162
162
 
163
163
  ```js
164
- const temperature = uns.openPath('project:/production-line/temperature');
165
- const current = await temperature.pointCurrent();
164
+ const temperature = uns.openPath('project:/production-line/temperature');
165
+ const current = await temperature.pointCurrent();
166
+ ```
167
+
168
+ Realtime point consumers expose only an application-local WebSocket path. The
169
+ SDK resolves the DataFactory endpoint, registers each UNS node, handles
170
+ heartbeat/reconnect, and does not expose upstream addresses or runtime tokens to
171
+ application code or browsers:
172
+
173
+ ```js
174
+ const detachRealtime = uns.attachPointRealtimeRelay({
175
+ server,
176
+ path: '/ws/uns/points',
177
+ codes: ['TEMP_01'],
178
+ });
166
179
  ```
180
+
181
+ Resource apply, data writes, point writes, and video controls require explicit
182
+ confirmation. Data deletion accepts the exact rows returned by a prior query
183
+ and also requires explicit confirmation. Point writes require a reason and ticket. The browser entry
184
+ does not export UNS helpers.
167
185
 
168
- Realtime point consumers obtain the server-side WebSocket endpoint through the
169
- authorized project runtime API. Create one upstream connection per returned
170
- `nodeCode`; do not derive or hardcode a DataFactory address:
186
+ ## Direct Industrial Integration
187
+
188
+ The server-only `industrial` facade manages collection and video resources
189
+ without creating DataFactory UNS nodes. Use it only when the user explicitly
190
+ selects direct collector integration; UNS failures must not trigger this mode.
171
191
 
172
192
  ```js
173
- const realtime = await uns.pointRealtimeConfig('TEMP_01');
174
- // Connect to realtime.websocketUrl and register with realtime.nodeCode.
193
+ import { industrial } from '@kmlckj/licos-platform-sdk';
194
+
195
+ const points = await industrial.searchResources('collectionPoint', {
196
+ scope: 'project',
197
+ keyword: 'temperature',
198
+ });
199
+
200
+ const current = await industrial.pointCurrent(points.items[0].id);
201
+ const detachRealtime = industrial.attachRealtimeRelay({
202
+ server: httpServer,
203
+ path: '/ws/industrial/realtime',
204
+ pointIds: [points.items[0].id],
205
+ transport: 'websocket',
206
+ });
175
207
  ```
176
208
 
177
- Resource apply, data writes, point writes, and video controls require explicit
178
- confirmation. Data deletion accepts the exact rows returned by a prior query
179
- and also requires explicit confirmation. Point writes require a reason and ticket. The browser entry
180
- does not export UNS helpers.
181
-
182
- ## OAuth2 Application Management
209
+ Realtime subscription defaults to upstream WebSocket and supports explicitly
210
+ selected MQTT. Point control defaults to HTTP and supports explicitly selected
211
+ MQTT. Neither direction silently falls back. Resource writes use a reviewed
212
+ `IndustrialChangeSet`; point and video writes require independent confirmation.
213
+ `attachRealtimeRelay` exchanges and refreshes the platform user token inside the
214
+ SDK, authenticates the upstream WebSocket, sends the subscription, and relays
215
+ messages on the exact local path. Application code must not read runtime identity
216
+ variables or construct an `Authorization` header. The browser entry does not
217
+ export industrial helpers or external credentials.
218
+
219
+ ## OAuth2 Application Management
183
220
 
184
221
  OAuth2 management is available only from trusted Node runtime code. It uses the
185
222
  current project owner's platform identity and is not exported by the browser
package/dist/index.d.ts CHANGED
@@ -28,7 +28,8 @@ export interface RuntimeConfig {
28
28
  export function runtimeConfig(options?: { environmentOverrideEnv?: string }): Promise<RuntimeConfig>;
29
29
  export function authHeaders(config: RuntimeConfig, contentType?: string): Record<string, string>;
30
30
 
31
- export * from './uns.js';
31
+ export * from './uns.js';
32
+ export * from './industrial.js';
32
33
 
33
34
  export type OAuthLoginAudience = 'ALL' | 'OWNER_TENANT';
34
35
 
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{PlatformSdkError,ConfigurationError,ApiError}from"./shared.js";export{runtimeConfig,authHeaders}from"./runtime.js";export*from"./database.js";export*from"./knowledge.js";export*from"./oauth.js";export*from"./oauth-runtime.js";export*from"./storage.js";export*from"./uns.js";export{studioDatabase}from"./studio-database.js";
1
+ export{PlatformSdkError,ConfigurationError,ApiError}from"./shared.js";export{runtimeConfig,authHeaders}from"./runtime.js";export*from"./database.js";export*from"./knowledge.js";export*from"./industrial.js";export*from"./oauth.js";export*from"./oauth-runtime.js";export*from"./storage.js";export*from"./uns.js";export{studioDatabase}from"./studio-database.js";
@@ -0,0 +1,130 @@
1
+ export type IndustrialResourceType =
2
+ | 'edgeAgent'
3
+ | 'connection'
4
+ | 'collectionTask'
5
+ | 'collectionPoint'
6
+ | 'videoEdge'
7
+ | 'videoGateway'
8
+ | 'videoDevice'
9
+ | 'videoChannel';
10
+
11
+ export type IndustrialSubscriptionTransport = 'auto' | 'websocket' | 'mqtt';
12
+ export type IndustrialControlTransport = 'auto' | 'http' | 'mqtt';
13
+
14
+ export function getIndustrialProtocolSchema(protocol: string): Promise<Record<string, unknown>>;
15
+ export function searchIndustrialResources(
16
+ resourceType: IndustrialResourceType | string,
17
+ options?: {
18
+ protocol?: string;
19
+ keyword?: string;
20
+ scope?: 'project' | 'enterprise';
21
+ selector?: Record<string, unknown>;
22
+ page?: number;
23
+ pageSize?: number;
24
+ page_size?: number;
25
+ },
26
+ ): Promise<Record<string, unknown>>;
27
+ export function getIndustrialResource(
28
+ resourceType: IndustrialResourceType | string,
29
+ resourceId: string,
30
+ ): Promise<Record<string, unknown>>;
31
+ export function validateIndustrialChanges(document: Record<string, unknown>): Promise<Record<string, unknown>>;
32
+ export function planIndustrialChanges(document: Record<string, unknown>): Promise<Record<string, unknown>>;
33
+ export function applyIndustrialChanges(
34
+ document: Record<string, unknown>,
35
+ options: {
36
+ expectedDocumentHash?: string;
37
+ expected_document_hash?: string;
38
+ expectedPlanHash?: string;
39
+ expected_plan_hash?: string;
40
+ confirmed: boolean;
41
+ impactConfirmed?: boolean;
42
+ impact_confirmed?: boolean;
43
+ },
44
+ ): Promise<Record<string, unknown>>;
45
+ export function getIndustrialRuntimeCapabilities(): Promise<Record<string, unknown>>;
46
+ export function getIndustrialRealtimeConnection(options: {
47
+ pointIds?: string[];
48
+ point_ids?: string[];
49
+ tagCodes?: string[];
50
+ tag_codes?: string[];
51
+ transport?: IndustrialSubscriptionTransport;
52
+ }): Promise<{
53
+ url: string;
54
+ authentication: string;
55
+ subscribe: {
56
+ type: 'subscribe';
57
+ transport: IndustrialSubscriptionTransport;
58
+ pointIds: string[];
59
+ tagCodes: string[];
60
+ };
61
+ }>;
62
+ export function attachIndustrialRealtimeRelay(options: {
63
+ server: import('node:http').Server;
64
+ path: string;
65
+ pointIds?: string[];
66
+ point_ids?: string[];
67
+ tagCodes?: string[];
68
+ tag_codes?: string[];
69
+ transport?: IndustrialSubscriptionTransport;
70
+ }): () => void;
71
+ export function getIndustrialPointCurrent(pointId: string): Promise<Record<string, unknown>>;
72
+ export function getIndustrialPointHistory(
73
+ pointId: string,
74
+ options: {
75
+ startTime?: string;
76
+ start_time?: string;
77
+ endTime?: string;
78
+ end_time?: string;
79
+ page?: number;
80
+ pageSize?: number;
81
+ page_size?: number;
82
+ },
83
+ ): Promise<Record<string, unknown>>;
84
+ export function writeIndustrialPoint(
85
+ pointId: string,
86
+ value: unknown,
87
+ options: {
88
+ reason: string;
89
+ ticket: string;
90
+ transport?: IndustrialControlTransport;
91
+ requestId?: string;
92
+ request_id?: string;
93
+ dryRun?: boolean;
94
+ dry_run?: boolean;
95
+ confirmed?: boolean;
96
+ },
97
+ ): Promise<Record<string, unknown>>;
98
+ export function getIndustrialVideoStreamOptions(channelId: string): Promise<Record<string, unknown>>;
99
+ export function startIndustrialVideoPreview(
100
+ channelId: string,
101
+ options: {
102
+ confirmed: boolean;
103
+ preferredProtocol?: string;
104
+ preferred_protocol?: string;
105
+ preferredProtocolOrder?: string[];
106
+ preferred_protocol_order?: string[];
107
+ },
108
+ ): Promise<Record<string, unknown>>;
109
+ export function stopIndustrialVideoPreview(
110
+ channelId: string,
111
+ options: { confirmed: boolean },
112
+ ): Promise<Record<string, unknown>>;
113
+
114
+ export const industrial: {
115
+ protocolSchema: typeof getIndustrialProtocolSchema;
116
+ searchResources: typeof searchIndustrialResources;
117
+ resource: typeof getIndustrialResource;
118
+ validateChanges: typeof validateIndustrialChanges;
119
+ planChanges: typeof planIndustrialChanges;
120
+ applyChanges: typeof applyIndustrialChanges;
121
+ runtimeCapabilities: typeof getIndustrialRuntimeCapabilities;
122
+ realtimeConnection: typeof getIndustrialRealtimeConnection;
123
+ attachRealtimeRelay: typeof attachIndustrialRealtimeRelay;
124
+ pointCurrent: typeof getIndustrialPointCurrent;
125
+ pointHistory: typeof getIndustrialPointHistory;
126
+ writePoint: typeof writeIndustrialPoint;
127
+ videoStreamOptions: typeof getIndustrialVideoStreamOptions;
128
+ startVideoPreview: typeof startIndustrialVideoPreview;
129
+ stopVideoPreview: typeof stopIndustrialVideoPreview;
130
+ };
@@ -0,0 +1 @@
1
+ import{ApiError as e}from"./shared.js";import{authHeaders as t,refreshRuntimeConfig as r,runtimeConfig as n,shouldRefreshUserToken as o}from"./runtime.js";import{WebSocket as i,WebSocketServer as a}from"ws";function s(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e))}function c(e,t){return`/projects/${encodeURIComponent(e.projectId)}${t}`}async function u(i,a,{body:c,config:u}={}){let d=u||await n();for(let n=0;n<2;n+=1)try{const r=await fetch(new URL(`${d.baseUrl}/api/v1${a}`),{method:i,headers:t(d,void 0===c?void 0:"application/json"),body:void 0===c?void 0:JSON.stringify(s(c))}),n=await r.text();let o=null;try{o=n?JSON.parse(n):null}catch{throw new e(n||`platform industrial API returned ${r.status}`,{status:r.status,details:n})}if(!r.ok||o&&void 0!==o.code&&0!==o.code||!1===o?.success)throw new e(o?.message||n||"platform industrial API failed",{status:r.status,code:"number"==typeof o?.code?o.code:void 0,details:o});return o&&"object"==typeof o?o.data:o}catch(e){if(0===n&&o(e)){d=await r(d);continue}throw e}throw new e("platform industrial API failed")}export async function getIndustrialProtocolSchema(e){const t=await n();return u("GET",c(t,`/industrial/protocols/${encodeURIComponent(e)}/schema`),{config:t})}export async function searchIndustrialResources(e,t={}){const r=await n();return u("POST",c(r,"/industrial/resources/search"),{body:{resourceType:e,protocol:t.protocol,keyword:t.keyword,scope:t.scope||"project",selector:t.selector||{},page:t.page??1,pageSize:t.pageSize??t.page_size??20},config:r})}export async function getIndustrialResource(e,t){const r=await n();return u("GET",c(r,`/industrial/resources/${encodeURIComponent(e)}/${encodeURIComponent(t)}`),{config:r})}async function d(e,t){const r=await n();return u("POST",c(r,`/industrial/changes/${e}`),{body:{document:t},config:r})}export function validateIndustrialChanges(e){return d("validate",e)}export function planIndustrialChanges(e){return d("plan",e)}export async function applyIndustrialChanges(e,t={}){const r=String(t.expectedDocumentHash??t.expected_document_hash??"").trim(),o=String(t.expectedPlanHash??t.expected_plan_hash??"").trim();if(!0!==t.confirmed)throw new TypeError("industrial changes apply requires explicit confirmation");if(!r||!o)throw new TypeError("industrial changes apply requires document and plan hashes");const i=await n();return u("POST",c(i,"/industrial/changes/apply"),{body:{document:e,expectedDocumentHash:r,expectedPlanHash:o,confirmed:!0,impactConfirmed:!0===t.impactConfirmed||!0===t.impact_confirmed},config:i})}export async function getIndustrialRuntimeCapabilities(){const e=await n();return u("GET",c(e,"/industrial/runtime/capabilities"),{config:e})}function l(e,t={}){const r=(t.pointIds||t.point_ids||[]).map(String).map(e=>e.trim()).filter(Boolean),n=(t.tagCodes||t.tag_codes||[]).map(String).map(e=>e.trim()).filter(Boolean);if(0===r.length&&0===n.length)throw new TypeError("pointIds or tagCodes must be provided");const o=new URL(e.baseUrl);return o.protocol="https:"===o.protocol?"wss:":"ws:",o.pathname=`/api/v1${c(e,"/industrial/runtime/points/realtime")}`,o.search="",o.hash="",{url:o.toString(),authentication:"use platform session or Authorization header",subscribe:{type:"subscribe",transport:t.transport||"auto",pointIds:r,tagCodes:n}}}export async function getIndustrialRealtimeConnection(e={}){return l(await n(),e)}function p(r,n,o){const a=l(r,n);return new Promise((n,s)=>{const c=new i(a.url,{headers:t(r,null)});let u=!1;const d=e=>{u||s(e)};c.on("message",(e,t)=>{u&&o.onMessage(e,t)}),c.on("close",(e,t)=>{u&&o.onClose(e,t)}),c.on("error",e=>{u?o.onError(e):d(e)}),c.once("unexpected-response",(t,r)=>{r.resume(),d(function(t,r){const n=new e(t,{status:r});return n.status=r,n}(`platform industrial realtime returned ${r.statusCode||500}`,r.statusCode||500))}),c.once("open",()=>{u=!0,c.send(JSON.stringify(a.subscribe)),o.onOpen(),n(c)})})}function f(e,t){e.readyState===i.OPEN&&e.send(JSON.stringify(t))}export function attachIndustrialRealtimeRelay(e={}){const t=e.server;if(!t||"function"!=typeof t.on||"function"!=typeof t.removeListener)throw new TypeError("industrial realtime relay requires a Node HTTP server");const s=function(e){const t=String(e||"").trim();if(!t.startsWith("/")||t.includes("?")||t.includes("#"))throw new TypeError("industrial realtime relay path must be an absolute path without query or hash");return t}(e.path),c={pointIds:e.pointIds??e.point_ids,tagCodes:e.tagCodes??e.tag_codes,transport:e.transport||"websocket"};l({baseUrl:"http://localhost",projectId:"validation"},c);const u=new a({noServer:!0}),d=new Set;u.on("connection",e=>{(async e=>{let t=null;const a=()=>{t&&(d.delete(t),t.readyState!==i.OPEN&&t.readyState!==i.CONNECTING||t.close(),t=null)};e.once("close",a),e.once("error",a);try{if(t=await async function(e,t){let i=await n();try{return await p(i,e,t)}catch(n){if(!o(n))throw n;return i=await r(i),p(i,e,t)}}(c,{onOpen:()=>f(e,{type:"status",status:"connected"}),onMessage:(t,r)=>{e.readyState===i.OPEN&&e.send(t,{binary:r})},onClose:()=>{t&&d.delete(t),f(e,{type:"upstream.close",message:"上游连接已关闭"})},onError:()=>f(e,{type:"upstream.error",message:"上游连接失败"})}),e.readyState!==i.OPEN)return void a();d.add(t)}catch{f(e,{type:"error",message:"建立实时连接失败"}),e.close()}})(e)});const m=(e,t,r)=>{let n;try{n=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`).pathname}catch{return}n===s&&u.handleUpgrade(e,t,r,t=>{u.emit("connection",t,e)})};return t.on("upgrade",m),()=>{t.removeListener("upgrade",m);for(const e of d)e.close();d.clear();for(const e of u.clients)e.close(1001,"relay closed");u.close()}}function m(e,t,r){return c(e,`/industrial/runtime/points/${encodeURIComponent(t)}/${r}`)}export async function getIndustrialPointCurrent(e){const t=await n();return u("GET",m(t,e,"current"),{config:t})}export async function getIndustrialPointHistory(e,t={}){const r=await n();return u("POST",m(r,e,"history"),{body:{startTime:t.startTime??t.start_time,endTime:t.endTime??t.end_time,page:t.page??1,pageSize:t.pageSize??t.page_size??100},config:r})}export async function writeIndustrialPoint(e,t,r={}){const o=String(r.reason||"").trim(),i=String(r.ticket||"").trim(),a=!0===r.dryRun||!0===r.dry_run;if(!a&&!0!==r.confirmed)throw new TypeError("industrial point write requires explicit confirmation");if(!o||!i)throw new TypeError("industrial point write requires reason and ticket");const s=await n();return u("POST",m(s,e,"write"),{body:{value:t,transport:r.transport||"auto",reason:o,ticket:i,requestId:r.requestId??r.request_id,dryRun:a,confirmed:!0===r.confirmed},config:s})}async function h(e,t,r,o){const i=await n();return u(e,c(i,`/industrial/runtime/video/channels/${encodeURIComponent(t)}/${r}`),{body:o,config:i})}export function getIndustrialVideoStreamOptions(e){return h("GET",e,"stream-options")}export function startIndustrialVideoPreview(e,t={}){if(!0!==t.confirmed)throw new TypeError("industrial video preview start requires explicit confirmation");return h("POST",e,"preview/start",{preferredProtocol:t.preferredProtocol??t.preferred_protocol??"http-flv",preferredProtocolOrder:t.preferredProtocolOrder??t.preferred_protocol_order??["http-flv","webrtc","hls"],confirmed:!0})}export function stopIndustrialVideoPreview(e,t={}){if(!0!==t.confirmed)throw new TypeError("industrial video preview stop requires explicit confirmation");return h("POST",e,"preview/stop",{confirmed:!0})}export const industrial={protocolSchema:getIndustrialProtocolSchema,searchResources:searchIndustrialResources,resource:getIndustrialResource,validateChanges:validateIndustrialChanges,planChanges:planIndustrialChanges,applyChanges:applyIndustrialChanges,runtimeCapabilities:getIndustrialRuntimeCapabilities,realtimeConnection:getIndustrialRealtimeConnection,attachRealtimeRelay:attachIndustrialRealtimeRelay,pointCurrent:getIndustrialPointCurrent,pointHistory:getIndustrialPointHistory,writePoint:writeIndustrialPoint,videoStreamOptions:getIndustrialVideoStreamOptions,startVideoPreview:startIndustrialVideoPreview,stopVideoPreview:stopIndustrialVideoPreview};
package/dist/uns.d.ts CHANGED
@@ -84,11 +84,19 @@ export function deleteUnsNodeData(nodeCode: string, rows: UnsObject[], options:
84
84
  export function queryUnsPathData(path: string, options?: UnsDataQueryOptions): Promise<UnsObject>;
85
85
  export function writeUnsPathData(path: string, fieldValues: UnsObject, options: { confirmed: boolean }): Promise<UnsObject>;
86
86
  export function deleteUnsPathData(path: string, rows: UnsObject[], options: { confirmed: boolean }): Promise<UnsObject>;
87
- export function getUnsPointCurrent(nodeCode: string): Promise<UnsObject>;
88
- export function getUnsPathPointCurrent(path: string): Promise<UnsObject>;
87
+ export function getUnsPointCurrent(nodeCode: string): Promise<UnsObject>;
88
+ export function getUnsPathPointCurrent(path: string): Promise<UnsObject>;
89
89
  export function getUnsPointRealtimeConfig(nodeCode: string): Promise<UnsObject>;
90
90
  export function getUnsPathPointRealtimeConfig(path: string): Promise<UnsObject>;
91
- export function getUnsPointHistory(nodeCode: string, options: UnsPointHistoryOptions): Promise<UnsObject>;
91
+ export function attachUnsPointRealtimeRelay(options: {
92
+ server: import('node:http').Server;
93
+ path: string;
94
+ codes?: string[];
95
+ nodeCodes?: string[];
96
+ node_codes?: string[];
97
+ paths?: string[];
98
+ }): () => void;
99
+ export function getUnsPointHistory(nodeCode: string, options: UnsPointHistoryOptions): Promise<UnsObject>;
92
100
  export function getUnsPathPointHistory(path: string, options: UnsPointHistoryOptions): Promise<UnsObject>;
93
101
  export function writeUnsPoint(nodeCode: string, value: unknown, options: UnsPointWriteOptions): Promise<UnsObject>;
94
102
  export function writeUnsPathPoint(path: string, value: unknown, options: UnsPointWriteOptions): Promise<UnsObject>;
@@ -105,10 +113,10 @@ export class UnsPathNode {
105
113
  resolve(): Promise<UnsObject>;
106
114
  queryData(options?: UnsDataQueryOptions): Promise<UnsObject>;
107
115
  writeData(fieldValues: UnsObject, options: { confirmed: boolean }): Promise<UnsObject>;
108
- deleteData(rows: UnsObject[], options: { confirmed: boolean }): Promise<UnsObject>;
109
- pointCurrent(): Promise<UnsObject>;
110
- pointRealtimeConfig(): Promise<UnsObject>;
111
- pointHistory(options: UnsPointHistoryOptions): Promise<UnsObject>;
116
+ deleteData(rows: UnsObject[], options: { confirmed: boolean }): Promise<UnsObject>;
117
+ pointCurrent(): Promise<UnsObject>;
118
+ pointRealtimeConfig(): Promise<UnsObject>;
119
+ pointHistory(options: UnsPointHistoryOptions): Promise<UnsObject>;
112
120
  writePoint(value: unknown, options: UnsPointWriteOptions): Promise<UnsObject>;
113
121
  videoStreamOptions(): Promise<UnsObject>;
114
122
  videoSnapshot(): Promise<UnsObject>;
@@ -139,11 +147,12 @@ export const uns: {
139
147
  queryPathData: typeof queryUnsPathData;
140
148
  writePathData: typeof writeUnsPathData;
141
149
  deletePathData: typeof deleteUnsPathData;
142
- pointCurrent: typeof getUnsPointCurrent;
143
- pathPointCurrent: typeof getUnsPathPointCurrent;
150
+ pointCurrent: typeof getUnsPointCurrent;
151
+ pathPointCurrent: typeof getUnsPathPointCurrent;
144
152
  pointRealtimeConfig: typeof getUnsPointRealtimeConfig;
145
153
  pathPointRealtimeConfig: typeof getUnsPathPointRealtimeConfig;
146
- pointHistory: typeof getUnsPointHistory;
154
+ attachPointRealtimeRelay: typeof attachUnsPointRealtimeRelay;
155
+ pointHistory: typeof getUnsPointHistory;
147
156
  pathPointHistory: typeof getUnsPathPointHistory;
148
157
  writePoint: typeof writeUnsPoint;
149
158
  writePathPoint: typeof writeUnsPathPoint;
package/dist/uns.js CHANGED
@@ -1 +1 @@
1
- import{ApiError as t}from"./shared.js";import{authHeaders as e,refreshRuntimeConfig as n,runtimeConfig as o,shouldRefreshUserToken as r}from"./runtime.js";function a(t){return Object.fromEntries(Object.entries(t).filter(([,t])=>null!=t))}function i(t,e){return`/projects/${encodeURIComponent(t.projectId)}${e}`}function s(t,e,n,o){return i(t,`/uns/runtime/${e}/${encodeURIComponent(n)}/${o}`)}function c(t,e,n){const o=String(n||"").trim();if(!o)throw new TypeError("UNS path cannot be empty");return`${i(t,`/uns/runtime/path/${e}`)}?${new URLSearchParams({path:o})}`}function p(t,e){return new URL(`${t.baseUrl}/api/v1${e}`)}async function u(e){const n=await e.text();let o=null;try{o=n?JSON.parse(n):null}catch{throw new t(n||`platform UNS API returned ${e.status}`,{status:e.status,details:n})}if(!e.ok)throw new t(o?.message||n||`platform UNS API returned ${e.status}`,{status:e.status,code:"number"==typeof o?.code?o.code:void 0,details:o});if(!o||"object"!=typeof o)return o;if(void 0!==o.code&&0!==o.code||!1===o.success)throw new t(o.message||"platform UNS API failed",{status:e.status,code:"number"==typeof o.code?o.code:void 0,details:o});return o.data}async function d(i,s,{body:c,config:d}={}){let h=d||await o();for(let t=0;t<2;t+=1)try{const t="function"==typeof s?s(h):s,n=await fetch(p(h,t),{method:i,headers:e(h,void 0===c?void 0:"application/json"),body:void 0===c?void 0:JSON.stringify(a(c))});return await u(n)}catch(e){if(0===t&&r(e)){h=await n(h);continue}throw e}throw new t("platform UNS API failed")}export function getCurrentUnsSchema(){return d("GET","/uns/schemas/current")}export function getUnsSchema(t="1.0"){return d("GET",`/uns/schemas/${encodeURIComponent(t)}`)}export function listUnsSchemaBlocks(t="1.0"){return d("GET",`/uns/schemas/${encodeURIComponent(t)}/blocks`)}export function getUnsSchemaBlock(t,{version:e="1.0",depth:n=1}={}){return d("GET",`/uns/schemas/${encodeURIComponent(e)}/blocks/${encodeURIComponent(t)}?depth=${encodeURIComponent(n)}`)}export function getUnsProtocolSchema(t,{version:e="1.0"}={}){return d("GET",`/uns/schemas/${encodeURIComponent(e)}/blocks/collection-point/protocols/${encodeURIComponent(t)}`)}export async function discoverUnsResources({resourceTypes:t=[],protocols:e=[]}={}){const n=await o();return d("POST",i(n,"/uns/discover"),{body:{resourceTypes:t,protocols:e},config:n})}export async function searchUnsResources(t,e={}){const n=await o();return d("POST",i(n,"/uns/resources/search"),{body:{resourceType:t,protocol:e.protocol,keyword:e.keyword,selector:e.selector||{},page:e.page??1,pageSize:e.pageSize??e.page_size??20},config:n})}export async function listUnsBindings(){const t=await o();return d("GET",i(t,"/uns/bindings"),{config:t})}export async function resolveUnsPath(t){const e=await o();return d("GET",c(e,"resolve",t),{config:e})}export async function validateUnsDocument(t){const e=await o();return d("POST",i(e,"/uns/validate"),{body:{document:t},config:e})}export async function planUnsDocument(t){const e=await o();return d("POST",i(e,"/uns/plan"),{body:{document:t},config:e})}export async function applyUnsDocument(t,e={}){const n=String(e.expectedDocumentHash??e.expected_document_hash??"").trim(),r=String(e.expectedPlanHash??e.expected_plan_hash??"").trim();if(!0!==e.confirmed)throw new TypeError("UNS apply requires explicit confirmation");if(!n||!r)throw new TypeError("UNS apply requires document and plan hashes");const a=await o();return d("POST",i(a,"/uns/apply"),{body:{document:t,expectedDocumentHash:n,expectedPlanHash:r,confirmed:!0},config:a})}export async function validateUnsChanges(t){const e=await o();return d("POST",i(e,"/uns/changes/validate"),{body:{document:t},config:e})}export async function planUnsChanges(t){const e=await o();return d("POST",i(e,"/uns/changes/plan"),{body:{document:t},config:e})}export async function applyUnsChanges(t,e={}){const n=String(e.expectedDocumentHash??e.expected_document_hash??"").trim(),r=String(e.expectedPlanHash??e.expected_plan_hash??"").trim();if(!0!==e.confirmed)throw new TypeError("UNS changes apply requires explicit confirmation");if(!n||!r)throw new TypeError("UNS changes apply requires document and plan hashes");const a=await o();return d("POST",i(a,"/uns/changes/apply"),{body:{document:t,expectedDocumentHash:n,expectedPlanHash:r,confirmed:!0,impactConfirmed:!0===e.impactConfirmed||!0===e.impact_confirmed},config:a})}function h(t={}){return a({pageNum:t.pageNum??t.page_num??1,pageSize:t.pageSize??t.page_size??50,paginationMode:t.paginationMode??t.pagination_mode??"OFFSET",pageToken:t.pageToken??t.page_token})}export async function queryUnsNodeData(t,e={}){const n=await o();return d("POST",s(n,"nodes",t,"query"),{body:h(e),config:n})}function f(t,e){if(!0!==e)throw new TypeError("UNS data write requires explicit confirmation");if(!t||"object"!=typeof t||0===Object.keys(t).length)throw new TypeError("UNS data write field values cannot be empty")}export async function writeUnsNodeData(t,e,{confirmed:n=!1}={}){f(e,n);const r=await o();return d("POST",s(r,"nodes",t,"write"),{body:{fieldValues:e,environment:r.environment,confirmed:!0},config:r})}function m(t,e){if(!0!==e)throw new TypeError("UNS data deletion requires explicit confirmation");if(!Array.isArray(t)||0===t.length||t.some(t=>!t||"object"!=typeof t||Array.isArray(t)||0===Object.keys(t).length))throw new TypeError("UNS data deletion rows cannot be empty")}export async function deleteUnsNodeData(t,e,{confirmed:n=!1}={}){m(e,n);const r=await o();return d("POST",s(r,"nodes",t,"delete"),{body:{rows:e,confirmed:!0},config:r})}export async function queryUnsPathData(t,e={}){const n=await o();return d("POST",c(n,"data/query",t),{body:h(e),config:n})}export async function writeUnsPathData(t,e,{confirmed:n=!1}={}){f(e,n);const r=await o();return d("POST",c(r,"data/write",t),{body:{fieldValues:e,environment:r.environment,confirmed:!0},config:r})}export async function deleteUnsPathData(t,e,{confirmed:n=!1}={}){m(e,n);const r=await o();return d("POST",c(r,"data/delete",t),{body:{rows:e,confirmed:!0},config:r})}export async function getUnsPointCurrent(t){const e=await o();return d("GET",s(e,"points",t,"current"),{config:e})}export async function getUnsPathPointCurrent(t){const e=await o();return d("GET",c(e,"point/current",t),{config:e})}export async function getUnsPointRealtimeConfig(t){const e=await o();return d("GET",s(e,"points",t,"realtime-config"),{config:e})}export async function getUnsPathPointRealtimeConfig(t){const e=await o();return d("GET",c(e,"point/realtime-config",t),{config:e})}function l(t={}){return{startTime:t.startTime??t.start_time,endTime:t.endTime??t.end_time,page:t.page??1,pageSize:t.pageSize??t.page_size??100}}export async function getUnsPointHistory(t,e){const n=await o();return d("POST",s(n,"points",t,"history"),{body:l(e),config:n})}export async function getUnsPathPointHistory(t,e){const n=await o();return d("POST",c(n,"point/history",t),{body:l(e),config:n})}function y(t,e={}){const n=String(e.reason||"").trim(),o=String(e.ticket||"").trim(),r=e.dryRun??e.dry_run??!1,i=e.confirmed??!1;if(!r&&!0!==i)throw new TypeError("UNS point write requires explicit confirmation");if(!n||!o)throw new TypeError("UNS point write requires reason and ticket");return a({value:t,reason:n,ticket:o,requestId:e.requestId??e.request_id,dryRun:r,confirmed:i})}export async function writeUnsPoint(t,e,n={}){const r=y(e,n),a=await o();return d("POST",s(a,"points",t,"write"),{body:r,config:a})}export async function writeUnsPathPoint(t,e,n={}){const r=y(e,n),a=await o();return d("POST",c(a,"point/write",t),{body:r,config:a})}export async function getUnsVideoStreamOptions(t){const e=await o();return d("GET",s(e,"videos",t,"stream-options"),{config:e})}export async function getUnsPathVideoStreamOptions(t){const e=await o();return d("GET",c(e,"video/stream-options",t),{config:e})}export async function createUnsVideoSnapshot(t){const e=await o();return d("POST",s(e,"videos",t,"snapshot"),{config:e})}export async function createUnsPathVideoSnapshot(t){const e=await o();return d("POST",c(e,"video/snapshot",t),{config:e})}function g(t,e={}){if(!0!==e.confirmed)throw new TypeError("UNS video control requires explicit confirmation");return{action:t,parameters:e.parameters||{},confirmed:!0}}export async function controlUnsVideo(t,e,n={}){const r=g(e,n),a=await o();return d("POST",s(a,"videos",t,"control"),{body:r,config:a})}export async function controlUnsPathVideo(t,e,n={}){const r=g(e,n),a=await o();return d("POST",c(a,"video/control",t),{body:r,config:a})}export class UnsPathNode{constructor(t){if(this.path=String(t||"").trim(),!this.path)throw new TypeError("UNS path cannot be empty")}resolve(){return resolveUnsPath(this.path)}queryData(t){return queryUnsPathData(this.path,t)}writeData(t,e){return writeUnsPathData(this.path,t,e)}deleteData(t,e){return deleteUnsPathData(this.path,t,e)}pointCurrent(){return getUnsPathPointCurrent(this.path)}pointRealtimeConfig(){return getUnsPathPointRealtimeConfig(this.path)}pointHistory(t){return getUnsPathPointHistory(this.path,t)}writePoint(t,e){return writeUnsPathPoint(this.path,t,e)}videoStreamOptions(){return getUnsPathVideoStreamOptions(this.path)}videoSnapshot(){return createUnsPathVideoSnapshot(this.path)}controlVideo(t,e){return controlUnsPathVideo(this.path,t,e)}}export function openUnsPath(t){return new UnsPathNode(t)}export const uns={schemaCurrent:getCurrentUnsSchema,schema:getUnsSchema,schemaBlocks:listUnsSchemaBlocks,schemaBlock:getUnsSchemaBlock,protocolSchema:getUnsProtocolSchema,discover:discoverUnsResources,searchResources:searchUnsResources,bindings:listUnsBindings,resolvePath:resolveUnsPath,validate:validateUnsDocument,plan:planUnsDocument,apply:applyUnsDocument,validateChanges:validateUnsChanges,planChanges:planUnsChanges,applyChanges:applyUnsChanges,queryNodeData:queryUnsNodeData,writeNodeData:writeUnsNodeData,deleteNodeData:deleteUnsNodeData,queryPathData:queryUnsPathData,writePathData:writeUnsPathData,deletePathData:deleteUnsPathData,pointCurrent:getUnsPointCurrent,pathPointCurrent:getUnsPathPointCurrent,pointRealtimeConfig:getUnsPointRealtimeConfig,pathPointRealtimeConfig:getUnsPathPointRealtimeConfig,pointHistory:getUnsPointHistory,pathPointHistory:getUnsPathPointHistory,writePoint:writeUnsPoint,writePathPoint:writeUnsPathPoint,videoStreamOptions:getUnsVideoStreamOptions,pathVideoStreamOptions:getUnsPathVideoStreamOptions,videoSnapshot:createUnsVideoSnapshot,pathVideoSnapshot:createUnsPathVideoSnapshot,controlVideo:controlUnsVideo,controlPathVideo:controlUnsPathVideo,openPath:openUnsPath};
1
+ import{ApiError as e}from"./shared.js";import{authHeaders as t,refreshRuntimeConfig as n,runtimeConfig as o,shouldRefreshUserToken as r}from"./runtime.js";import{WebSocket as a,WebSocketServer as i}from"ws";function s(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e))}function c(e,t){return`/projects/${encodeURIComponent(e.projectId)}${t}`}function p(e,t,n,o){return c(e,`/uns/runtime/${t}/${encodeURIComponent(n)}/${o}`)}function u(e,t,n){const o=String(n||"").trim();if(!o)throw new TypeError("UNS path cannot be empty");return`${c(e,`/uns/runtime/path/${t}`)}?${new URLSearchParams({path:o})}`}function d(e,t){return new URL(`${e.baseUrl}/api/v1${t}`)}async function l(t){const n=await t.text();let o=null;try{o=n?JSON.parse(n):null}catch{throw new e(n||`platform UNS API returned ${t.status}`,{status:t.status,details:n})}if(!t.ok)throw new e(o?.message||n||`platform UNS API returned ${t.status}`,{status:t.status,code:"number"==typeof o?.code?o.code:void 0,details:o});if(!o||"object"!=typeof o)return o;if(void 0!==o.code&&0!==o.code||!1===o.success)throw new e(o.message||"platform UNS API failed",{status:t.status,code:"number"==typeof o.code?o.code:void 0,details:o});return o.data}async function h(a,i,{body:c,config:p}={}){let u=p||await o();for(let e=0;e<2;e+=1)try{const e="function"==typeof i?i(u):i,n=await fetch(d(u,e),{method:a,headers:t(u,void 0===c?void 0:"application/json"),body:void 0===c?void 0:JSON.stringify(s(c))});return await l(n)}catch(t){if(0===e&&r(t)){u=await n(u);continue}throw t}throw new e("platform UNS API failed")}export function getCurrentUnsSchema(){return h("GET","/uns/schemas/current")}export function getUnsSchema(e="1.0"){return h("GET",`/uns/schemas/${encodeURIComponent(e)}`)}export function listUnsSchemaBlocks(e="1.0"){return h("GET",`/uns/schemas/${encodeURIComponent(e)}/blocks`)}export function getUnsSchemaBlock(e,{version:t="1.0",depth:n=1}={}){return h("GET",`/uns/schemas/${encodeURIComponent(t)}/blocks/${encodeURIComponent(e)}?depth=${encodeURIComponent(n)}`)}export function getUnsProtocolSchema(e,{version:t="1.0"}={}){return h("GET",`/uns/schemas/${encodeURIComponent(t)}/blocks/collection-point/protocols/${encodeURIComponent(e)}`)}export async function discoverUnsResources({resourceTypes:e=[],protocols:t=[]}={}){const n=await o();return h("POST",c(n,"/uns/discover"),{body:{resourceTypes:e,protocols:t},config:n})}export async function searchUnsResources(e,t={}){const n=await o();return h("POST",c(n,"/uns/resources/search"),{body:{resourceType:e,protocol:t.protocol,keyword:t.keyword,selector:t.selector||{},page:t.page??1,pageSize:t.pageSize??t.page_size??20},config:n})}export async function listUnsBindings(){const e=await o();return h("GET",c(e,"/uns/bindings"),{config:e})}export async function resolveUnsPath(e){const t=await o();return h("GET",u(t,"resolve",e),{config:t})}export async function validateUnsDocument(e){const t=await o();return h("POST",c(t,"/uns/validate"),{body:{document:e},config:t})}export async function planUnsDocument(e){const t=await o();return h("POST",c(t,"/uns/plan"),{body:{document:e},config:t})}export async function applyUnsDocument(e,t={}){const n=String(t.expectedDocumentHash??t.expected_document_hash??"").trim(),r=String(t.expectedPlanHash??t.expected_plan_hash??"").trim();if(!0!==t.confirmed)throw new TypeError("UNS apply requires explicit confirmation");if(!n||!r)throw new TypeError("UNS apply requires document and plan hashes");const a=await o();return h("POST",c(a,"/uns/apply"),{body:{document:e,expectedDocumentHash:n,expectedPlanHash:r,confirmed:!0},config:a})}export async function validateUnsChanges(e){const t=await o();return h("POST",c(t,"/uns/changes/validate"),{body:{document:e},config:t})}export async function planUnsChanges(e){const t=await o();return h("POST",c(t,"/uns/changes/plan"),{body:{document:e},config:t})}export async function applyUnsChanges(e,t={}){const n=String(t.expectedDocumentHash??t.expected_document_hash??"").trim(),r=String(t.expectedPlanHash??t.expected_plan_hash??"").trim();if(!0!==t.confirmed)throw new TypeError("UNS changes apply requires explicit confirmation");if(!n||!r)throw new TypeError("UNS changes apply requires document and plan hashes");const a=await o();return h("POST",c(a,"/uns/changes/apply"),{body:{document:e,expectedDocumentHash:n,expectedPlanHash:r,confirmed:!0,impactConfirmed:!0===t.impactConfirmed||!0===t.impact_confirmed},config:a})}function f(e={}){return s({pageNum:e.pageNum??e.page_num??1,pageSize:e.pageSize??e.page_size??50,paginationMode:e.paginationMode??e.pagination_mode??"OFFSET",pageToken:e.pageToken??e.page_token})}export async function queryUnsNodeData(e,t={}){const n=await o();return h("POST",p(n,"nodes",e,"query"),{body:f(t),config:n})}function m(e,t){if(!0!==t)throw new TypeError("UNS data write requires explicit confirmation");if(!e||"object"!=typeof e||0===Object.keys(e).length)throw new TypeError("UNS data write field values cannot be empty")}export async function writeUnsNodeData(e,t,{confirmed:n=!1}={}){m(t,n);const r=await o();return h("POST",p(r,"nodes",e,"write"),{body:{fieldValues:t,environment:r.environment,confirmed:!0},config:r})}function y(e,t){if(!0!==t)throw new TypeError("UNS data deletion requires explicit confirmation");if(!Array.isArray(e)||0===e.length||e.some(e=>!e||"object"!=typeof e||Array.isArray(e)||0===Object.keys(e).length))throw new TypeError("UNS data deletion rows cannot be empty")}export async function deleteUnsNodeData(e,t,{confirmed:n=!1}={}){y(t,n);const r=await o();return h("POST",p(r,"nodes",e,"delete"),{body:{rows:t,confirmed:!0},config:r})}export async function queryUnsPathData(e,t={}){const n=await o();return h("POST",u(n,"data/query",e),{body:f(t),config:n})}export async function writeUnsPathData(e,t,{confirmed:n=!1}={}){m(t,n);const r=await o();return h("POST",u(r,"data/write",e),{body:{fieldValues:t,environment:r.environment,confirmed:!0},config:r})}export async function deleteUnsPathData(e,t,{confirmed:n=!1}={}){y(t,n);const r=await o();return h("POST",u(r,"data/delete",e),{body:{rows:t,confirmed:!0},config:r})}export async function getUnsPointCurrent(e){const t=await o();return h("GET",p(t,"points",e,"current"),{config:t})}export async function getUnsPathPointCurrent(e){const t=await o();return h("GET",u(t,"point/current",e),{config:t})}export async function getUnsPointRealtimeConfig(e){const t=await o();return h("GET",p(t,"points",e,"realtime-config"),{config:t})}export async function getUnsPathPointRealtimeConfig(e){const t=await o();return h("GET",u(t,"point/realtime-config",e),{config:t})}function g(e,t){if(null==e)return[];if(!Array.isArray(e))throw new TypeError(`${t} must be an array`);return e.map(String).map(e=>e.trim()).filter(Boolean)}function w(e,t){const n="string"==typeof e?e.trim():"";if(!n)throw new TypeError(`UNS point realtime config is missing ${t}`);return n}function U(e,t){return"string"==typeof e&&e.trim()?e.trim():t}function P(e){return"code"===e.kind?{code:e.value}:{path:e.value}}function S(e,t){e.readyState===a.OPEN&&e.send(JSON.stringify(t))}export function attachUnsPointRealtimeRelay(e={}){const t=e.server;if(!t||"function"!=typeof t.on||"function"!=typeof t.removeListener)throw new TypeError("UNS point realtime relay requires a Node HTTP server");const n=function(e){const t=String(e||"").trim();if(!t.startsWith("/")||t.includes("?")||t.includes("#"))throw new TypeError("UNS point realtime relay path must be an absolute path without query or hash");return t}(e.path),o=function(e){const t=g(e.codes??e.nodeCodes??e.node_codes,"codes"),n=g(e.paths,"paths"),o=[...t.map(e=>({kind:"code",value:e})),...n.map(e=>({kind:"path",value:e}))],r=new Map(o.map(e=>[`${e.kind}:${e.value}`,e]));if(0===r.size)throw new TypeError("UNS point realtime relay requires at least one code or path");return[...r.values()]}(e),r=new i({noServer:!0});let c=!1;const p=o.map(e=>({target:e,socket:null,reconnectTimer:null,reconnectAttempt:0,status:"connecting",statusMessage:void 0,lastMessage:null,messageFingerprints:new Set})),u=e=>s({type:"uns.point.status",target:P(e.target),status:e.status,message:e.statusMessage}),d=e=>{for(const t of r.clients)S(t,e)},l=(e,t,n)=>{e.status=t,e.statusMessage=n,d(u(e))},h=async e=>{if(c)return;let t,n;l(e,"connecting");try{const n="code"===e.target.kind?await getUnsPointRealtimeConfig(e.target.value):await getUnsPathPointRealtimeConfig(e.target.value);t=function(e){if(!e||"object"!=typeof e||Array.isArray(e))throw new TypeError("UNS point realtime config must be an object");const t=e.registration&&"object"==typeof e.registration&&!Array.isArray(e.registration)?e.registration:{},n=w(e.nodeCode,"nodeCode"),o=new URL(w(e.websocketUrl,"websocketUrl"));if("ws:"!==o.protocol&&"wss:"!==o.protocol)throw new TypeError("UNS point realtime websocketUrl must use ws or wss");const r=U(t.queryParameter,"clientKey");return o.searchParams.set(r,n),{nodeCode:n,websocketUrl:o.toString(),queryParameter:r,connectedType:U(t.connectedType,"CONNECTED"),registerType:U(t.registerType,"REGISTER"),registeredType:U(t.registeredType,"REGISTERED"),messageType:U(t.messageType,"UNS_POINT_MESSAGE"),pingType:U(t.pingType,"PING"),pongType:U(t.pongType,"PONG")}}(n)}catch{return void l(e,"error","无法获取 UNS 点位实时连接配置")}if(c)return;try{n=new a(t.websocketUrl)}catch{return void l(e,"error","无法建立数据工厂实时连接")}e.socket=n;let o=!1,r=!1;n.on("open",()=>{c||e.socket!==n||l(e,"connected")}),n.on("message",(i,s)=>{if(c||e.socket!==n)return;const p=function(e,t){if(t)return null;try{const t=JSON.parse(e.toString());return t&&"object"==typeof t&&!Array.isArray(t)?t:null}catch{return null}}(i,s);if(!p)return;const u="string"==typeof p.type?p.type:"";if(u===t.pingType)return void(n.readyState===a.OPEN&&n.send(JSON.stringify({type:t.pongType})));if(u===t.connectedType&&!o&&!r)return r=!0,void n.send(JSON.stringify({type:t.registerType,[t.queryParameter]:t.nodeCode}));if(u===t.registeredType)return o=!0,e.reconnectAttempt=0,void l(e,"registered");if(u!==t.messageType||!o)return;if(void 0!==p.nodeCode&&String(p.nodeCode)!==t.nodeCode)return void l(e,"error","数据工厂返回了不匹配的 UNS 点位消息");if(!function(e,t){const n=function(e){try{return JSON.stringify([e.nodeCode,e.topicName,e.receivedAt,e.status,e.message])}catch{return null}}(t);return!n||!e.messageFingerprints.has(n)&&(e.messageFingerprints.add(n),e.messageFingerprints.size>256&&e.messageFingerprints.delete(e.messageFingerprints.values().next().value),!0)}(e,p))return;const h={type:"uns.point.message",target:P(e.target),payload:p};e.lastMessage=h,d(h)}),n.on("error",()=>{c||e.socket!==n||l(e,"error","数据工厂实时连接失败")}),n.on("close",t=>{e.socket===n&&(e.socket=null,o=!1,c||(1e3!==t&&1001!==t?((e,t)=>{if(c||e.reconnectTimer)return;e.reconnectAttempt+=1;const n=Math.min(3e4,500*2**Math.min(e.reconnectAttempt-1,6)),o=Math.round(n*(.75+.5*Math.random()));l(e,"reconnecting","数据工厂实时连接已断开,正在重连"),e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=null,t(e)},o)})(e,h):l(e,"disconnected")))})};r.on("connection",e=>{for(const t of p)S(e,u(t)),t.lastMessage&&S(e,t.lastMessage)});const f=(e,t,o)=>{let a;try{a=new URL(e.url||"/",`http://${e.headers.host||"localhost"}`).pathname}catch{return}a===n&&r.handleUpgrade(e,t,o,t=>{r.emit("connection",t,e)})};t.on("upgrade",f);for(const e of p)h(e);return()=>{if(!c){c=!0,t.removeListener("upgrade",f);for(const e of p)e.reconnectTimer&&clearTimeout(e.reconnectTimer),!e.socket||e.socket.readyState!==a.OPEN&&e.socket.readyState!==a.CONNECTING||e.socket.close(1001,"relay closed"),e.socket=null;for(const e of r.clients)e.close(1001,"relay closed");r.close()}}}function T(e={}){return{startTime:e.startTime??e.start_time,endTime:e.endTime??e.end_time,page:e.page??1,pageSize:e.pageSize??e.page_size??100}}export async function getUnsPointHistory(e,t){const n=await o();return h("POST",p(n,"points",e,"history"),{body:T(t),config:n})}export async function getUnsPathPointHistory(e,t){const n=await o();return h("POST",u(n,"point/history",e),{body:T(t),config:n})}function v(e,t={}){const n=String(t.reason||"").trim(),o=String(t.ticket||"").trim(),r=t.dryRun??t.dry_run??!1,a=t.confirmed??!1;if(!r&&!0!==a)throw new TypeError("UNS point write requires explicit confirmation");if(!n||!o)throw new TypeError("UNS point write requires reason and ticket");return s({value:e,reason:n,ticket:o,requestId:t.requestId??t.request_id,dryRun:r,confirmed:a})}export async function writeUnsPoint(e,t,n={}){const r=v(t,n),a=await o();return h("POST",p(a,"points",e,"write"),{body:r,config:a})}export async function writeUnsPathPoint(e,t,n={}){const r=v(t,n),a=await o();return h("POST",u(a,"point/write",e),{body:r,config:a})}export async function getUnsVideoStreamOptions(e){const t=await o();return h("GET",p(t,"videos",e,"stream-options"),{config:t})}export async function getUnsPathVideoStreamOptions(e){const t=await o();return h("GET",u(t,"video/stream-options",e),{config:t})}export async function createUnsVideoSnapshot(e){const t=await o();return h("POST",p(t,"videos",e,"snapshot"),{config:t})}export async function createUnsPathVideoSnapshot(e){const t=await o();return h("POST",u(t,"video/snapshot",e),{config:t})}function x(e,t={}){if(!0!==t.confirmed)throw new TypeError("UNS video control requires explicit confirmation");return{action:e,parameters:t.parameters||{},confirmed:!0}}export async function controlUnsVideo(e,t,n={}){const r=x(t,n),a=await o();return h("POST",p(a,"videos",e,"control"),{body:r,config:a})}export async function controlUnsPathVideo(e,t,n={}){const r=x(t,n),a=await o();return h("POST",u(a,"video/control",e),{body:r,config:a})}export class UnsPathNode{constructor(e){if(this.path=String(e||"").trim(),!this.path)throw new TypeError("UNS path cannot be empty")}resolve(){return resolveUnsPath(this.path)}queryData(e){return queryUnsPathData(this.path,e)}writeData(e,t){return writeUnsPathData(this.path,e,t)}deleteData(e,t){return deleteUnsPathData(this.path,e,t)}pointCurrent(){return getUnsPathPointCurrent(this.path)}pointRealtimeConfig(){return getUnsPathPointRealtimeConfig(this.path)}pointHistory(e){return getUnsPathPointHistory(this.path,e)}writePoint(e,t){return writeUnsPathPoint(this.path,e,t)}videoStreamOptions(){return getUnsPathVideoStreamOptions(this.path)}videoSnapshot(){return createUnsPathVideoSnapshot(this.path)}controlVideo(e,t){return controlUnsPathVideo(this.path,e,t)}}export function openUnsPath(e){return new UnsPathNode(e)}export const uns={schemaCurrent:getCurrentUnsSchema,schema:getUnsSchema,schemaBlocks:listUnsSchemaBlocks,schemaBlock:getUnsSchemaBlock,protocolSchema:getUnsProtocolSchema,discover:discoverUnsResources,searchResources:searchUnsResources,bindings:listUnsBindings,resolvePath:resolveUnsPath,validate:validateUnsDocument,plan:planUnsDocument,apply:applyUnsDocument,validateChanges:validateUnsChanges,planChanges:planUnsChanges,applyChanges:applyUnsChanges,queryNodeData:queryUnsNodeData,writeNodeData:writeUnsNodeData,deleteNodeData:deleteUnsNodeData,queryPathData:queryUnsPathData,writePathData:writeUnsPathData,deletePathData:deleteUnsPathData,pointCurrent:getUnsPointCurrent,pathPointCurrent:getUnsPathPointCurrent,pointRealtimeConfig:getUnsPointRealtimeConfig,pathPointRealtimeConfig:getUnsPathPointRealtimeConfig,attachPointRealtimeRelay:attachUnsPointRealtimeRelay,pointHistory:getUnsPointHistory,pathPointHistory:getUnsPathPointHistory,writePoint:writeUnsPoint,writePathPoint:writeUnsPathPoint,videoStreamOptions:getUnsVideoStreamOptions,pathVideoStreamOptions:getUnsPathVideoStreamOptions,videoSnapshot:createUnsVideoSnapshot,pathVideoSnapshot:createUnsPathVideoSnapshot,controlVideo:controlUnsVideo,controlPathVideo:controlUnsPathVideo,openPath:openUnsPath};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kmlckj/licos-platform-sdk",
3
- "version": "0.8.6",
3
+ "version": "0.10.0",
4
4
  "description": "LICOS platform SDK package shell for browser and Node runtimes",
5
5
  "author": "kmlckj",
6
6
  "type": "module",
@@ -34,12 +34,15 @@
34
34
  "dist",
35
35
  "README.md"
36
36
  ],
37
- "scripts": {
37
+ "scripts": {
38
38
  "build": "node build.mjs",
39
39
  "prepack": "npm run build",
40
40
  "test": "node --test"
41
- },
42
- "devDependencies": {
41
+ },
42
+ "dependencies": {
43
+ "ws": "8.21.1"
44
+ },
45
+ "devDependencies": {
43
46
  "terser": "5.48.0"
44
47
  },
45
48
  "license": "MIT",