@atomm-developer/generator-sdk 1.0.4 → 1.0.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
@@ -4,16 +4,25 @@
4
4
 
5
5
  The Atomm Generator Open Platform SDK provides core capabilities for third-party generator developers, including authentication, cloud storage, history, credits, billing, export, and template protocols.
6
6
 
7
+ For a standardized host layout with built-in login, template actions, and floating export entry, use the companion package `@atomm-developer/generator-workbench`.
8
+
7
9
  ## Installation
8
10
 
9
11
  ```bash
10
12
  # Via npm/pnpm/yarn
11
13
  pnpm add @atomm-developer/generator-sdk
14
+ pnpm add @atomm-developer/generator-workbench
12
15
 
13
16
  # Via CDN (for plain HTML)
14
17
  # <script src="https://static-res.atomm.com/scripts/js/generator-sdk/index.umd.js"></script>
18
+ # <script src="https://static-res.atomm.com/scripts/js/generator-sdk/generator-workbench/index.umd.js"></script>
15
19
  ```
16
20
 
21
+ ## Package Roles
22
+
23
+ - `@atomm-developer/generator-sdk`: platform capability layer such as auth, export, template, billing, and history.
24
+ - `@atomm-developer/generator-workbench`: official host shell that mounts the runtime canvas and parameter panel into a unified layout.
25
+
17
26
  ## Quick Start
18
27
 
19
28
  ### Initialization
@@ -25,6 +34,9 @@ const sdk = GeneratorSDK.init({
25
34
  appKey: 'your_app_key', // Get from developer console
26
35
  env: 'prod', // 'dev' | 'test' | 'pre' | 'prod'
27
36
  });
