@flaxia/node 0.3.5 → 0.3.7
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 +63 -5
- package/dist/__tests__/public-api.test.d.ts +2 -0
- package/dist/__tests__/public-api.test.d.ts.map +1 -0
- package/dist/client/SignalingClient.d.ts +2 -2
- package/dist/client/SignalingClient.d.ts.map +1 -1
- package/dist/consent/storage.d.ts +10 -0
- package/dist/consent/storage.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +86 -48
- package/dist/worker.js +25 -22
- package/package.json +2 -2
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,68 @@ initFlaxiaNode({
|
|
|
34
34
|
| `orchestratorUrl` | `string` | yes | オーケストレーターのURL |
|
|
35
35
|
| `siteId` | `string` | yes | サイト固有の識別子 |
|
|
36
36
|
| `consent` | `ConsentConfig` | yes | 同意UIの設定 |
|
|
37
|
-
| `consent.brandName` | `string` | yes |
|
|
38
|
-
| `consent.position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | yes |
|
|
39
|
-
| `consent.accentColor` | `string` | no |
|
|
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
|
+
|
|
87
|
+
### 保存状態の読み取り(ノード初期化前)
|
|
88
|
+
|
|
89
|
+
`getFlaxiaNodeConsentState()` はコントローラを作らず、ネットワークにも触れず、
|
|
90
|
+
保存済みの同意状態だけを同期的に返します。設定画面をノード初期化前に描画する
|
|
91
|
+
ホストはこれを使えます。
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { getFlaxiaNodeConsentState } from '@flaxia/node'
|
|
95
|
+
|
|
96
|
+
getFlaxiaNodeConsentState() // 'unset' | 'granted' | 'denied'
|
|
97
|
+
```
|
|
98
|
+
|
|
42
99
|
## アーキテクチャ
|
|
43
100
|
|
|
44
101
|
```
|
|
@@ -93,11 +150,12 @@ initFlaxiaNode({
|
|
|
93
150
|
## フロー
|
|
94
151
|
|
|
95
152
|
1. `initFlaxiaNode()` が呼ばれると、localStorage の同意状態を確認
|
|
96
|
-
2.
|
|
153
|
+
2. 未同意の場合、内蔵の Shadow DOM 同意バナー、または `onConsentRequired` でホストのUIを表示
|
|
97
154
|
3. 同意後、WebSocket でオーケストレーターに接続(`/crowd/signal`)
|
|
98
155
|
4. タスクを受信すると WebWorker で実行
|
|
99
156
|
5. 結果・中間トークンを WebSocket で返送
|
|
100
157
|
6. 切断時は指数バックオフ付き自動再接続(最大30秒)
|
|
158
|
+
7. `FlaxiaNodeController` 経由で設定画面から同意の付与・取消が可能
|
|
101
159
|
|
|
102
160
|
## 開発
|
|
103
161
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/public-api.test.ts"],"names":[],"mappings":""}
|
|
@@ -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) =>
|
|
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":"
|
|
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":"
|
|
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,5 @@
|
|
|
1
1
|
import { initFlaxiaNode } from './client/SignalingClient';
|
|
2
|
+
export { getConsentState as getFlaxiaNodeConsentState } from './consent/storage';
|
|
2
3
|
export { initFlaxiaNode };
|
|
4
|
+
export type { ConsentControls, ConsentState, FlaxiaNodeController } from '@flaxia/sdk';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAE,eAAe,IAAI,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACjF,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 =
|
|
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
|
-
},
|
|
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 ? (
|
|
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
|
-
|
|
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" ? (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
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 (
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 && (
|
|
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
|
-
|
|
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[
|
|
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 (
|
|
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 || (
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
332
|
-
|
|
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
|
-
|
|
336
|
+
v(E, JSON.stringify(c));
|
|
335
337
|
} catch {}
|
|
336
338
|
return c;
|
|
337
339
|
} catch (e) {
|
|
338
|
-
return
|
|
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
|
-
}),
|
|
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
|
-
},
|
|
374
|
-
let t = window[
|
|
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
|
|
377
|
-
i || (i =
|
|
378
|
-
let a = new
|
|
379
|
-
window[
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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(),
|
|
392
|
-
});
|
|
429
|
+
m(), a.remove();
|
|
430
|
+
}), r;
|
|
393
431
|
};
|
|
394
432
|
//#endregion
|
|
395
|
-
export {
|
|
433
|
+
export { g as getFlaxiaNodeConsentState, 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
|
|
13
|
+
function i(e) {
|
|
4
14
|
return new Promise((t) => setTimeout(t, e));
|
|
5
15
|
}
|
|
6
|
-
var
|
|
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 >
|
|
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 =
|
|
31
|
+
async waitForSlot(e = o) {
|
|
22
32
|
let t = performance.now();
|
|
23
|
-
for (; await this.shouldPause() && !(performance.now() - t > e);) await
|
|
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
|
-
},
|
|
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:
|
|
81
|
+
let { id: t, workload: i, payload: a, config: o } = e.data, l = performance.now();
|
|
79
82
|
try {
|
|
80
|
-
if (
|
|
81
|
-
if (
|
|
82
|
-
await
|
|
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:
|
|
90
|
+
cpuLoad: c?.lastMeasuredLoad ?? 0
|
|
88
91
|
});
|
|
89
92
|
}, 1e4), p = performance.now();
|
|
90
93
|
try {
|
|
91
|
-
let e = await _(
|
|
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=${
|
|
105
|
+
}), console.log(`[flaxia-node:worker] task done id=${t} workload=${i} durationMs=${Math.round(performance.now() - l)}`);
|
|
103
106
|
} finally {
|
|
104
|
-
clearInterval(e),
|
|
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=${
|
|
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.
|
|
3
|
+
"version": "0.3.7",
|
|
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.
|
|
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"
|