@flaxia/node 0.3.5 → 0.3.6

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
@@ -14,7 +14,7 @@ npm install @flaxia/node
14
14
  ```typescript
15
15
  import { initFlaxiaNode } from '@flaxia/node'
16
16
 
17
- initFlaxiaNode({
17
+ const controller = initFlaxiaNode({
18
18
  orchestratorUrl: 'https://crowd.flaxia.app',
19
19
  siteId: 'your-site-id',
20
20
  consent: {
@@ -34,11 +34,56 @@ initFlaxiaNode({
34
34
  | `orchestratorUrl` | `string` | yes | オーケストレーターのURL |
35
35
  | `siteId` | `string` | yes | サイト固有の識別子 |
36
36
  | `consent` | `ConsentConfig` | yes | 同意UIの設定 |
37
- | `consent.brandName` | `string` | yes | サイト名(同意UIに表示) |
38
- | `consent.position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | yes | 同意UIの表示位置 |
39
- | `consent.accentColor` | `string` | no | 同意UIのアクセントカラー |
37
+ | `consent.brandName` | `string` | yes | サイト名(内蔵UIに表示) |
38
+ | `consent.position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | yes | 内蔵UIの表示位置 |
39
+ | `consent.accentColor` | `string` | no | 内蔵UIのアクセントカラー |
40
+ | `consent.onConsentRequired` | `(controls: ConsentControls) => void` | no | ホスト独自の同意UIを使う場合に指定。指定すると内蔵バナーは表示されない |
40
41
  | `maxCpuLoad` | `number` | no | CPU負荷制限(未実装) |
41
42
 
43
+ ### ホスト独自の同意UI
44
+
45
+ `consent.onConsentRequired` を渡すと、内蔵の Shadow DOM バナーは使われず、
46
+ 同意状態が `unset` のときだけホストが呼び出されます。ホストは `controls.accept()` /
47
+ `controls.reject()` を呼ぶだけで、保存とノードの起動・停止はSDK側が行います。
48
+
49
+ ```typescript
50
+ initFlaxiaNode({
51
+ orchestratorUrl: 'https://crowd.flaxia.app',
52
+ siteId: 'your-site-id',
53
+ consent: {
54
+ brandName: 'あなたのサービス名',
55
+ position: 'bottom-right',
56
+ onConsentRequired: ({ state, accept, reject }) => {
57
+ // 自前のモーダルを表示し、ユーザーの選択に応じて accept() / reject()
58
+ showMyConsentModal({
59
+ onAccept: accept,
60
+ onReject: reject,
61
+ })
62
+ },
63
+ },
64
+ })
65
+ ```
66
+
67
+ ### コントローラ(設定画面からの変更)
68
+
69
+ `initFlaxiaNode()` は `FlaxiaNodeController` を返します。同一モジュール内で一度だけ
70
+ 状態を保持するため、設定画面から同意の付与・取消をリロードなしで行えます。
71
+
72
+ ```typescript
73
+ const controller = initFlaxiaNode({ /* ... */ })
74
+
75
+ controller.getConsentState() // 'unset' | 'granted' | 'denied'
76
+ controller.grant() // 同意を保存(拒否を消去)
77
+ controller.deny() // 拒否を保存して停止(同意を消去)
78
+ controller.start() // ノード起動
79
+ controller.stop() // ノード停止 + Web Worker 解放
80
+ controller.isRunning() // 稼働中か
81
+ controller.clearConsent() // 同意・拒否・失効時刻をすべて消去
82
+ ```
83
+
84
+ 同意は localStorage に保存され、**30日**で失効します。`grant` と `deny` は排他で、
85
+ 片方を保存するともう片方は消去されます。
86
+
42
87
  ## アーキテクチャ
43
88
 
44
89
  ```