37
+
38
+ console.log(sdk.getAppKey());
39
+ console.log(GeneratorSDK.getAppKey());
28
40
  ```
29
41
 
30
42
  ### Core Modules
@@ -84,6 +96,27 @@ await sdk.export.openInStudio();
84
96
 
85
97
  For detailed API references and integration guides, please visit our [Documentation Portal](https://github.com/atomm-inc/generator-sdk/tree/main/docs).
86
98
 
99
+ ## Repository Build
100
+
101
+ This repository now builds both the SDK bundle and the `generator-workbench` UMD asset through the root build chain:
102
+
103
+ ```bash
104
+ pnpm build
105
+ pnpm type-check
106
+ pnpm test:workbench
107
+ ```
108
+
109
+ `pnpm deploy:cdn` uploads both `generator-sdk` and `generator-workbench` CDN assets to `static-res.atomm.com`.
110
+
111
+ If you need to publish both packages from the repository root, use:
112
+
113
+ ```bash
114
+ pnpm deploy
115
+ pnpm deploy:workbench
116
+ # or
117
+ pnpm deploy:all
118
+ ```
119
+
87
120
  ## License
88
121
 
89
122
  MIT
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { t as toUtf8, f as fromUtf8 } from "./index-qYdFih-w.js";
4
+ import { t as toUtf8, f as fromUtf8 } from "./index-DP-Q2Dkh.js";
5
5
  class EventStreamSerde {
6
6
  constructor({ marshaller, serializer, deserializer, serdeContext, defaultContentType }) {
7
7
  __publicField(this, "marshaller");
@@ -10002,6 +10002,25 @@ class AuthModule {
10002
10002
  getToken() {
10003
10003
  return getToken(this.appKey);
10004
10004
  }
10005
+ /**
10006
+ * 外部传入 token,同步到本地缓存并刷新登录状态
10007
+ */
10008
+ async syncToken(token) {
10009
+ const normalizedToken = token.trim();
10010
+ if (!normalizedToken) {
10011
+ throw new Error("Token is required");
10012
+ }
10013
+ try {
10014
+ setToken(this.appKey, normalizedToken);
10015
+ this.passportClient.token = normalizedToken;
10016
+ const userInfo = await this._fetchUserInfo(normalizedToken);
10017
+ this._setLoginStatus(userInfo);
10018
+ return this.getStatus();
10019
+ } catch (error) {
10020
+ this._clearLoginStatus();
10021
+ throw error;
10022
+ }
10023
+ }
10005
10024
  /**
10006
10025
  * 弹出登录弹窗
10007
10026
  * 登录成功后 resolve UserInfo,用户取消则 reject
@@ -12265,7 +12284,7 @@ class HttpProtocol extends SerdeContext {
12265
12284
  });
12266
12285
  }
12267
12286
  async loadEventStreamCapability() {
12268
- const { EventStreamSerde } = await import("./index-wW-kUlHs.js");
12287
+ const { EventStreamSerde } = await import("./index-6H3DQX8m.js");
12269
12288
  return new EventStreamSerde({
12270
12289
  marshaller: this.getEventStreamMarshaller(),
12271
12290
  serializer: this.serializer,
@@ -80710,20 +80729,20 @@ class ExportModule {
80710
80729
  }
80711
80730
  /**
80712
80731
  * 下载图片到本地
80713
- * 内部流程:getExportCanvas('download') toBlob 触发浏览器下载
80732
+ * 支持 png / jpeg / webp / svg 格式
80733
+ *
80734
+ * 内部流程:
80735
+ * 1. 优先调用 getExportData(purpose, format) 获取导出数据
80736
+ * 2. 若未实现则回退到 getExportCanvas(purpose) → canvas.toBlob
80737
+ * 3. 生成 Blob → 触发浏览器下载
80714
80738
  */
80715
80739
  async download(options) {
80716
80740
  var _a2, _b;
80717
- const canvas = await this._getCanvas("download");
80718
80741
  const format2 = (options == null ? void 0 : options.format) ?? "png";
80719
- const mimeType = format2 === "svg" ? "image/svg+xml" : "image/png";
80720
- const fileName = (options == null ? void 0 : options.fileName) ?? ((_b = (_a2 = this.provider) == null ? void 0 : _a2.getFileName) == null ? void 0 : _b.call(_a2, "download")) ?? `export-${Date.now()}.${format2}`;
80721
- const blob = await new Promise(
80722
- (resolve) => canvas.toBlob(resolve, mimeType)
80723
- );
80724
- if (!blob) {
80725
- throw new SdkError(-1, "Failed to create blob from canvas");
80726
- }
80742
+ const quality = options == null ? void 0 : options.quality;
80743
+ const blob = await this._getBlob("download", format2, quality);
80744
+ const extension = format2 === "jpeg" ? "jpg" : format2;
80745
+ const fileName = (options == null ? void 0 : options.fileName) ?? ((_b = (_a2 = this.provider) == null ? void 0 : _a2.getFileName) == null ? void 0 : _b.call(_a2, "download")) ?? `export-${Date.now()}.${extension}`;
80727
80746
  const url2 = URL.createObjectURL(blob);
80728
80747
  const link = document.createElement("a");
80729
80748
  link.download = fileName;
@@ -80739,48 +80758,153 @@ class ExportModule {
80739
80758
  }
80740
80759
  /**
80741
80760
  * 打开到 xTool Studio
80742
- * 内部流程:
80743
- * - 传入图片 URL:直接拉起 Studio
80744
- * - 传入图片数据:上传 OSS 调用 xtool:// 协议
80745
- * - 未传参数:getExportCanvas('studio') toDataURL 上传 OSS 调用 xtool:// 协议
80761
+ *
80762
+ * 支持两种调用方式(向后兼容):
80763
+ * - openInStudio(source) — 直接传图片源(URL / data URL / Blob / File)
80764
+ * - openInStudio({ source, format }) options 对象,可指定导入格式
80765
+ * - openInStudio() — 无参数,通过 provider 获取数据
80766
+ *
80767
+ * 当未传 source 时,根据 format 参数通过 provider 获取对应格式的数据上传到 OSS
80746
80768
  */
80747
- async openInStudio(source) {
80748
- const studioAsset = await this._resolveStudioAsset(source);
80769
+ async openInStudio(sourceOrOptions) {
80770
+ var _a2;
80771
+ const { source, format: format2 } = this._normalizeStudioArgs(sourceOrOptions);
80772
+ const studioAsset = await this._resolveStudioAsset(source, format2);
80749
80773
  const ossUrl = studioAsset.type === "url" ? studioAsset.url : await this._uploadFile(studioAsset.file);
80750
- this._openStudioProtocol(ossUrl);
80774
+ const isSvg = format2 === "svg" || ((_a2 = studioAsset.file) == null ? void 0 : _a2.type) === "image/svg+xml";
80775
+ this._openStudioProtocol(ossUrl, isSvg ? "SVG" : "2D");
80751
80776
  return { success: true };
80752
80777
  }
80778
+ /**
80779
+ * 兼容解析 openInStudio 参数:
80780
+ * - 无参数 → { source: undefined, format: 'png' }
80781
+ * - string / Blob / File → { source, format: 'png' }
80782
+ * - { source?, format? } → 直接使用
80783
+ */
80784
+ _normalizeStudioArgs(input) {
80785
+ if (input == null) {
80786
+ return { source: void 0, format: "png" };
80787
+ }
80788
+ if (typeof input === "string" || input instanceof Blob || input instanceof File) {
80789
+ return { source: input, format: "png" };
80790
+ }
80791
+ return {
80792
+ source: input.source,
80793
+ format: input.format ?? "png"
80794
+ };
80795
+ }
80753
80796
  // ─────────────────────────────────────────────
80754
- // 内部方法
80797
+ // 内部方法 - 数据获取
80755
80798
  // ─────────────────────────────────────────────
80756
80799
  /**
80757
- * 获取导出 Canvas 并校验
80800
+ * 获取导出 Blob,统一处理 getExportData / getExportCanvas 两种路径
80758
80801
  */
80759
- async _getCanvas(purpose) {
80802
+ async _getBlob(purpose, format2, quality) {
80803
+ this._ensureProvider();
80804
+ const exportData = await this._tryGetExportData(purpose, format2);
80805
+ if (exportData) {
80806
+ return this._exportDataToBlob(exportData, format2, quality);
80807
+ }
80808
+ const canvas = await this._tryGetCanvas(purpose);
80809
+ if (canvas) {
80810
+ return this._canvasToBlob(canvas, format2, quality);
80811
+ }
80812
+ throw new SdkError(
80813
+ -1,
80814
+ `No export data available for purpose: ${purpose}. Implement getExportData or getExportCanvas in your ExportProvider.`
80815
+ );
80816
+ }
80817
+ /** 尝试通过 getExportData 获取导出数据 */
80818
+ async _tryGetExportData(purpose, format2) {
80819
+ var _a2;
80820
+ if (!((_a2 = this.provider) == null ? void 0 : _a2.getExportData)) return null;
80821
+ return await Promise.resolve(this.provider.getExportData(purpose, format2));
80822
+ }
80823
+ /** 尝试通过 getExportCanvas 获取画布 */
80824
+ async _tryGetCanvas(purpose) {
80825
+ var _a2;
80826
+ if (!((_a2 = this.provider) == null ? void 0 : _a2.getExportCanvas)) return null;
80827
+ return await Promise.resolve(this.provider.getExportCanvas(purpose));
80828
+ }
80829
+ /** 确保 provider 已注册 */
80830
+ _ensureProvider() {
80760
80831
  if (!this.provider) {
80761
80832
  throw new SdkError(
80762
80833
  -1,
80763
80834
  "Export provider not registered. Call sdk.export.register() first."
80764
80835
  );
80765
80836
  }
80766
- const canvas = await Promise.resolve(this.provider.getExportCanvas(purpose));
80767
- if (!canvas) {
80768
- throw new SdkError(-1, `No canvas available for purpose: ${purpose}`);
80837
+ if (!this.provider.getExportData && !this.provider.getExportCanvas) {
80838
+ throw new SdkError(
80839
+ -1,
80840
+ "ExportProvider must implement at least one of getExportData or getExportCanvas."
80841
+ );
80842
+ }
80843
+ }
80844
+ // ─────────────────────────────────────────────
80845
+ // 内部方法 - ExportData → Blob 转换
80846
+ // ─────────────────────────────────────────────
80847
+ /** 将 ExportData 转换为 Blob */
80848
+ async _exportDataToBlob(data, format2, quality) {
80849
+ switch (data.type) {
80850
+ case "canvas":
80851
+ return this._canvasToBlob(data.canvas, format2, quality);
80852
+ case "blob":
80853
+ return data.blob;
80854
+ case "dataUrl":
80855
+ return this._dataUrlToBlob(data.dataUrl);
80856
+ case "url": {
80857
+ const response = await fetch(data.url);
80858
+ if (!response.ok) {
80859
+ throw new SdkError(-1, `Failed to fetch export URL: ${response.status}`);
80860
+ }
80861
+ return await response.blob();
80862
+ }
80863
+ case "svg":
80864
+ return new Blob([data.svgString], { type: "image/svg+xml" });
80865
+ default:
80866
+ throw new SdkError(-1, `Unknown ExportData type: ${data.type}`);
80769
80867
  }
80770
- return canvas;
80771
80868
  }
80869
+ /** Canvas → Blob,正确处理各格式 */
80870
+ _canvasToBlob(canvas, format2, quality) {
80871
+ if (format2 === "svg") {
80872
+ throw new SdkError(
80873
+ -1,
80874
+ 'Cannot export SVG from HTMLCanvasElement. Implement getExportData in your ExportProvider to return { type: "svg", svgString }.'
80875
+ );
80876
+ }
80877
+ const mimeType = this._formatToMimeType(format2);
80878
+ return new Promise((resolve, reject) => {
80879
+ canvas.toBlob(
80880
+ (blob) => {
80881
+ if (!blob) {
80882
+ reject(new SdkError(-1, "Failed to create blob from canvas"));
80883
+ return;
80884
+ }
80885
+ resolve(blob);
80886
+ },
80887
+ mimeType,
80888
+ quality
80889
+ );
80890
+ });
80891
+ }
80892
+ // ─────────────────────────────────────────────
80893
+ // 内部方法 - openInStudio 相关
80894
+ // ─────────────────────────────────────────────
80772
80895
  /**
80773
80896
  * 解析 openInStudio 的输入来源:
80774
80897
  * - URL 直传给 Studio
80775
80898
  * - data URL / Blob / File 先上传 OSS
80776
- * - 未传时回退到 provider canvas
80899
+ * - 未传时通过 provider 按 format 获取数据
80777
80900
  */
80778
- async _resolveStudioAsset(source) {
80901
+ async _resolveStudioAsset(source, format2 = "png") {
80779
80902
  if (typeof source === "string") {
80780
80903
  if (this._isDataUrl(source)) {
80904
+ const ext2 = this._detectDataUrlExtension(source);
80781
80905
  return {
80782
80906
  type: "file",
80783
- file: this._dataUrlToFile(source, `${v4$2()}.png`)
80907
+ file: this._dataUrlToFile(source, `${v4$2()}.${ext2}`)
80784
80908
  };
80785
80909
  }
80786
80910
  if (this._isHttpUrl(source)) {
@@ -80795,21 +80919,14 @@ class ExportModule {
80795
80919
  return { type: "file", file: source };
80796
80920
  }
80797
80921
  if (source instanceof Blob) {
80798
- return {
80799
- type: "file",
80800
- file: this._blobToFile(source)
80801
- };
80802
- }
80803
- const canvas = await this._getCanvas("studio");
80804
- let dataUrl;
80805
- try {
80806
- dataUrl = canvas.toDataURL("image/png");
80807
- } catch {
80808
- throw new SdkError(-1, "Failed to export canvas (cross-origin taint?)");
80922
+ return { type: "file", file: this._blobToFile(source) };
80809
80923
  }
80924
+ const blob = await this._getBlob("studio", format2);
80925
+ const ext = format2 === "jpeg" ? "jpg" : format2;
80926
+ const mimeType = this._formatToMimeType(format2);
80810
80927
  return {
80811
80928
  type: "file",
80812
- file: this._dataUrlToFile(dataUrl, `${v4$2()}.png`)
80929
+ file: new File([blob], `${v4$2()}.${ext}`, { type: mimeType })
80813
80930
  };
80814
80931
  }
80815
80932
  /** 获取或懒初始化 Uploader 实例 */
@@ -80835,7 +80952,7 @@ class ExportModule {
80835
80952
  });
80836
80953
  }
80837
80954
  /** 通过 xtool:// 协议打开 Studio */
80838
- _openStudioProtocol(ossUrl) {
80955
+ _openStudioProtocol(ossUrl, assetType = "2D") {
80839
80956
  const payload = {
80840
80957
  event: {
80841
80958
  name: "OpenOSSImage",
@@ -80862,7 +80979,7 @@ class ExportModule {
80862
80979
  payload: {
80863
80980
  urls: [
80864
80981
  {
80865
- type: "2D",
80982
+ type: assetType,
80866
80983
  url: ossUrl,
80867
80984
  aimakeInfo: { generator: this.appKey }
80868
80985
  }
@@ -80878,6 +80995,31 @@ class ExportModule {
80878
80995
  window.open(ossUrl, "_blank");
80879
80996
  }
80880
80997
  }
80998
+ // ─────────────────────────────────────────────
80999
+ // 工具方法
81000
+ // ─────────────────────────────────────────────
81001
+ /** 导出格式 → MIME 类型(仅位图格式) */
81002
+ _formatToMimeType(format2) {
81003
+ const map = {
81004
+ png: "image/png",
81005
+ jpeg: "image/jpeg",
81006
+ webp: "image/webp",
81007
+ svg: "image/svg+xml"
81008
+ };
81009
+ return map[format2] ?? "image/png";
81010
+ }
81011
+ /** data URL → Blob */
81012
+ _dataUrlToBlob(dataUrl) {
81013
+ var _a2;
81014
+ const [header, base64] = dataUrl.split(",");
81015
+ const mimeType = ((_a2 = header.match(/:(.*?);/)) == null ? void 0 : _a2[1]) ?? "image/png";
81016
+ const binary = atob(base64);
81017
+ const bytes = new Uint8Array(binary.length);
81018
+ for (let i = 0; i < binary.length; i++) {
81019
+ bytes[i] = binary.charCodeAt(i);
81020
+ }
81021
+ return new Blob([bytes], { type: mimeType });
81022
+ }
80881
81023
  /** data URL → File 对象 */
80882
81024
  _dataUrlToFile(dataUrl, filename) {
80883
81025
  var _a2;
@@ -80896,6 +81038,13 @@ class ExportModule {
80896
81038
  const extension = this._mimeTypeToExtension(mimeType);
80897
81039
  return new File([blob], `${v4$2()}.${extension}`, { type: mimeType });
80898
81040
  }
81041
+ /** 从 data URL 推断文件扩展名 */
81042
+ _detectDataUrlExtension(dataUrl) {
81043
+ var _a2;
81044
+ const mimeType = (_a2 = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);/)) == null ? void 0 : _a2[1];
81045
+ if (!mimeType) return "png";
81046
+ return this._mimeTypeToExtension(mimeType);
81047
+ }
80899
81048
  /** 判断是否为 data URL */
80900
81049
  _isDataUrl(value) {
80901
81050
  return /^data:image\/[a-zA-Z0-9.+-]+;base64,/.test(value);
@@ -80912,9 +81061,8 @@ class ExportModule {
80912
81061
  /** MIME 类型 → 文件扩展名 */
80913
81062
  _mimeTypeToExtension(mimeType) {
80914
81063
  var _a2;
80915
- if (mimeType === "image/svg+xml") {
80916
- return "svg";
80917
- }
81064
+ if (mimeType === "image/svg+xml") return "svg";
81065
+ if (mimeType === "image/jpeg") return "jpg";
80918
81066
  return ((_a2 = mimeType.split("/")[1]) == null ? void 0 : _a2.split("+")[0]) ?? "png";
80919
81067
  }
80920
81068
  }
@@ -81138,7 +81286,7 @@ class TemplateModule {
81138
81286
  }
81139
81287
  }
81140
81288
  const instanceCache = /* @__PURE__ */ new Map();
81141
- class GeneratorSDK {
81289
+ const _GeneratorSDK = class _GeneratorSDK {
81142
81290
  constructor(options) {
81143
81291
  /** 登录/退出/用户信息 */
81144
81292
  __publicField(this, "auth");
@@ -81170,6 +81318,12 @@ class GeneratorSDK {
81170
81318
  this.export = new ExportModule(appKey, env, http);
81171
81319
  this.template = new TemplateModule();
81172
81320
  }
81321
+ /**
81322
+ * 返回当前 SDK 实例绑定的 appKey
81323
+ */
81324
+ getAppKey() {
81325
+ return this.appKey;
81326
+ }
81173
81327
  // ─────────────────────────────────────────────
81174
81328
  // 静态工厂方法
81175
81329
  // ─────────────────────────────────────────────
@@ -81189,23 +81343,34 @@ class GeneratorSDK {
81189
81343
  }
81190
81344
  const env = options.env ?? "prod";
81191
81345
  const cacheKey = `${options.appKey}::${env}`;
81346
+ _GeneratorSDK.lastInitializedAppKey = options.appKey;
81192
81347
  if (instanceCache.has(cacheKey)) {
81193
81348
  return instanceCache.get(cacheKey);
81194
81349
  }
81195
- const instance = new GeneratorSDK({ appKey: options.appKey, env });
81350
+ const instance = new _GeneratorSDK({ appKey: options.appKey, env });
81196
81351
  instanceCache.set(cacheKey, instance);
81197
81352
  console.info(
81198
81353
  `[GeneratorSDK] initialized (appKey: ${options.appKey}, env: ${env})`
81199
81354
  );
81200
81355
  return instance;
81201
81356
  }
81357
+ /**
81358
+ * 返回最近一次初始化使用的 appKey
81359
+ */
81360
+ static getAppKey() {
81361
+ return _GeneratorSDK.lastInitializedAppKey;
81362
+ }
81202
81363
  /**
81203
81364
  * 清除实例缓存(测试场景使用)
81204
81365
  */
81205
81366
  static _clearCache() {
81206
81367
  instanceCache.clear();
81368
+ _GeneratorSDK.lastInitializedAppKey = null;
81207
81369
  }
81208
- }
81370
+ };
81371
+ /** 最近一次初始化使用的 appKey,供全局场景读取 */
81372
+ __publicField(_GeneratorSDK, "lastInitializedAppKey", null);
81373
+ let GeneratorSDK = _GeneratorSDK;
81209
81374
  const withBilling = async (billing, action5, options) => {
81210
81375
  var _a2;
81211
81376
  const check = billing.check();