@gaoding/cli 0.0.0 → 1.0.0

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.
Files changed (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -3
  3. package/contracts/operations/agent.send/input.schema.json +353 -0
  4. package/contracts/operations/agent.send/output.schema.json +271 -0
  5. package/contracts/operations/auth.status/input.schema.json +7 -0
  6. package/contracts/operations/auth.status/output.schema.json +132 -0
  7. package/contracts/operations/dam.copy/input.schema.json +19 -0
  8. package/contracts/operations/dam.copy/output.schema.json +19 -0
  9. package/contracts/operations/dam.delete/input.schema.json +23 -0
  10. package/contracts/operations/dam.delete/output.schema.json +6 -0
  11. package/contracts/operations/dam.download/input.schema.json +14 -0
  12. package/contracts/operations/dam.download/output.schema.json +25 -0
  13. package/contracts/operations/dam.folder.list/input.schema.json +15 -0
  14. package/contracts/operations/dam.folder.list/output.schema.json +37 -0
  15. package/contracts/operations/dam.get/input.schema.json +16 -0
  16. package/contracts/operations/dam.get/output.schema.json +46 -0
  17. package/contracts/operations/dam.list/input.schema.json +44 -0
  18. package/contracts/operations/dam.list/output.schema.json +71 -0
  19. package/contracts/operations/dam.move/input.schema.json +19 -0
  20. package/contracts/operations/dam.move/output.schema.json +18 -0
  21. package/contracts/operations/dam.recycle.list/input.schema.json +13 -0
  22. package/contracts/operations/dam.recycle.list/output.schema.json +34 -0
  23. package/contracts/operations/dam.recycle.restore/input.schema.json +14 -0
  24. package/contracts/operations/dam.recycle.restore/output.schema.json +6 -0
  25. package/contracts/operations/dam.rename/input.schema.json +18 -0
  26. package/contracts/operations/dam.rename/output.schema.json +18 -0
  27. package/contracts/operations/dam.repository.list/input.schema.json +8 -0
  28. package/contracts/operations/dam.repository.list/output.schema.json +52 -0
  29. package/contracts/operations/dam.search/input.schema.json +46 -0
  30. package/contracts/operations/dam.search/output.schema.json +71 -0
  31. package/contracts/operations/dam.tag.list/input.schema.json +12 -0
  32. package/contracts/operations/dam.tag.list/output.schema.json +26 -0
  33. package/contracts/operations/dam.upload/input.schema.json +31 -0
  34. package/contracts/operations/dam.upload/output.schema.json +46 -0
  35. package/contracts/operations/editor.apply/input.schema.json +134 -0
  36. package/contracts/operations/editor.apply/output.schema.json +15 -0
  37. package/contracts/operations/editor.connect/input.schema.json +15 -0
  38. package/contracts/operations/editor.connect/output.schema.json +17 -0
  39. package/contracts/operations/editor.disconnect/input.schema.json +8 -0
  40. package/contracts/operations/editor.disconnect/output.schema.json +14 -0
  41. package/contracts/operations/editor.save/input.schema.json +8 -0
  42. package/contracts/operations/editor.save/output.schema.json +23 -0
  43. package/contracts/operations/editor.screenshot/input.schema.json +8 -0
  44. package/contracts/operations/editor.screenshot/output.schema.json +16 -0
  45. package/contracts/operations/editor.snapshot/input.schema.json +8 -0
  46. package/contracts/operations/editor.snapshot/output.schema.json +113 -0
  47. package/contracts/operations/model.get/input.schema.json +18 -0
  48. package/contracts/operations/model.get/output.schema.json +191 -0
  49. package/contracts/operations/model.list/input.schema.json +15 -0
  50. package/contracts/operations/model.list/output.schema.json +111 -0
  51. package/contracts/operations/org.current/input.schema.json +7 -0
  52. package/contracts/operations/org.current/output.schema.json +49 -0
  53. package/contracts/operations/org.list/input.schema.json +7 -0
  54. package/contracts/operations/org.list/output.schema.json +71 -0
  55. package/contracts/operations/tool.call/input.schema.json +35 -0
  56. package/contracts/operations/tool.call/output.schema.json +129 -0
  57. package/contracts/operations/tool.list/input.schema.json +7 -0
  58. package/contracts/operations/tool.list/output.schema.json +45 -0
  59. package/dist/bin/gd-cli.js +60 -0
  60. package/dist/bin/postinstall.js +42 -0
  61. package/dist/src/bootstrap/create-cli.js +112 -0
  62. package/dist/src/bootstrap/create-runtime.js +290 -0
  63. package/dist/src/bootstrap/validators.js +157 -0
  64. package/dist/src/cli/action-binding.js +109 -0
  65. package/dist/src/cli/agent-commands.js +49 -0
  66. package/dist/src/cli/auth-commands.js +55 -0
  67. package/dist/src/cli/dam-commands.js +469 -0
  68. package/dist/src/cli/editor-commands.js +73 -0
  69. package/dist/src/cli/errors.js +157 -0
  70. package/dist/src/cli/model-commands.js +61 -0
  71. package/dist/src/cli/org-commands.js +58 -0
  72. package/dist/src/cli/presenter.js +105 -0
  73. package/dist/src/cli/prompt.js +30 -0
  74. package/dist/src/cli/tool-commands.js +77 -0
  75. package/dist/src/cli/update-command.js +14 -0
  76. package/dist/src/contracts/schema.js +4 -0
  77. package/dist/src/features/agent/creative-agent-adapter.js +36 -0
  78. package/dist/src/features/agent/creative-protocol.js +432 -0
  79. package/dist/src/features/agent/creative-stream.js +127 -0
  80. package/dist/src/features/agent/use-cases.js +150 -0
  81. package/dist/src/features/auth/access-policy.js +101 -0
  82. package/dist/src/features/auth/credential-store.js +62 -0
  83. package/dist/src/features/auth/sso-service.js +194 -0
  84. package/dist/src/features/auth/state.js +129 -0
  85. package/dist/src/features/auth/use-cases.js +136 -0
  86. package/dist/src/features/dam/asset-projection.js +278 -0
  87. package/dist/src/features/dam/dam-api-adapter.js +562 -0
  88. package/dist/src/features/dam/file-downloader.js +242 -0
  89. package/dist/src/features/dam/object-storage.js +193 -0
  90. package/dist/src/features/dam/operation-executor.js +72 -0
  91. package/dist/src/features/dam/registered-uploader.js +247 -0
  92. package/dist/src/features/dam/repository-catalog.js +127 -0
  93. package/dist/src/features/dam/storage-upload.js +141 -0
  94. package/dist/src/features/dam/transient-uploader.js +17 -0
  95. package/dist/src/features/dam/use-cases.js +470 -0
  96. package/dist/src/features/editor/bridge-client.js +109 -0
  97. package/dist/src/features/editor/bridge-process.js +222 -0
  98. package/dist/src/features/editor/bridge-server.js +337 -0
  99. package/dist/src/features/editor/protocol.js +1 -0
  100. package/dist/src/features/editor/public-url.js +36 -0
  101. package/dist/src/features/editor/session-state.js +78 -0
  102. package/dist/src/features/editor/session.js +207 -0
  103. package/dist/src/features/editor/use-cases.js +88 -0
  104. package/dist/src/features/org/org-service.js +76 -0
  105. package/dist/src/features/org/use-cases.js +156 -0
  106. package/dist/src/features/skill/bundled-skills.js +55 -0
  107. package/dist/src/features/skill/installer.js +271 -0
  108. package/dist/src/features/tool/catalog.js +83 -0
  109. package/dist/src/features/tool/dynamic-schema.js +128 -0
  110. package/dist/src/features/tool/mns-catalog-adapter.js +265 -0
  111. package/dist/src/features/tool/tool-api-adapter.js +352 -0
  112. package/dist/src/features/tool/use-cases.js +177 -0
  113. package/dist/src/features/update/latest-version.js +37 -0
  114. package/dist/src/features/update/update-notification.js +262 -0
  115. package/dist/src/features/update/update-service.js +183 -0
  116. package/dist/src/platform/json-input.js +67 -0
  117. package/dist/src/platform/local-json-file.js +59 -0
  118. package/dist/src/platform/open-browser.js +8 -0
  119. package/dist/src/platform/redact.js +80 -0
  120. package/dist/src/platform/remote-error-evidence.js +80 -0
  121. package/dist/src/platform/remote-protocol.js +10 -0
  122. package/dist/src/platform/safe-upload-file.js +146 -0
  123. package/dist/src/platform/signature.js +20 -0
  124. package/dist/src/platform/signed-http-transport.js +143 -0
  125. package/dist/src/platform/url-safety.js +100 -0
  126. package/dist/src/telemetry/invocation.js +156 -0
  127. package/dist/src/telemetry/sls-sink.js +31 -0
  128. package/package.json +54 -8
  129. package/skills/gd-cli/SKILL.md +15 -0
  130. package/skills/gd-cli/references/auth-org.md +9 -0
  131. package/skills/gd-cli/references/creation.md +115 -0
  132. package/skills/gd-cli/references/dam.md +52 -0
  133. package/skills/gd-cli/references/editor.md +254 -0
  134. package/skills/gd-cli/references/errors.md +13 -0
  135. package/skills/gd-cli/references/update.md +13 -0
  136. package/index.js +0 -3
@@ -0,0 +1,247 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { setTimeout as delay } from "node:timers/promises";
4
+ import sharp from "sharp";
5
+ import { RemoteRequestError } from "../../platform/signed-http-transport.js";
6
+ import { projectDamAssetDetail } from "./asset-projection.js";
7
+ const imageFormats = new Set([
8
+ "png", "jpg", "jpeg", "gif", "svg", "webp", "jfif", "bmp", "tiff", "tif", "heic", "avif"
9
+ ]);
10
+ const videoFormats = new Set([
11
+ "mp4", "avi", "flv", "mov", "3gp", "m3u8", "mpg", "asf", "wmv", "mkv", "ts", "webm", "mxf"
12
+ ]);
13
+ const audioFormats = new Set(["mp3", "wav", "ogg", "flac"]);
14
+ const fontFormats = new Set(["ttf", "otf"]);
15
+ const designFormats = new Set(["psd", "ai", "sketch", "pdf"]);
16
+ const slideFormats = new Set(["ppt", "pptx"]);
17
+ function storageFormat(format) {
18
+ const normalized = format.toLowerCase();
19
+ if (imageFormats.has(normalized))
20
+ return "gdpic";
21
+ if (videoFormats.has(normalized))
22
+ return "gdvideo";
23
+ if (audioFormats.has(normalized))
24
+ return "gdaudio";
25
+ if (fontFormats.has(normalized))
26
+ return "gdfont";
27
+ if (designFormats.has(normalized))
28
+ return "gdimage";
29
+ if (slideFormats.has(normalized))
30
+ return "gdslide";
31
+ return normalized;
32
+ }
33
+ function publicType(format) {
34
+ if (format === "gdpic" || format === "gdimage")
35
+ return "image";
36
+ if (format === "gdvideo")
37
+ return "video";
38
+ if (format === "gdaudio")
39
+ return "audio";
40
+ if (format === "gdfont")
41
+ return "font";
42
+ if (format === "gdtemplate")
43
+ return "template";
44
+ return "file";
45
+ }
46
+ async function hashes(filePath, signal) {
47
+ signal.throwIfAborted();
48
+ const md5 = createHash("md5");
49
+ const sha1 = createHash("sha1");
50
+ const stream = createReadStream(filePath, { signal });
51
+ try {
52
+ for await (const chunk of stream) {
53
+ signal.throwIfAborted();
54
+ md5.update(chunk);
55
+ sha1.update(chunk);
56
+ }
57
+ signal.throwIfAborted();
58
+ return { md5: md5.digest("hex"), sha1: sha1.digest("hex") };
59
+ }
60
+ catch (error) {
61
+ if (signal.aborted)
62
+ throw signal.reason;
63
+ throw error;
64
+ }
65
+ }
66
+ function filenameTitle(filename) {
67
+ const withoutExtension = filename.replace(/\.[^.]+$/u, "");
68
+ return withoutExtension === "" ? filename : withoutExtension;
69
+ }
70
+ function terminalAnalysisStatus(value) {
71
+ const normalized = typeof value === "string" ? Number(value) : value;
72
+ return normalized === 2 || normalized === 3 || normalized === 4;
73
+ }
74
+ async function defaultSleep(milliseconds, signal) {
75
+ signal.throwIfAborted();
76
+ try {
77
+ await delay(milliseconds, undefined, { signal });
78
+ }
79
+ catch (error) {
80
+ if (signal.aborted)
81
+ throw signal.reason;
82
+ throw error;
83
+ }
84
+ }
85
+ export function createDamRegisteredUploader(dependencies) {
86
+ const now = dependencies.now ?? (() => new Date());
87
+ const sleep = dependencies.sleep ?? defaultSleep;
88
+ const imageMetadata = dependencies.imageMetadata
89
+ ?? (async (filePath) => {
90
+ const metadata = await sharp(filePath).metadata();
91
+ return { width: metadata.width, height: metadata.height };
92
+ });
93
+ async function localMetadata(file, signal) {
94
+ if (!file.mediaType.startsWith("image/"))
95
+ return {};
96
+ try {
97
+ const metadata = await imageMetadata(file.filePath);
98
+ signal.throwIfAborted();
99
+ return {
100
+ ...(typeof metadata.width === "number" && Number.isFinite(metadata.width)
101
+ ? { width: metadata.width }
102
+ : {}),
103
+ ...(typeof metadata.height === "number" && Number.isFinite(metadata.height)
104
+ ? { height: metadata.height }
105
+ : {})
106
+ };
107
+ }
108
+ catch {
109
+ if (signal.aborted)
110
+ throw signal.reason;
111
+ return {};
112
+ }
113
+ }
114
+ return {
115
+ async upload(input) {
116
+ try {
117
+ const stage = (name, run) => (dependencies.telemetry.stage(name, async () => {
118
+ try {
119
+ return await run();
120
+ }
121
+ catch (error) {
122
+ if (input.signal.aborted)
123
+ throw input.signal.reason;
124
+ if (error instanceof RemoteRequestError)
125
+ throw error;
126
+ throw new RemoteRequestError();
127
+ }
128
+ }));
129
+ input.signal.throwIfAborted();
130
+ const prepared = await stage("prepare", async () => {
131
+ const [digest, metadata] = await Promise.all([
132
+ hashes(input.file.filePath, input.signal),
133
+ localMetadata(input.file, input.signal)
134
+ ]);
135
+ input.signal.throwIfAborted();
136
+ const contentId = await dependencies.api.createAssetId({
137
+ access: input.access,
138
+ signal: input.signal
139
+ });
140
+ const format = input.file.format.toLowerCase();
141
+ return {
142
+ digest,
143
+ metadata,
144
+ contentId,
145
+ format,
146
+ damFormat: storageFormat(format),
147
+ title: input.input.title ?? filenameTitle(input.file.filename),
148
+ folderId: input.input.folderId ?? "0"
149
+ };
150
+ });
151
+ const { digest, metadata, contentId, format, damFormat, title, folderId } = prepared;
152
+ const stored = await stage("upload", () => dependencies.storage.upload({
153
+ file: input.file,
154
+ contentId
155
+ }, input.access, input.signal));
156
+ const preview = input.file.mediaType.startsWith("image/")
157
+ ? {
158
+ url: stored.url,
159
+ ...metadata,
160
+ format
161
+ }
162
+ : {};
163
+ const body = {
164
+ folder_id: folderId,
165
+ repository_id: input.repository.id,
166
+ second_transmission: false,
167
+ storage_format: damFormat,
168
+ category_ids: [],
169
+ asset_info: {
170
+ id: contentId,
171
+ origin_url: stored.url,
172
+ size: input.file.size,
173
+ title,
174
+ md5: digest.md5,
175
+ sha1: digest.sha1,
176
+ mime_type: input.file.mediaType,
177
+ format,
178
+ preview
179
+ },
180
+ ...(input.input.tagIds === undefined
181
+ ? {}
182
+ : { tag_ids: [...input.input.tagIds] })
183
+ };
184
+ const fallback = projectDamAssetDetail({
185
+ id: contentId,
186
+ title,
187
+ format,
188
+ type: publicType(damFormat),
189
+ origin_info: {
190
+ url: stored.url,
191
+ size: input.file.size,
192
+ format
193
+ },
194
+ preview_info: preview,
195
+ repository_info: {
196
+ id: input.repository.id,
197
+ ...(input.repository.name === undefined ? {} : { name: input.repository.name })
198
+ },
199
+ folder_info: { id: folderId },
200
+ ...(input.input.tagIds === undefined
201
+ ? {}
202
+ : { tag_ids: [...input.input.tagIds] })
203
+ });
204
+ if (fallback === undefined)
205
+ throw new RemoteRequestError();
206
+ let latest = await stage("persist", () => dependencies.api.createMaterial({
207
+ body,
208
+ fallback,
209
+ access: input.access,
210
+ signal: input.signal
211
+ }));
212
+ if (input.input.waitAnalysis !== true || terminalAnalysisStatus(latest.analysis_status)) {
213
+ return latest;
214
+ }
215
+ return stage("wait", async () => {
216
+ const deadline = now().getTime() + 180_000;
217
+ while (true) {
218
+ input.signal.throwIfAborted();
219
+ const current = await dependencies.api.getBatchStatus({
220
+ assetId: latest.asset_id,
221
+ fallback: latest,
222
+ access: input.access,
223
+ signal: input.signal
224
+ });
225
+ if (current !== null)
226
+ latest = current;
227
+ if (terminalAnalysisStatus(latest.analysis_status))
228
+ return latest;
229
+ const remaining = deadline - now().getTime();
230
+ if (remaining <= 0) {
231
+ dependencies.warn("素材分析仍在处理中,已返回当前结果。");
232
+ return latest;
233
+ }
234
+ await sleep(Math.min(3000, remaining), input.signal);
235
+ }
236
+ });
237
+ }
238
+ catch (error) {
239
+ if (input.signal.aborted)
240
+ throw input.signal.reason;
241
+ if (error instanceof RemoteRequestError)
242
+ throw error;
243
+ throw new RemoteRequestError();
244
+ }
245
+ }
246
+ };
247
+ }
@@ -0,0 +1,127 @@
1
+ import { RemoteProtocolError } from "../../platform/remote-protocol.js";
2
+ const permissions = new Set([
3
+ "OWNER",
4
+ "MANAGE",
5
+ "EDIT",
6
+ "USE",
7
+ "READ"
8
+ ]);
9
+ function record(value) {
10
+ return value !== null && typeof value === "object" && !Array.isArray(value)
11
+ ? value
12
+ : undefined;
13
+ }
14
+ function rows(value) {
15
+ if (Array.isArray(value))
16
+ return value;
17
+ const nested = record(value)?.data;
18
+ if (Array.isArray(nested))
19
+ return nested;
20
+ throw new RemoteProtocolError();
21
+ }
22
+ function nonblank(value) {
23
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
24
+ }
25
+ function permission(value) {
26
+ return typeof value === "string"
27
+ && permissions.has(value)
28
+ ? value
29
+ : undefined;
30
+ }
31
+ function privateRepositories(value) {
32
+ return rows(value).flatMap((candidate) => {
33
+ const item = record(candidate);
34
+ const repositoryId = nonblank(item?.repository_id);
35
+ const name = nonblank(item?.name) ?? nonblank(item?.title);
36
+ const type = item?.type === 1
37
+ ? "personal"
38
+ : item?.type === 6
39
+ ? "draft"
40
+ : undefined;
41
+ if (repositoryId === undefined || name === undefined || type === undefined)
42
+ return [];
43
+ return [{ repository_id: repositoryId, name, type, is_default: false }];
44
+ });
45
+ }
46
+ function teamRepositories(value) {
47
+ return rows(value).flatMap((candidate) => {
48
+ const item = record(candidate);
49
+ const repositoryId = nonblank(item?.repository_id);
50
+ const name = nonblank(item?.name) ?? nonblank(item?.title);
51
+ if (repositoryId === undefined || name === undefined)
52
+ return [];
53
+ const permissionCode = permission(item?.permission_code);
54
+ return [{
55
+ repository_id: repositoryId,
56
+ name,
57
+ type: "team",
58
+ ...(permissionCode === undefined ? {} : { permission_code: permissionCode }),
59
+ is_default: false
60
+ }];
61
+ });
62
+ }
63
+ function enterpriseRepositories(value) {
64
+ return rows(value).flatMap((candidate) => {
65
+ const item = record(candidate);
66
+ const repositoryId = nonblank(item?.id);
67
+ const name = nonblank(item?.name) ?? nonblank(item?.title);
68
+ if (repositoryId === undefined || name === undefined)
69
+ return [];
70
+ const permissionCode = permission(item?.permission_code);
71
+ return [{
72
+ repository_id: repositoryId,
73
+ name,
74
+ type: "enterprise",
75
+ ...(permissionCode === undefined ? {} : { permission_code: permissionCode }),
76
+ is_default: false
77
+ }];
78
+ });
79
+ }
80
+ export function normalizeRepositoryCatalog(privateRows, teamRows, enterpriseRows) {
81
+ const merged = [
82
+ ...privateRepositories(privateRows),
83
+ ...teamRepositories(teamRows),
84
+ ...enterpriseRepositories(enterpriseRows)
85
+ ];
86
+ const seen = new Set();
87
+ const unique = merged.filter((repository) => {
88
+ if (seen.has(repository.repository_id))
89
+ return false;
90
+ seen.add(repository.repository_id);
91
+ return true;
92
+ });
93
+ const defaultIndex = unique.findIndex((repository) => repository.type === "personal");
94
+ const selectedIndex = defaultIndex === -1 && unique.length > 0 ? 0 : defaultIndex;
95
+ return unique.map((repository, index) => ({
96
+ ...repository,
97
+ is_default: index === selectedIndex
98
+ }));
99
+ }
100
+ function requestContext(input) {
101
+ return {
102
+ credential: input.access.credential,
103
+ organizationId: input.access.organizationId,
104
+ signal: input.signal
105
+ };
106
+ }
107
+ export function createDamRepositoryCatalog(dependencies) {
108
+ return {
109
+ async list(input) {
110
+ const [privateRows, teamRows, enterpriseRows] = await Promise.all([
111
+ dependencies.transport.getJson({
112
+ path: "/tb-dam/repositories",
113
+ ...requestContext(input)
114
+ }),
115
+ dependencies.transport.getJson({
116
+ path: "/tb-dam/teams",
117
+ ...requestContext(input)
118
+ }),
119
+ dependencies.transport.getJson({
120
+ path: "/dam/org-repositories",
121
+ ...requestContext(input)
122
+ })
123
+ ]);
124
+ return normalizeRepositoryCatalog(privateRows, teamRows, enterpriseRows);
125
+ }
126
+ };
127
+ }
@@ -0,0 +1,141 @@
1
+ import { RemoteRequestError } from "../../platform/signed-http-transport.js";
2
+ import { inspectUploadFile } from "../../platform/safe-upload-file.js";
3
+ function isRecord(value) {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+ function requiredString(record, key) {
7
+ const value = record[key];
8
+ if (typeof value !== "string" || value.trim() === "")
9
+ throw new RemoteRequestError();
10
+ return value;
11
+ }
12
+ function optionalString(record, key) {
13
+ const value = record[key];
14
+ if (value === undefined)
15
+ return undefined;
16
+ if (typeof value !== "string" || value.trim() === "")
17
+ throw new RemoteRequestError();
18
+ return value;
19
+ }
20
+ function parseToken(response) {
21
+ if (!isRecord(response))
22
+ throw new RemoteRequestError();
23
+ const record = "data" in response ? response.data : response;
24
+ if (!isRecord(record))
25
+ throw new RemoteRequestError();
26
+ const standbyEndpoint = optionalString(record, "standby_endpoint");
27
+ const accelerateEndpoint = optionalString(record, "accelerate_endpoint");
28
+ const authKey = optionalString(record, "auth_key");
29
+ return {
30
+ securityToken: requiredString(record, "security_token"),
31
+ accessKeyId: requiredString(record, "access_key_id"),
32
+ accessKeySecret: requiredString(record, "access_key_secret"),
33
+ endpoint: requiredString(record, "endpoint"),
34
+ ...(standbyEndpoint === undefined ? {} : { standbyEndpoint }),
35
+ ...(accelerateEndpoint === undefined ? {} : { accelerateEndpoint }),
36
+ region: requiredString(record, "region"),
37
+ bucketName: requiredString(record, "bucket_name"),
38
+ objectName: requiredString(record, "object_name"),
39
+ host: requiredString(record, "host"),
40
+ ...(authKey === undefined ? {} : { authKey })
41
+ };
42
+ }
43
+ function publicAssetUrl(token) {
44
+ try {
45
+ const normalizedHost = token.host.includes("://") ? token.host : `https://${token.host}`;
46
+ const url = new URL(normalizedHost);
47
+ if ((url.protocol !== "http:" && url.protocol !== "https:")
48
+ || url.username !== ""
49
+ || url.password !== "") {
50
+ throw new RemoteRequestError();
51
+ }
52
+ url.search = "";
53
+ url.hash = "";
54
+ if (!url.pathname.endsWith("/"))
55
+ url.pathname += "/";
56
+ url.pathname += token.objectName
57
+ .replace(/^\/+/, "")
58
+ .split("/")
59
+ .map(encodeURIComponent)
60
+ .join("/");
61
+ if (token.authKey !== undefined)
62
+ url.searchParams.set("auth_key", token.authKey);
63
+ return url.href;
64
+ }
65
+ catch (error) {
66
+ if (error instanceof RemoteRequestError)
67
+ throw error;
68
+ throw new RemoteRequestError();
69
+ }
70
+ }
71
+ function storageToken(token) {
72
+ return {
73
+ securityToken: token.securityToken,
74
+ accessKeyId: token.accessKeyId,
75
+ accessKeySecret: token.accessKeySecret,
76
+ endpoint: token.endpoint,
77
+ ...(token.standbyEndpoint === undefined
78
+ ? {}
79
+ : { standbyEndpoint: token.standbyEndpoint }),
80
+ ...(token.accelerateEndpoint === undefined
81
+ ? {}
82
+ : { accelerateEndpoint: token.accelerateEndpoint }),
83
+ region: token.region,
84
+ bucketName: token.bucketName,
85
+ objectName: token.objectName
86
+ };
87
+ }
88
+ export function createDamStorageUploader(dependencies) {
89
+ return {
90
+ inspect(input, signal) {
91
+ return inspectUploadFile({ ...input, signal });
92
+ },
93
+ async upload(input, access, signal) {
94
+ signal.throwIfAborted();
95
+ let token;
96
+ let url;
97
+ try {
98
+ const response = await dependencies.transport.postJson({
99
+ path: "/tb-dam/asset/upload/tokens",
100
+ body: {
101
+ ...(input.contentId === undefined ? {} : { content_id: input.contentId }),
102
+ format: input.file.format
103
+ },
104
+ credential: access.credential,
105
+ organizationId: access.organizationId,
106
+ signal
107
+ });
108
+ signal.throwIfAborted();
109
+ token = parseToken(response);
110
+ url = publicAssetUrl(token);
111
+ }
112
+ catch (error) {
113
+ if (signal.aborted)
114
+ throw signal.reason;
115
+ if (error instanceof RemoteRequestError)
116
+ throw error;
117
+ throw new RemoteRequestError();
118
+ }
119
+ try {
120
+ await dependencies.storage.upload({
121
+ token: storageToken(token),
122
+ filePath: input.file.filePath,
123
+ mediaType: input.file.mediaType,
124
+ signal
125
+ });
126
+ signal.throwIfAborted();
127
+ return {
128
+ ...input.file,
129
+ url
130
+ };
131
+ }
132
+ catch (error) {
133
+ if (signal.aborted)
134
+ throw signal.reason;
135
+ if (error instanceof RemoteRequestError)
136
+ throw error;
137
+ throw new RemoteRequestError();
138
+ }
139
+ }
140
+ };
141
+ }
@@ -0,0 +1,17 @@
1
+ export function createDamTransientUploader(dependencies) {
2
+ return {
3
+ async upload(input, access, signal) {
4
+ const file = await dependencies.storageUpload.inspect({
5
+ url: input.url,
6
+ ...(input.mediaType === undefined ? {} : { claimedMediaType: input.mediaType })
7
+ }, signal);
8
+ const stored = await dependencies.storageUpload.upload({ file }, access, signal);
9
+ return {
10
+ url: stored.url,
11
+ mediaType: stored.mediaType,
12
+ filename: input.filename ?? stored.filename,
13
+ fromUserUpload: true
14
+ };
15
+ }
16
+ };
17
+ }