@@ -93,11 +138,12 @@ initFlaxiaNode({
93
138
  ## フロー
94
139
 
95
140
  1. `initFlaxiaNode()` が呼ばれると、localStorage の同意状態を確認
96
- 2. 未同意の場合、Shadow DOM の同意バナーを表示
141
+ 2. 未同意の場合、内蔵の Shadow DOM 同意バナー、または `onConsentRequired` でホストのUIを表示
97
142
  3. 同意後、WebSocket でオーケストレーターに接続(`/crowd/signal`)
98
143
  4. タスクを受信すると WebWorker で実行
99
144
  5. 結果・中間トークンを WebSocket で返送
100
145
  6. 切断時は指数バックオフ付き自動再接続(最大30秒)
146
+ 7. `FlaxiaNodeController` 経由で設定画面から同意の付与・取消が可能
101
147
 
102
148
  ## 開発
103
149
 
@@ -1,4 +1,4 @@
1
- import type { NodeConfig, WorkloadType } from '@flaxia/sdk';
1
+ import type { FlaxiaNodeController, NodeConfig, WorkloadType } from '@flaxia/sdk';
2
2
  export interface TaskMessage {
3
3
  type: 'task';
4
4
  taskId: string;
@@ -6,5 +6,5 @@ export interface TaskMessage {
6
6
  payload: unknown;
7
7
  timeoutMs?: number;
8
8
  }
9
- export declare const initFlaxiaNode: (config: NodeConfig) => void;
9
+ export declare const initFlaxiaNode: (config: NodeConfig) => FlaxiaNodeController;
10
10
  //# sourceMappingURL=SignalingClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SignalingClient.d.ts","sourceRoot":"","sources":["../../src/client/SignalingClient.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK5D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2RD,eAAO,MAAM,cAAc,GAAI,QAAQ,UAAU,SAkChD,CAAC"}
1
+ {"version":3,"file":"SignalingClient.d.ts","sourceRoot":"","sources":["../../src/client/SignalingClient.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAgB,oBAAoB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhG,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2VD,eAAO,MAAM,cAAc,GAAI,QAAQ,UAAU,KAAG,oBAwDnD,CAAC"}
@@ -1,10 +1,20 @@
1
+ import type { ConsentState } from '@flaxia/sdk';
1
2
  declare function safeGet(key: string): string | null;
2
3
  declare function safeSet(key: string, value: string): void;
3
4
  declare function safeRemove(key: string): void;
4
5
  export declare const hasConsent: () => boolean;
6
+ /** Persist consent. Clears any previous denial so the two never co-exist. */
5
7
  export declare const saveConsent: () => void;
6
8
  export declare const hasDenial: () => boolean;
9
+ /** Persist denial. Clears any previous consent/expiry so the two never co-exist. */
7
10
  export declare const saveDenial: () => void;
11
+ /** Forget the consent decision entirely (granted + expiry + denial). */
12
+ export declare const clearConsent: () => void;
13
+ /**
14
+ * Resolve the persisted consent state. A stored grant wins over a stored
15
+ * denial, but the two are kept mutually exclusive by the save helpers.
16
+ */
17
+ export declare const getConsentState: () => ConsentState;
8
18
  export declare const safeLocalStorageGet: typeof safeGet;
9
19
  export declare const safeLocalStorageSet: typeof safeSet;
10
20
  export declare const safeLocalStorageRemove: typeof safeRemove;
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/consent/storage.ts"],"names":[],"mappings":"AAYA,iBAAS,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3C;AAED,iBAAS,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,iBAAS,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMrC;AAED,eAAO,MAAM,UAAU,QAAO,OAgB7B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAG9B,CAAC;AAEF,eAAO,MAAM,SAAS,QAAO,OAE5B,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,IAE7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,gBAAU,CAAC;AAC3C,eAAO,MAAM,mBAAmB,gBAAU,CAAC;AAC3C,eAAO,MAAM,sBAAsB,mBAAa,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,MAajC,CAAC"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/consent/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,iBAAS,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3C;AAED,iBAAS,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMjD;AAED,iBAAS,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMrC;AAED,eAAO,MAAM,UAAU,QAAO,OAgB7B,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,WAAW,QAAO,IAI9B,CAAC;AAEF,eAAO,MAAM,SAAS,QAAO,OAE5B,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,UAAU,QAAO,IAI7B,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,YAAY,QAAO,IAI/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,YAIlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,gBAAU,CAAC;AAC3C,eAAO,MAAM,mBAAmB,gBAAU,CAAC;AAC3C,eAAO,MAAM,sBAAsB,mBAAa,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,MAajC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { initFlaxiaNode } from './client/SignalingClient';
2
2
  export { initFlaxiaNode };
3
+ export type { ConsentControls, ConsentState, FlaxiaNodeController } from '@flaxia/sdk';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -110,10 +110,12 @@ var d = () => {
110
110
  }
111
111
  return !0;
112
112
  }, f = () => {
113
- l(r, "true"), l(i, String(Date.now() + o));
113
+ u(a), l(r, "true"), l(i, String(Date.now() + o));
114
114
  }, p = () => c(a) === "true", m = () => {
115
- l(a, "true");
116
- }, h = c, g = l, _ = () => {
115
+ u(r), u(i), l(a, "true");
116
+ }, h = () => {
117
+ u(r), u(i), u(a);
118
+ }, g = () => d() ? "granted" : p() ? "denied" : "unset", _ = c, v = l, y = u, b = () => {
117
119
  try {
118
120
  if (typeof crypto < "u" && typeof crypto.randomUUID == "function") return crypto.randomUUID();
119
121
  } catch {}
@@ -121,7 +123,7 @@ var d = () => {
121
123
  let t = Math.random() * 16 | 0;
122
124
  return (e === "x" ? t : t & 3 | 8).toString(16);
123
125
  });
124
- }, v = (...e) => console.log("[flaxia-node]", ...e), y = class {
126
+ }, x = (...e) => console.log("[flaxia-node]", ...e), S = class {
125
127
  constructor(e, t = 3e5) {
126
128
  this.worker = null, this.queue = [], this.active = null, this._lastCpuLoad = 0, this.workerUrl = e || "/worker.js", this.defaultTimeoutMs = t, this.initWorker();
127
129
  }
@@ -141,7 +143,7 @@ var d = () => {
141
143
  return;
142
144
  }
143
145
  let c = () => this.execute(e, t, n, r, i, a, o, s);
144
- this.active ? (v(`task queued id=${e} workload=${t} (active task running)`), this.queue.push(c)) : c();
146
+ this.active ? (x(`task queued id=${e} workload=${t} (active task running)`), this.queue.push(c)) : c();
145
147
  });
146
148
  }
