@schema-element-editor/host-sdk 2.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -28,11 +28,12 @@ module.exports = __toCommonJS(index_exports);
28
28
  // src/react.ts
29
29
  var import_react = require("react");
30
30
 
31
- // src/core.ts
31
+ // src/constants.ts
32
32
  var DEFAULT_SOURCE_CONFIG = {
33
33
  contentSource: "schema-element-editor-content",
34
34
  hostSource: "schema-element-editor-host"
35
35
  };
36
+ var SDK_COORDINATOR_SOURCE = "schema-element-editor-sdk-coordinator";
36
37
  var DEFAULT_MESSAGE_TYPES = {
37
38
  getSchema: "GET_SCHEMA",
38
39
  updateSchema: "UPDATE_SCHEMA",
@@ -44,8 +45,245 @@ var DEFAULT_MESSAGE_TYPES = {
44
45
  stopRecording: "STOP_RECORDING",
45
46
  schemaPush: "SCHEMA_PUSH"
46
47
  };
48
+ var SDK_COORDINATION_MESSAGE_TYPES = {
49
+ register: "SDK_REGISTER",
50
+ unregister: "SDK_UNREGISTER",
51
+ query: "SDK_QUERY"
52
+ };
53
+
54
+ // src/coordinator.ts
55
+ function generateSdkId() {
56
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
57
+ return crypto.randomUUID();
58
+ }
59
+ return `sdk-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
60
+ }
61
+ var SdkCoordinator = class {
62
+ constructor(config) {
63
+ /**
64
+ * SDK 销毁标志
65
+ *
66
+ * 作用:防止在 cleanup() 执行期间,监听器移除前仍然响应请求
67
+ *
68
+ * 场景:cleanup() 中先调用 destroy()(设置 isDestroyed = true),
69
+ * 然后才移除 handleMessage 监听器。在这个微小的时间窗口内,
70
+ * 如果插件请求到达,没有 isDestroyed 检查会导致已销毁的 SDK 仍然响应。
71
+ */
72
+ this.isDestroyed = false;
73
+ /**
74
+ * 存储比当前 SDK 优先级更高的其他 SDK ID
75
+ * 按方法分类:如果某个方法的集合不为空,说明有更高优先级的 SDK 应该处理该方法
76
+ *
77
+ * 优化:只为当前 SDK 实现了的方法维护优先级集合
78
+ */
79
+ this.higherLevelSDKs = {};
80
+ /**
81
+ * 存储与当前 SDK 优先级相同的其他 SDK ID
82
+ * 按方法分类:用于判断是否需要在执行失败时跳过响应
83
+ *
84
+ * 当有同级竞争者时,SDK在执行失败/无数据时会跳过响应,让其他SDK有机会响应
85
+ */
86
+ this.sameLevelSDKs = {};
87
+ /**
88
+ * 处理 SDK 协调消息
89
+ */
90
+ this.handleCoordinationMessage = (event) => {
91
+ const isFromSelf = event.source === window;
92
+ const isFromParent = window !== window.top && event.source === window.parent;
93
+ if (!isFromSelf && !isFromParent) return;
94
+ const data = event.data;
95
+ if (!data || data.source !== SDK_COORDINATOR_SOURCE) return;
96
+ switch (data.type) {
97
+ case SDK_COORDINATION_MESSAGE_TYPES.query:
98
+ this.sendRegister();
99
+ break;
100
+ case SDK_COORDINATION_MESSAGE_TYPES.register:
101
+ this.handleSdkRegister(data.payload);
102
+ break;
103
+ case SDK_COORDINATION_MESSAGE_TYPES.unregister:
104
+ this.handleSdkUnregister(data.payload.sdkId);
105
+ break;
106
+ }
107
+ };
108
+ this.sdkId = config.sdkId || generateSdkId();
109
+ this.messageSource = config.messageSource;
110
+ this.level = config.level ?? 0;
111
+ this.methodLevels = config.methodLevels ?? {};
112
+ this.implementedMethods = config.implementedMethods;
113
+ this.implementedMethods.forEach((method) => {
114
+ this.higherLevelSDKs[method] = /* @__PURE__ */ new Set();
115
+ this.sameLevelSDKs[method] = /* @__PURE__ */ new Set();
116
+ });
117
+ }
118
+ /**
119
+ * 初始化协调器
120
+ */
121
+ init() {
122
+ window.addEventListener("message", this.handleCoordinationMessage);
123
+ this.sendQuery();
124
+ this.sendRegister();
125
+ }
126
+ /**
127
+ * 销毁协调器
128
+ */
129
+ destroy() {
130
+ this.isDestroyed = true;
131
+ this.sendUnregister();
132
+ window.removeEventListener("message", this.handleCoordinationMessage);
133
+ Object.values(this.higherLevelSDKs).forEach((set) => set.clear());
134
+ Object.values(this.sameLevelSDKs).forEach((set) => set.clear());
135
+ }
136
+ /**
137
+ * 判断是否应该响应某个方法的请求
138
+ * @param method - 方法名
139
+ * @returns 是否应该响应
140
+ */
141
+ shouldRespond(method) {
142
+ if (this.isDestroyed) return false;
143
+ if (!this.implementedMethods.includes(method)) {
144
+ return false;
145
+ }
146
+ const higherSDKs = this.higherLevelSDKs[method];
147
+ return !higherSDKs || higherSDKs.size === 0;
148
+ }
149
+ /**
150
+ * 判断某个方法是否有相同优先级的竞争者
151
+ * @param method - 方法名
152
+ * @returns 是否存在同级竞争者
153
+ */
154
+ hasSameLevelCompetitors(method) {
155
+ const sameSDKs = this.sameLevelSDKs[method];
156
+ return sameSDKs ? sameSDKs.size > 0 : false;
157
+ }
158
+ /**
159
+ * 获取方法的优先级
160
+ */
161
+ getMethodLevel(method) {
162
+ return this.methodLevels[method] ?? this.level;
163
+ }
164
+ /**
165
+ * 处理其他 SDK 的注册
166
+ */
167
+ handleSdkRegister(info) {
168
+ if (info.sdkId === this.sdkId) return;
169
+ if (info.messageSource !== this.messageSource) return;
170
+ this.implementedMethods.forEach((method) => {
171
+ if (!info.implementedMethods.includes(method)) {
172
+ return;
173
+ }
174
+ const myLevel = this.getMethodLevel(method);
175
+ const otherLevel = info.methodLevels[method] ?? info.level;
176
+ if (otherLevel > myLevel) {
177
+ this.higherLevelSDKs[method].add(info.sdkId);
178
+ this.sameLevelSDKs[method].delete(info.sdkId);
179
+ } else if (otherLevel === myLevel) {
180
+ this.sameLevelSDKs[method].add(info.sdkId);
181
+ this.higherLevelSDKs[method].delete(info.sdkId);
182
+ } else {
183
+ this.higherLevelSDKs[method].delete(info.sdkId);
184
+ this.sameLevelSDKs[method].delete(info.sdkId);
185
+ }
186
+ });
187
+ }
188
+ /**
189
+ * 处理其他 SDK 的注销
190
+ */
191
+ handleSdkUnregister(sdkId) {
192
+ Object.values(this.higherLevelSDKs).forEach((set) => {
193
+ set.delete(sdkId);
194
+ });
195
+ Object.values(this.sameLevelSDKs).forEach((set) => {
196
+ set.delete(sdkId);
197
+ });
198
+ }
199
+ /**
200
+ * 发送查询消息
201
+ */
202
+ sendQuery() {
203
+ const message = {
204
+ source: SDK_COORDINATOR_SOURCE,
205
+ type: SDK_COORDINATION_MESSAGE_TYPES.query,
206
+ payload: { sdkId: this.sdkId }
207
+ };
208
+ this.postCoordinationMessage(message);
209
+ }
210
+ /**
211
+ * 发送注册消息
212
+ */
213
+ sendRegister() {
214
+ const message = {
215
+ source: SDK_COORDINATOR_SOURCE,
216
+ type: SDK_COORDINATION_MESSAGE_TYPES.register,
217
+ payload: {
218
+ sdkId: this.sdkId,
219
+ messageSource: this.messageSource,
220
+ level: this.level,
221
+ methodLevels: this.methodLevels,
222
+ implementedMethods: this.implementedMethods
223
+ }
224
+ };
225
+ this.postCoordinationMessage(message);
226
+ }
227
+ /**
228
+ * 发送注销消息
229
+ */
230
+ sendUnregister() {
231
+ const message = {
232
+ source: SDK_COORDINATOR_SOURCE,
233
+ type: SDK_COORDINATION_MESSAGE_TYPES.unregister,
234
+ payload: { sdkId: this.sdkId }
235
+ };
236
+ this.postCoordinationMessage(message);
237
+ }
238
+ /**
239
+ * 发送协调消息
240
+ */
241
+ postCoordinationMessage(message) {
242
+ window.postMessage(message, "*");
243
+ if (window.top && window.top !== window) {
244
+ window.top.postMessage(message, "*");
245
+ }
246
+ }
247
+ };
248
+
249
+ // src/bridge.ts
250
+ var METHOD_NAMES = {
251
+ GET_SCHEMA: "getSchema",
252
+ UPDATE_SCHEMA: "updateSchema",
253
+ CHECK_PREVIEW: "checkPreview",
254
+ RENDER_PREVIEW: "renderPreview",
255
+ CLEANUP_PREVIEW: "cleanupPreview",
256
+ START_RECORDING: "startRecording",
257
+ STOP_RECORDING: "stopRecording"
258
+ };
259
+ function shouldSkipFailedResponse(method, result) {
260
+ switch (method) {
261
+ case METHOD_NAMES.GET_SCHEMA:
262
+ return result.success === true && result.data === void 0;
263
+ case METHOD_NAMES.UPDATE_SCHEMA:
264
+ return result.success === false;
265
+ case METHOD_NAMES.RENDER_PREVIEW:
266
+ case METHOD_NAMES.CLEANUP_PREVIEW:
267
+ return result.success === false;
268
+ case METHOD_NAMES.CHECK_PREVIEW:
269
+ case METHOD_NAMES.START_RECORDING:
270
+ case METHOD_NAMES.STOP_RECORDING:
271
+ return false;
272
+ default:
273
+ return false;
274
+ }
275
+ }
47
276
  function createSchemaElementEditorBridge(config) {
48
- const { getSchema, updateSchema, renderPreview, sourceConfig, messageTypes } = config;
277
+ const {
278
+ getSchema,
279
+ updateSchema,
280
+ renderPreview,
281
+ sourceConfig,
282
+ messageTypes,
283
+ sdkId,
284
+ level,
285
+ methodLevels
286
+ } = config;
49
287
  const mergedSourceConfig = {
50
288
  ...DEFAULT_SOURCE_CONFIG,
51
289
  ...sourceConfig
@@ -54,9 +292,40 @@ function createSchemaElementEditorBridge(config) {
54
292
  ...DEFAULT_MESSAGE_TYPES,
55
293
  ...messageTypes
56
294
  };
295
+ const implementedMethods = [];
296
+ if (typeof getSchema === "function") implementedMethods.push(METHOD_NAMES.GET_SCHEMA);
297
+ if (typeof updateSchema === "function") implementedMethods.push(METHOD_NAMES.UPDATE_SCHEMA);
298
+ if (typeof renderPreview === "function") {
299
+ implementedMethods.push(
300
+ METHOD_NAMES.CHECK_PREVIEW,
301
+ METHOD_NAMES.RENDER_PREVIEW,
302
+ METHOD_NAMES.CLEANUP_PREVIEW
303
+ );
304
+ }
305
+ implementedMethods.push(METHOD_NAMES.START_RECORDING, METHOD_NAMES.STOP_RECORDING);
306
+ const coordinator = new SdkCoordinator({
307
+ sdkId,
308
+ messageSource: mergedSourceConfig.contentSource,
309
+ level,
310
+ methodLevels,
311
+ implementedMethods
312
+ });
313
+ coordinator.init();
57
314
  let previewCleanupFn = null;
58
315
  const recordingParams = /* @__PURE__ */ new Set();
59
316
  const currentConfig = { getSchema, updateSchema, renderPreview };
317
+ const methodTypeToName = {
318
+ [mergedMessageTypes.getSchema]: METHOD_NAMES.GET_SCHEMA,
319
+ [mergedMessageTypes.updateSchema]: METHOD_NAMES.UPDATE_SCHEMA,
320
+ [mergedMessageTypes.checkPreview]: METHOD_NAMES.CHECK_PREVIEW,
321
+ [mergedMessageTypes.renderPreview]: METHOD_NAMES.RENDER_PREVIEW,
322
+ [mergedMessageTypes.cleanupPreview]: METHOD_NAMES.CLEANUP_PREVIEW,
323
+ [mergedMessageTypes.startRecording]: METHOD_NAMES.START_RECORDING,
324
+ [mergedMessageTypes.stopRecording]: METHOD_NAMES.STOP_RECORDING
325
+ };
326
+ const getMethodNameByType = (type) => {
327
+ return methodTypeToName[type] ?? null;
328
+ };
60
329
  const sendResponse = (requestId, result) => {
61
330
  const message = {
62
331
  source: mergedSourceConfig.hostSource,
@@ -89,10 +358,18 @@ function createSchemaElementEditorBridge(config) {
89
358
  if (!event.data || event.data.source !== mergedSourceConfig.contentSource) return;
90
359
  const { type, payload, requestId } = event.data;
91
360
  if (!requestId) return;
361
+ const methodName = getMethodNameByType(type);
362
+ if (!methodName) return;
363
+ if (!coordinator.shouldRespond(methodName)) {
364
+ return;
365
+ }
92
366
  const { getSchema: getSchema2, updateSchema: updateSchema2, renderPreview: renderPreview2 } = currentConfig;
93
367
  let result;
94
368
  switch (type) {
95
369
  case mergedMessageTypes.getSchema: {
370
+ if (typeof getSchema2 !== "function") {
371
+ return;
372
+ }
96
373
  const params = String(payload?.params ?? "");
97
374
  try {
98
375
  const data = getSchema2(params);
@@ -106,6 +383,9 @@ function createSchemaElementEditorBridge(config) {
106
383
  break;
107
384
  }
108
385
  case mergedMessageTypes.updateSchema: {
386
+ if (typeof updateSchema2 !== "function") {
387
+ return;
388
+ }
109
389
  const schema = payload?.schema;
110
390
  const params = String(payload?.params ?? "");
111
391
  try {
@@ -178,11 +458,16 @@ function createSchemaElementEditorBridge(config) {
178
458
  default:
179
459
  return;
180
460
  }
461
+ const hasSameLevelSdks = coordinator.hasSameLevelCompetitors(methodName);
462
+ if (hasSameLevelSdks && shouldSkipFailedResponse(methodName, result)) {
463
+ return;
464
+ }
181
465
  sendResponse(requestId, result);
182
466
  };
183
467
  window.addEventListener("message", handleMessage);
184
468
  return {
185
469
  cleanup: () => {
470
+ coordinator.destroy();
186
471
  window.removeEventListener("message", handleMessage);
187
472
  if (previewCleanupFn) {
188
473
  previewCleanupFn();
@@ -198,7 +483,17 @@ function createSchemaElementEditorBridge(config) {
198
483
 
199
484
  // src/react.ts
200
485
  function useSchemaElementEditor(config) {
201
- const { getSchema, updateSchema, renderPreview, sourceConfig, messageTypes, enabled } = config;
486
+ const {
487
+ getSchema,
488
+ updateSchema,
489
+ renderPreview,
490
+ sourceConfig,
491
+ messageTypes,
492
+ enabled,
493
+ sdkId,
494
+ level,
495
+ methodLevels
496
+ } = config;
202
497
  const configRef = (0, import_react.useRef)({ getSchema, updateSchema, renderPreview });
203
498
  const bridgeRef = (0, import_react.useRef)(null);
204
499
  (0, import_react.useEffect)(() => {
@@ -209,11 +504,16 @@ function useSchemaElementEditor(config) {
209
504
  return;
210
505
  }
211
506
  const proxyConfig = {
212
- getSchema: (params) => configRef.current.getSchema(params),
213
- updateSchema: (schema, params) => configRef.current.updateSchema(schema, params),
507
+ // 外层检查确保初始时函数存在才创建代理
508
+ // 内层使用可选链安全访问,配合类型断言确保类型正确
509
+ getSchema: configRef.current.getSchema ? (params) => configRef.current.getSchema?.(params) : void 0,
510
+ updateSchema: configRef.current.updateSchema ? (schema, params) => configRef.current.updateSchema?.(schema, params) : void 0,
214
511
  renderPreview: configRef.current.renderPreview ? (schema, containerId) => configRef.current.renderPreview?.(schema, containerId) : void 0,
215
512
  sourceConfig,
216
- messageTypes
513
+ messageTypes,
514
+ sdkId,
515
+ level,
516
+ methodLevels
217
517
  };
218
518
  const bridge = createSchemaElementEditorBridge(proxyConfig);
219
519
  bridgeRef.current = bridge;
@@ -221,7 +521,7 @@ function useSchemaElementEditor(config) {
221
521
  bridge.cleanup();
222
522
  bridgeRef.current = null;
223
523
  };
224
- }, [enabled, configRef, sourceConfig, messageTypes]);
524
+ }, [enabled, configRef, sourceConfig, messageTypes, sdkId, level, methodLevels]);
225
525
  const push = (0, import_react.useCallback)((params, data) => {
226
526
  bridgeRef.current?.recording.push(params, data);
227
527
  }, []);
package/dist/index.d.cts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { useSchemaElementEditor } from './react.cjs';
2
- export { PostMessageSourceConfig, PostMessageTypeConfig, SchemaElementEditorBridge, SchemaElementEditorConfig, SchemaValue, createSchemaElementEditorBridge } from './core.cjs';
2
+ export { createSchemaElementEditorBridge } from './core.cjs';
3
+ export { M as MethodLevelConfig, P as PostMessageSourceConfig, a as PostMessageTypeConfig, c as SchemaElementEditorBridge, b as SchemaElementEditorConfig, d as SchemaElementEditorRecording, S as SchemaValue } from './types-D2ZJx8T_.cjs';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { useSchemaElementEditor } from './react.js';
2
- export { PostMessageSourceConfig, PostMessageTypeConfig, SchemaElementEditorBridge, SchemaElementEditorConfig, SchemaValue, createSchemaElementEditorBridge } from './core.js';
2
+ export { createSchemaElementEditorBridge } from './core.js';
3
+ export { M as MethodLevelConfig, P as PostMessageSourceConfig, a as PostMessageTypeConfig, c as SchemaElementEditorBridge, b as SchemaElementEditorConfig, d as SchemaElementEditorRecording, S as SchemaValue } from './types-D2ZJx8T_.js';
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
+ import "./chunk-432BCQVI.js";
1
2
  import {
2
3
  useSchemaElementEditor
3
- } from "./chunk-HBZGOJIJ.js";
4
+ } from "./chunk-MK3EDWC5.js";
4
5
  import {
5
6
  createSchemaElementEditorBridge
6
- } from "./chunk-52EFKQHQ.js";
7
+ } from "./chunk-DLMX4NDA.js";
7
8
  export {
8
9
  createSchemaElementEditorBridge,
9
10
  useSchemaElementEditor