@blocklet/sdk 1.17.2-beta-20251112-085154-0103b877 → 1.17.2-beta-20251113-121338-9c917e68

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.
@@ -57,7 +57,12 @@ const debug = (0, debug_1.default)('@blocklet/sdk:notification');
57
57
  let client = null;
58
58
  let connectionToken = null;
59
59
  let connectionTokenTimer = null;
60
- const refreshConnectionToken = async () => {
60
+ let initPromise = null; // 全局 Promise,确保多次调用时等待同一个初始化过程
61
+ const refreshConnectionToken = async (force = false) => {
62
+ // don't refresh token if client is closed
63
+ if (!force && connectionToken === null && connectionTokenTimer === null) {
64
+ return;
65
+ }
61
66
  const accessWallet = (0, wallet_1.getAccessWallet)();
62
67
  connectionToken = await accessWallet.signJWT({});
63
68
  // refresh token in 12 hours
@@ -150,121 +155,138 @@ const joinChannelErrorHandler = (name, type, emitters) => async (err) => {
150
155
  (emitters || [emitter]).forEach((x) => x.emit('error', { message: msg }));
151
156
  await debouncedRefreshConnectionToken();
152
157
  };
153
- const initClient = async () => {
154
- if (!client) {
155
- ensureErrorListener();
156
- const accessWallet = (0, wallet_1.getAccessWallet)();
157
- const componentDid = process.env.BLOCKLET_COMPONENT_DID;
158
- const appDid = process.env.BLOCKLET_APP_PID;
159
- const { publicKey: pk } = accessWallet;
160
- await refreshConnectionToken();
161
- // Build URL with query parameters directly
162
- const baseUrl = `ws://${(0, parse_docker_endpoint_1.getServerHost)()}:${process.env.ABT_NODE_SERVICE_PORT}${constants_1.SERVICE_PREFIX}/websocket`;
163
- const url = `${baseUrl}?token=${encodeURIComponent(connectionToken)}&pk=${encodeURIComponent(pk)}`;
164
- client = new ws_1.WsClient(url, {
165
- heartbeatIntervalMs: 10 * 1000,
166
- });
167
- client.connect();
168
- const messageChannel = client.channel(accessWallet.address, () => ({ token: connectionToken, pk }));
169
- const appPublicChannel = client.channel((0, channel_1.getAppPublicChannel)(appDid), () => ({ token: connectionToken, pk }));
170
- const componentChannel = client.channel((0, channel_1.getComponentChannel)(appDid, componentDid), () => ({
171
- token: connectionToken,
172
- pk,
173
- apiKey: process.env.BLOCKLET_COMPONENT_API_KEY,
174
- }));
175
- const eventBusChannel = client.channel((0, channel_1.getEventBusChannel)(appDid), () => ({ token: connectionToken, pk }));
176
- messageChannel
177
- .join()
178
- .receive('error', joinChannelErrorHandler('message channel', 'error', [messageEmitter, emitter]))
179
- .receive('timeout', joinChannelErrorHandler('message channel', 'timeout', [messageEmitter, emitter]));
180
- appPublicChannel
181
- .join()
182
- .receive('error', joinChannelErrorHandler('app public channel', 'error'))
183
- .receive('timeout', joinChannelErrorHandler('app public channel', 'timeout'));
184
- componentChannel
185
- .join()
186
- .receive('error', joinChannelErrorHandler('app component channel', 'error'))
187
- .receive('timeout', joinChannelErrorHandler('app component channel', 'timeout'));
188
- eventBusChannel
189
- .join()
190
- .receive('error', joinChannelErrorHandler('eventbus channel', 'error'))
191
- .receive('timeout', joinChannelErrorHandler('eventbus channel', 'timeout'));
192
- messageChannel.on('message', ({ status, response } = {}) => {
193
- debug('messageChannel.on', { status, response });
194
- if (status === 'ok') {
195
- messageEmitter.emit(response.type, response);
196
- if (response.type === notification_1.NOTIFICATION_TYPES.HI) {
197
- emitter.emit(response.type, response);
158
+ const initClient = () => {
159
+ // 如果正在初始化,返回同一个 Promise 等待完成
160
+ if (initPromise) {
161
+ return initPromise;
162
+ }
163
+ // 如果已经初始化完成,直接返回
164
+ if (client) {
165
+ return Promise.resolve();
166
+ }
167
+ // 创建新的初始化 Promise
168
+ initPromise = (async () => {
169
+ try {
170
+ ensureErrorListener();
171
+ const accessWallet = (0, wallet_1.getAccessWallet)();
172
+ const componentDid = process.env.BLOCKLET_COMPONENT_DID;
173
+ const appDid = process.env.BLOCKLET_APP_PID;
174
+ const { publicKey: pk } = accessWallet;
175
+ await refreshConnectionToken(true);
176
+ // Build URL with query parameters directly
177
+ const baseUrl = `ws://${(0, parse_docker_endpoint_1.getServerHost)()}:${process.env.ABT_NODE_SERVICE_PORT}${constants_1.SERVICE_PREFIX}/websocket`;
178
+ const url = `${baseUrl}?token=${encodeURIComponent(connectionToken)}&pk=${encodeURIComponent(pk)}`;
179
+ client = new ws_1.WsClient(url, {
180
+ heartbeatIntervalMs: 10 * 1000,
181
+ });
182
+ client.connect();
183
+ const messageChannel = client.channel(accessWallet.address, () => ({ token: connectionToken, pk }));
184
+ const appPublicChannel = client.channel((0, channel_1.getAppPublicChannel)(appDid), () => ({ token: connectionToken, pk }));
185
+ const componentChannel = client.channel((0, channel_1.getComponentChannel)(appDid, componentDid), () => ({
186
+ token: connectionToken,
187
+ pk,
188
+ apiKey: process.env.BLOCKLET_COMPONENT_API_KEY,
189
+ }));
190
+ const eventBusChannel = client.channel((0, channel_1.getEventBusChannel)(appDid), () => ({ token: connectionToken, pk }));
191
+ messageChannel
192
+ .join()
193
+ .receive('error', joinChannelErrorHandler('message channel', 'error', [messageEmitter, emitter]))
194
+ .receive('timeout', joinChannelErrorHandler('message channel', 'timeout', [messageEmitter, emitter]));
195
+ appPublicChannel
196
+ .join()
197
+ .receive('error', joinChannelErrorHandler('app public channel', 'error'))
198
+ .receive('timeout', joinChannelErrorHandler('app public channel', 'timeout'));
199
+ componentChannel
200
+ .join()
201
+ .receive('error', joinChannelErrorHandler('app component channel', 'error'))
202
+ .receive('timeout', joinChannelErrorHandler('app component channel', 'timeout'));
203
+ eventBusChannel
204
+ .join()
205
+ .receive('error', joinChannelErrorHandler('eventbus channel', 'error'))
206
+ .receive('timeout', joinChannelErrorHandler('eventbus channel', 'timeout'));
207
+ messageChannel.on('message', ({ status, response } = {}) => {
208
+ debug('messageChannel.on', { status, response });
209
+ if (status === 'ok') {
210
+ messageEmitter.emit(response.type, response);
211
+ if (response.type === notification_1.NOTIFICATION_TYPES.HI) {
212
+ emitter.emit(response.type, response);
213
+ }
198
214
  }
199
- }
200
- else {
201
- emitError(response);
202
- console.error('Message channel error', { status, response });
203
- }
204
- });
205
- eventBusChannel.on('event', ({ status, response } = {}) => {
206
- debug('eventBusChannel.on', { status, response });
207
- if (status === 'ok') {
208
- // ignore events from self
209
- if (response.source !== process.env.BLOCKLET_COMPONENT_DID) {
210
- exports._eventBus.emit('event', response);
215
+ else {
216
+ emitError(response);
217
+ console.error('Message channel error', { status, response });
211
218
  }
212
- }
213
- else {
214
- exports._eventBus.emit('error', response);
215
- console.error('Event channel error', { status, response });
216
- }
217
- });
218
- [...Object.keys(constant_1.BlockletInternalEvents), ...Object.keys(constant_1.TeamEvents)].forEach((key) => {
219
- const event = constant_1.BlockletInternalEvents[key] || constant_1.TeamEvents[key];
220
- componentChannel.on(event, async ({ status, response } = {}) => {
221
- debug('componentChannel.on', { event, status, response });
219
+ });
220
+ eventBusChannel.on('event', ({ status, response } = {}) => {
221
+ debug('eventBusChannel.on', { status, response });
222
222
  if (status === 'ok') {
223
- const { data, sender, time } = response;
224
- if (!time || new Date(time).getTime() < new Date(process.env.BLOCKLET_START_AT).getTime()) {
225
- return;
223
+ // ignore events from self
224
+ if (response.source !== process.env.BLOCKLET_COMPONENT_DID) {
225
+ exports._eventBus.emit('event', response);
226
226
  }
227
- // verify sender is server
228
- const tolerance = 600;
229
- if (!(await Jwt.verify(sender.token, process.env.ABT_NODE_PK, { tolerance }))) {
230
- const message = `verify sender failed in internal events. event: ${event}, sender: ${JSON.stringify({
231
- sender,
232
- decode: Jwt.decode(sender.token),
233
- now: Date.now(),
234
- ABT_NODE_PK: process.env.ABT_NODE_PK,
235
- })}`;
236
- emitError({ message });
237
- console.error(message);
238
- return;
227
+ }
228
+ else {
229
+ exports._eventBus.emit('error', response);
230
+ console.error('Event channel error', { status, response });
231
+ }
232
+ });
233
+ [...Object.keys(constant_1.BlockletInternalEvents), ...Object.keys(constant_1.TeamEvents)].forEach((key) => {
234
+ const event = constant_1.BlockletInternalEvents[key] || constant_1.TeamEvents[key];
235
+ componentChannel.on(event, async ({ status, response } = {}) => {
236
+ debug('componentChannel.on', { event, status, response });
237
+ if (status === 'ok') {
238
+ const { data, sender, time } = response;
239
+ if (!time || new Date(time).getTime() < new Date(process.env.BLOCKLET_START_AT).getTime()) {
240
+ return;
241
+ }
242
+ // verify sender is server
243
+ const tolerance = 600;
244
+ if (!(await Jwt.verify(sender.token, process.env.ABT_NODE_PK, { tolerance }))) {
245
+ const message = `verify sender failed in internal events. event: ${event}, sender: ${JSON.stringify({
246
+ sender,
247
+ decode: Jwt.decode(sender.token),
248
+ now: Date.now(),
249
+ ABT_NODE_PK: process.env.ABT_NODE_PK,
250
+ })}`;
251
+ emitError({ message });
252
+ console.error(message);
253
+ return;
254
+ }
255
+ emitter.emit(event, data);
256
+ // Emit team events to event bus
257
+ if (constant_1.TeamEvents[key]) {
258
+ exports._eventBus.emit('event', {
259
+ id: (0, util_1.nanoid)(),
260
+ time: new Date().toISOString(),
261
+ type: `blocklet.${event}`,
262
+ data: { object: data.user },
263
+ });
264
+ }
239
265
  }
240
- emitter.emit(event, data);
241
- // Emit team events to event bus
242
- if (constant_1.TeamEvents[key]) {
243
- exports._eventBus.emit('event', {
244
- id: (0, util_1.nanoid)(),
245
- time: new Date().toISOString(),
246
- type: `blocklet.${event}`,
247
- data: { object: data.user },
248
- });
266
+ else {
267
+ emitError(response);
268
+ console.error('Component channel error', { status, response });
249
269
  }
270
+ });
271
+ });
272
+ appPublicChannel.on(notification_1.NOTIFICATION_TYPES.HI, ({ status, response } = {}) => {
273
+ debug('appPublicChannel.on', { event: notification_1.NOTIFICATION_TYPES.HI, status, response });
274
+ if (status === 'ok') {
275
+ emitter.emit(notification_1.NOTIFICATION_TYPES.HI, response);
250
276
  }
251
277
  else {
252
- emitError(response);
253
- console.error('Component channel error', { status, response });
278
+ emitter.emit('error', response);
279
+ console.error('App public channel error', { status, response });
254
280
  }
255
281
  });
256
- });
257
- appPublicChannel.on(notification_1.NOTIFICATION_TYPES.HI, ({ status, response } = {}) => {
258
- debug('appPublicChannel.on', { event: notification_1.NOTIFICATION_TYPES.HI, status, response });
259
- if (status === 'ok') {
260
- emitter.emit(notification_1.NOTIFICATION_TYPES.HI, response);
261
- }
262
- else {
263
- emitter.emit('error', response);
264
- console.error('App public channel error', { status, response });
265
- }
266
- });
267
- }
282
+ }
283
+ catch (err) {
284
+ initPromise = null; // 清除 Promise 以便重试
285
+ console.warn('Failed to init notification service', err);
286
+ throw err;
287
+ }
288
+ })();
289
+ return initPromise;
268
290
  };
269
291
  const cleanup = () => {
270
292
  try {
@@ -277,6 +299,7 @@ const cleanup = () => {
277
299
  client = null;
278
300
  }
279
301
  connectionToken = null;
302
+ initPromise = null;
280
303
  }
281
304
  catch (err) {
282
305
  console.warn('Failed to cleanup notification service', err);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.17.2-beta-20251112-085154-0103b877",
6
+ "version": "1.17.2-beta-20251113-121338-9c917e68",
7
7
  "description": "graphql client to read/write data on abt node",
8
8
  "homepage": "https://www.arcblock.io/docs/blocklet-sdk-nodejs",
9
9
  "main": "lib/index.js",
@@ -26,19 +26,19 @@
26
26
  "author": "linchen1987 <linchen.1987@foxmail.com> (http://github.com/linchen1987)",
27
27
  "license": "Apache-2.0",
28
28
  "dependencies": {
29
- "@abtnode/constant": "1.17.2-beta-20251112-085154-0103b877",
30
- "@abtnode/db-cache": "1.17.2-beta-20251112-085154-0103b877",
31
- "@abtnode/util": "1.17.2-beta-20251112-085154-0103b877",
29
+ "@abtnode/constant": "1.17.2-beta-20251113-121338-9c917e68",
30
+ "@abtnode/db-cache": "1.17.2-beta-20251113-121338-9c917e68",
31
+ "@abtnode/util": "1.17.2-beta-20251113-121338-9c917e68",
32
32
  "@arcblock/did": "^1.27.4",
33
33
  "@arcblock/did-connect-js": "^1.27.4",
34
34
  "@arcblock/did-ext": "^1.27.4",
35
35
  "@arcblock/jwt": "^1.27.4",
36
36
  "@arcblock/ws": "^1.27.4",
37
- "@blocklet/constant": "1.17.2-beta-20251112-085154-0103b877",
38
- "@blocklet/env": "1.17.2-beta-20251112-085154-0103b877",
37
+ "@blocklet/constant": "1.17.2-beta-20251113-121338-9c917e68",
38
+ "@blocklet/env": "1.17.2-beta-20251113-121338-9c917e68",
39
39
  "@blocklet/error": "^0.3.2",
40
- "@blocklet/meta": "1.17.2-beta-20251112-085154-0103b877",
41
- "@blocklet/server-js": "1.17.2-beta-20251112-085154-0103b877",
40
+ "@blocklet/meta": "1.17.2-beta-20251113-121338-9c917e68",
41
+ "@blocklet/server-js": "1.17.2-beta-20251113-121338-9c917e68",
42
42
  "@blocklet/theme": "^3.2.3",
43
43
  "@did-connect/authenticator": "^2.2.8",
44
44
  "@did-connect/handler": "^2.2.8",
@@ -82,5 +82,5 @@
82
82
  "ts-node": "^10.9.1",
83
83
  "typescript": "^5.6.3"
84
84
  },
85
- "gitHead": "78509a648488c741e93d33b743ea2f6d65535c34"
85
+ "gitHead": "82362ffba5d50f01774b257b9cd8143adeb7a898"
86
86
  }