147
149
  execute(e, t, n, r, i, a, o, s) {
@@ -151,7 +153,7 @@ var d = () => {
151
153
  return;
152
154
  }
153
155
  let l = r ?? this.defaultTimeoutMs, u = !1, d = performance.now();
154
- v(`task start id=${e} workload=${t} timeoutMs=${l}`);
156
+ x(`task start id=${e} workload=${t} timeoutMs=${l}`);
155
157
  let f = (e) => {
156
158
  if (u) return;
157
159
  u = !0;
@@ -168,10 +170,10 @@ var d = () => {
168
170
  typeof p == "number" && Number.isFinite(p) && (this._lastCpuLoad = Math.min(1, Math.max(0, p)));
169
171
  return;
170
172
  }
171
- a === "done" ? (v(`task done id=${e} workload=${t} durationMs=${Math.round(performance.now() - d)}`), f(() => o(c))) : a === "error" ? (v(`task failed id=${e} workload=${t} error=${l}`), f(() => s(Error(l)))) : (v(`task unknown-message id=${e} type=${String(a)}`), f(() => s(/* @__PURE__ */ Error(`Unknown message type: ${String(a)}`))));
173
+ a === "done" ? (x(`task done id=${e} workload=${t} durationMs=${Math.round(performance.now() - d)}`), f(() => o(c))) : a === "error" ? (x(`task failed id=${e} workload=${t} error=${l}`), f(() => s(Error(l)))) : (x(`task unknown-message id=${e} type=${String(a)}`), f(() => s(/* @__PURE__ */ Error(`Unknown message type: ${String(a)}`))));
172
174
  }
173
175
  }, m = () => c.removeEventListener("message", p), h = setTimeout(() => {
174
- v(`task timeout id=${e} workload=${t} (worker terminated after ${l}ms)`), this.cleanupWorker(), f(() => s(/* @__PURE__ */ Error("TIMEOUT")));
176
+ x(`task timeout id=${e} workload=${t} (worker terminated after ${l}ms)`), this.cleanupWorker(), f(() => s(/* @__PURE__ */ Error("TIMEOUT")));
175
177
  }, l);
176
178
  this.active = {
177
179
  id: e,
@@ -194,7 +196,7 @@ var d = () => {
194
196
  }
195
197
  }
196
198
  handleWorkerError(e) {
197
- v(`worker error error=${e.message}`);
199
+ x(`worker error error=${e.message}`);
198
200
  let t = this.active;
199
201
  this.cleanupWorker(), t && (clearTimeout(t.timer), this.active = null, t.reject(e), this.dequeue());
200
202
  }
@@ -214,13 +216,13 @@ var d = () => {
214
216
  get lastCpuLoad() {
215
217
  return this._lastCpuLoad;
216
218
  }
217
- }, b = (...e) => console.log("[flaxia-node]", ...e), x = (...e) => console.error("[flaxia-node]", ...e), S = "flaxia_node_id", C = "flaxia_node_token", w = class {
219
+ }, C = (...e) => console.log("[flaxia-node]", ...e), w = (...e) => console.error("[flaxia-node]", ...e), T = "flaxia_node_id", E = "flaxia_node_token", D = class {
218
220
  constructor(e, t, n) {
219
221
  this.config = e, this.workerPool = t, this.nodeId = n, this.ws = null, this.reconnectAttempts = 0, this.MAX_RECONNECT_DELAY = 3e4, this.destroyed = !1, this.suspended = !1, this.visibilityHandler = null, this.inflightTasks = /* @__PURE__ */ new Set();
220
222
  }
221
223
  async connect() {
222
224
  if (this.destroyed) return;
223
- if (b(`connect nodeId=${this.nodeId}`), this.ws) {
225
+ if (C(`connect nodeId=${this.nodeId}`), this.ws) {
224
226
  let e = this.ws;
225
227
  e.onclose = null;
226
228
  try {
@@ -231,14 +233,14 @@ var d = () => {
231
233
  this.setupVisibilityHandler();
232
234
  let e = await this.obtainToken();
233
235
  if (!e) {
234
- x("token acquisition failed; scheduling reconnect"), this.scheduleReconnect();
236
+ w("token acquisition failed; scheduling reconnect"), this.scheduleReconnect();
235
237
  return;
236
238
  }
237
239
  let t = this.config.orchestratorUrl.replace(/\/+$/, "").replace(/^http/, "ws"), n = new URL(`${t}/crowd/signal`);
238
240
  n.searchParams.set("token", e.token);
239
241
  let r = new WebSocket(n.toString());
240
242
  this.ws = r, r.onopen = () => {
241
- this.reconnectAttempts = 0, b(`signal connected ${n.toString()}`);
243
+ this.reconnectAttempts = 0, C(`signal connected ${n.toString()}`);
242
244
  }, r.onmessage = async (e) => {
243
245
  let t;
244
246
  try {
@@ -255,12 +257,12 @@ var d = () => {
255
257
  }
256
258
  if (t.type === "task") {
257
259
  let e = t;
258
- b(`task received taskId=${e.taskId} workload=${e.workload} timeoutMs=${e.timeoutMs ?? "default"}`);
260
+ C(`task received taskId=${e.taskId} workload=${e.workload} timeoutMs=${e.timeoutMs ?? "default"}`);
259
261
  try {
260
262
  await this.handleTask(e);
261
263
  } catch (n) {
262
264
  let r = n instanceof Error ? n.message : String(n);
263
- x(`task failed taskId=${e.taskId} workload=${e.workload} error=${r}`), this.send({
265
+ w(`task failed taskId=${e.taskId} workload=${e.workload} error=${r}`), this.send({
264
266
  type: "error",
265
267
  taskId: String(t.taskId ?? ""),
266
268
  error: r
@@ -268,7 +270,7 @@ var d = () => {
268
270
  }
269
271
  }
270
272
  }, r.onclose = () => {
271
- r.onclose = null, this.ws === r && (this.ws = null, !this.destroyed && (b(`signal disconnected; scheduling reconnect attempt=${this.reconnectAttempts + 1}`), this.scheduleReconnect()));
273
+ r.onclose = null, this.ws === r && (this.ws = null, !this.destroyed && (C(`signal disconnected; scheduling reconnect attempt=${this.reconnectAttempts + 1}`), this.scheduleReconnect()));
272
274
  };
273
275
  }
274
276
  scheduleReconnect() {
@@ -278,13 +280,13 @@ var d = () => {
278
280
  }, e);
279
281
  }
280
282
  disconnect() {
281
- b(`disconnect nodeId=${this.nodeId}`), this.destroyed = !0, this.suspended = !1, this.removeVisibilityHandler();
283
+ C(`disconnect nodeId=${this.nodeId}`), this.destroyed = !0, this.suspended = !1, this.removeVisibilityHandler();
282
284
  try {
283
285
  this.ws?.close();
284
286
  } catch {}
285
287
  this.ws = null;
286
288
  try {
287
- delete window[E];
289
+ delete window[k];
288
290
  } catch {}
289
291
  try {
290
292
  this.workerPool.terminate();
@@ -292,7 +294,7 @@ var d = () => {
292
294
  }
293
295
  suspend() {
294
296
  if (!(this.suspended || this.destroyed)) {
295
- if (b("suspended (visibility or manual)"), this.suspended = !0, this.ws) {
297
+ if (C("suspended (visibility or manual)"), this.suspended = !0, this.ws) {
296
298
  let e = this.ws;
297
299
  e.onclose = null;
298
300
  try {
@@ -304,17 +306,17 @@ var d = () => {
304
306
  }
305
307
  }
306
308
  resume() {
307
- !this.suspended || this.destroyed || (b("resumed"), this.suspended = !1, this.workerPool.resume(), this.connect());
309
+ !this.suspended || this.destroyed || (C("resumed"), this.suspended = !1, this.workerPool.resume(), this.connect());
308
310
  }
309
311
  async obtainToken() {
310
312
  try {
311
- let n = h(C);
313
+ let n = _(E);
312
314
  if (n) try {
313
315
  let e = JSON.parse(n);
314
- if (e.token && e.nodeId && e.expiresAt > Date.now() + 6e4) return b(`token cached nodeId=${e.nodeId} expiresIn=${Math.round((e.expiresAt - Date.now()) / 1e3)}s`), e;
316
+ if (e.token && e.nodeId && e.expiresAt > Date.now() + 6e4) return C(`token cached nodeId=${e.nodeId} expiresIn=${Math.round((e.expiresAt - Date.now()) / 1e3)}s`), e;
315
317
  } catch {}
316
318
  let r = e(), i = r >= t, a = i ? this.config.capabilities ?? ["ai-inference", "image-process"] : [];
317
- b(`register capability probe capable=${i} wasmMemoryBytes=${r} capabilities=${JSON.stringify(a)}`);
319
+ C(`register capability probe capable=${i} wasmMemoryBytes=${r} capabilities=${JSON.stringify(a)}`);
318
320
  let o = this.config.orchestratorUrl.replace(/\/+$/, ""), s = await fetch(`${o}/crowd/nodes/register`, {
319
321
  method: "POST",
320
322
  headers: { "Content-Type": "application/json" },
@@ -326,16 +328,16 @@ var d = () => {
326
328
  deviceMemory: typeof navigator.deviceMemory == "number" ? navigator.deviceMemory : null
327
329
  })
328
330
  });
329
- if (!s.ok) return x(`node register failed HTTP ${s.status} (${o}/crowd/nodes/register)`), null;
331
+ if (!s.ok) return w(`node register failed HTTP ${s.status} (${o}/crowd/nodes/register)`), null;
330
332
  let c = await s.json();
331
- if (!c.token) return x("node register returned no token"), null;
332
- b(`node registered nodeId=${this.nodeId} expiresIn=${Math.round((c.expiresAt - Date.now()) / 1e3)}s`);
333
+ if (!c.token) return w("node register returned no token"), null;
334
+ C(`node registered nodeId=${this.nodeId} expiresIn=${Math.round((c.expiresAt - Date.now()) / 1e3)}s`);
333
335
  try {
334
- g(C, JSON.stringify(c));
336
+ v(E, JSON.stringify(c));
335
337
  } catch {}
336
338
  return c;
337
339
  } catch (e) {
338
- return x("node register threw", e instanceof Error ? e.message : e), null;
340
+ return w("node register threw", e instanceof Error ? e.message : e), null;
339
341
  }
340
342
  }
341
343
  send(e) {
@@ -357,7 +359,7 @@ var d = () => {
357
359
  type: "result",
358
360
  taskId: e.taskId,
359
361
  payload: r
360
- }), b(`task result sent taskId=${e.taskId} workload=${e.workload} durationMs=${Math.round(performance.now() - t)}`);
362
+ }), C(`task result sent taskId=${e.taskId} workload=${e.workload} durationMs=${Math.round(performance.now() - t)}`);
361
363
  } finally {
362
364
  this.inflightTasks.delete(e.taskId);
363
365
  }
@@ -370,26 +372,62 @@ var d = () => {
370
372
  removeVisibilityHandler() {
371
373
  this.visibilityHandler &&= (document.removeEventListener("visibilitychange", this.visibilityHandler), null);
372
374
  }
373
- }, T = "__flaxia_node_signal_client", E = "__flaxia_node_init_started", D = (e) => {
374
- let t = window[T];
375
+ }, O = "__flaxia_node_signal_client", k = "__flaxia_node_init_started", A = "__flaxia_node_controller", j = (e) => {
376
+ let t = window[O];
375
377
  t && t.disconnect();
376
- let n = new URL("./worker.js", "" + import.meta.url).href, r = new y(n), i = h(S);
377
- i || (i = _(), g(S, i));
378
- let a = new w(e, r, i);
379
- window[T] = a, a.connect();
380
- }, O = (e) => {
381
- if (window[E]) return;
382
- if (window[E] = !0, d()) {
383
- D(e);
384
- return;
385
- }
386
- if (p()) return;
387
- let t = document.createElement("div");
388
- t.id = "flaxia-consent-container", document.body.appendChild(t), new n(t, e.consent, () => {
389
- f(), t.remove(), D(e);
378
+ let n = new URL("./worker.js", "" + import.meta.url).href, r = new S(n), i = _(T);
379
+ i || (i = b(), v(T, i));
380
+ let a = new D(e, r, i);
381
+ window[O] = a, a.connect();
382
+ }, M = class {
383
+ constructor(e) {
384
+ this.config = e;
385
+ }
386
+ start() {
387
+ this.isRunning() || j(this.config);
388
+ }
389
+ stop() {
390
+ let e = window[O];
391
+ e && e.disconnect(), delete window[O], delete window[k];
392
+ }
393
+ isRunning() {
394
+ return !!window[O];
395
+ }
396
+ getConsentState() {
397
+ return g();
398
+ }
399
+ grant() {
400
+ f();
401
+ }
402
+ deny() {
403
+ m(), this.stop(), y(E);
404
+ }
405
+ clearConsent() {
406
+ h(), this.stop(), y(E);
407
+ }
408
+ }, N = (e) => {
409
+ let t = window[A];
410
+ if (t) return t;
411
+ let r = new M(e);
412
+ window[A] = r, window[k] = !0;
413
+ let i = g();
414
+ if (i === "granted") return r.start(), r;
415
+ if (i === "denied") return r;
416
+ if (e.consent.onConsentRequired) return e.consent.onConsentRequired({
417
+ state: i,
418
+ accept: () => {
419
+ f(), r.start();
420
+ },
421
+ reject: () => {
422
+ m();
423
+ }
424
+ }), r;
425
+ let a = document.createElement("div");
426
+ return a.id = "flaxia-consent-container", document.body.appendChild(a), new n(a, e.consent, () => {
427
+ f(), a.remove(), r.start();
390
428
  }, () => {
391
- m(), t.remove();
392
- });
429
+ m(), a.remove();
430
+ }), r;
393
431
  };
394
432
  //#endregion
395
- export { O as initFlaxiaNode };
433
+ export { N as initFlaxiaNode };
package/dist/worker.js CHANGED
@@ -1,15 +1,25 @@
1
1
  import { n as e, r as t, t as n } from "./assets/memoryProbe.js";
2
+ //#region ../sdk/dist/index.js
3
+ var r = new Set([
4
+ "ai-inference",
5
+ "image-process",
6
+ "container",
7
+ "vector-embed",
8
+ "vector-query",
9
+ "nudenet"
10
+ ]);
11
+ //#endregion
2
12
  //#region src/executor/throttle.ts
3
- function r(e) {
13
+ function i(e) {
4
14
  return new Promise((t) => setTimeout(t, e));
5
15
  }
6
- var i = 3e4, a = 3e4, o = class {
16
+ var a = 3e4, o = 3e4, s = class {
7
17
  constructor(e = .15) {
8
18
  this.windowStart = performance.now(), this.windowBusyMs = 0, this.lastLoad = 0, this.maxLoad = Math.max(.05, Math.min(.3, e));
9
19
  }
10
20
  markTaskComplete(e) {
11
21
  let t = performance.now();
12
- t - this.windowStart > i && (this.windowStart = t, this.windowBusyMs = 0), this.windowBusyMs += Math.max(0, e), this.lastLoad = this.estimate();
22
+ t - this.windowStart > a && (this.windowStart = t, this.windowBusyMs = 0), this.windowBusyMs += Math.max(0, e), this.lastLoad = this.estimate();
13
23
  }
14
24
  estimate() {
15
25
  let e = performance.now() - this.windowStart;
@@ -18,9 +28,9 @@ var i = 3e4, a = 3e4, o = class {
18
28
  async shouldPause() {
19
29
  return this.estimate() > this.maxLoad;
20
30
  }
21
- async waitForSlot(e = a) {
31
+ async waitForSlot(e = o) {
22
32
  let t = performance.now();
23
- for (; await this.shouldPause() && !(performance.now() - t > e);) await r(500);
33
+ for (; await this.shouldPause() && !(performance.now() - t > e);) await i(500);
24
34
  }
25
35
  get lastMeasuredLoad() {
26
36
  return this.lastLoad;
@@ -28,14 +38,7 @@ var i = 3e4, a = 3e4, o = class {
28
38
  get maxLoadValue() {
29
39
  return this.maxLoad;
30
40
  }
31
- }, s = null, c = new Set([
32
- "ai-inference",
33
- "vector-embed",
34
- "vector-query",
35
- "nudenet",
36
- "image-process",
37
- "container"
38
- ]), l = 6e4, u = t(), d = u >= n;
41
+ }, c = null, l = 6e4, u = t(), d = u >= n;
39
42
  console.log(`[flaxia-node:worker] startup memory probe wasmMemoryBytes=${u} capable=${d}`);
40
43
  function f() {
41
44
  return e();
@@ -75,20 +78,20 @@ async function _(e, t, n) {
75
78
  }
76
79
  }
77
80
  self.onmessage = async (e) => {
78
- let { id: t, workload: r, payload: i, config: a } = e.data, l = performance.now();
81
+ let { id: t, workload: i, payload: a, config: o } = e.data, l = performance.now();
79
82
  try {
80
- if (s ||= new o(a?.maxCpuLoad), console.log(`[flaxia-node:worker] task start id=${t} workload=${r}`), !d) throw Error(`node incapable: insufficient WASM memory (${u} bytes < ${n}); rejecting all tasks`);
81
- if (c.has(r) && !f()) throw Error(`${r}: insufficient WASM memory (${u} bytes < ${n})`);
82
- await s.waitForSlot();
83
+ if (c ||= new s(o?.maxCpuLoad), console.log(`[flaxia-node:worker] task start id=${t} workload=${i}`), !d) throw Error(`node incapable: insufficient WASM memory (${u} bytes < ${n}); rejecting all tasks`);
84
+ if (r.has(i) && !f()) throw Error(`${i}: insufficient WASM memory (${u} bytes < ${n})`);
85
+ await c.waitForSlot();
83
86
  let e = setInterval(() => {
84
87
  self.postMessage({
85
88
  id: t,
86
89
  type: "heartbeat",
87
- cpuLoad: s?.lastMeasuredLoad ?? 0
90
+ cpuLoad: c?.lastMeasuredLoad ?? 0
88
91
  });
89
92
  }, 1e4), p = performance.now();
90
93
  try {
91
- let e = await _(r, i, (e) => {
94
+ let e = await _(i, a, (e) => {
92
95
  self.postMessage({
93
96
  id: t,
94
97
  type: "token",
@@ -99,13 +102,13 @@ self.onmessage = async (e) => {
99
102
  id: t,
100
103
  type: "done",
101
104
  result: e
102
- }), console.log(`[flaxia-node:worker] task done id=${t} workload=${r} durationMs=${Math.round(performance.now() - l)}`);
105
+ }), console.log(`[flaxia-node:worker] task done id=${t} workload=${i} durationMs=${Math.round(performance.now() - l)}`);
103
106
  } finally {
104
- clearInterval(e), s.markTaskComplete(performance.now() - p), g();
107
+ clearInterval(e), c.markTaskComplete(performance.now() - p), g();
105
108
  }
106
109
  } catch (e) {
107
110
  let n = e instanceof Error ? e.message : String(e);
108
- console.error(`[flaxia-node:worker] task error id=${t} workload=${r} error=${n}`), self.postMessage({
111
+ console.error(`[flaxia-node:worker] task error id=${t} workload=${i} error=${n}`), self.postMessage({
109
112
  id: t,
110
113
  type: "error",
111
114
  error: n
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flaxia/node",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@bjorn3/browser_wasi_shim": "^0.4.2",
24
- "@flaxia/sdk": "^0.3.0",
24
+ "@flaxia/sdk": "^0.4.0",
25
25
  "@huggingface/transformers": "^4.2.0",
26
26
  "onnxruntime-web": "1.26.0",
27
27
  "rollup": "^4.61.0"