transloadit 4.0.7 → 4.1.2
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/CHANGELOG.md +37 -0
- package/README.md +200 -21
- package/dist/ApiError.d.ts +1 -1
- package/dist/ApiError.d.ts.map +1 -1
- package/dist/ApiError.js.map +1 -1
- package/dist/Transloadit.d.ts +23 -4
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +62 -28
- package/dist/Transloadit.js.map +1 -1
- package/dist/apiTypes.d.ts +1 -1
- package/dist/apiTypes.d.ts.map +1 -1
- package/dist/cli/OutputCtl.d.ts +46 -0
- package/dist/cli/OutputCtl.d.ts.map +1 -0
- package/dist/cli/OutputCtl.js +85 -0
- package/dist/cli/OutputCtl.js.map +1 -0
- package/dist/cli/commands/BaseCommand.d.ts +23 -0
- package/dist/cli/commands/BaseCommand.d.ts.map +1 -0
- package/dist/cli/commands/BaseCommand.js +52 -0
- package/dist/cli/commands/BaseCommand.js.map +1 -0
- package/dist/cli/commands/assemblies.d.ts +93 -0
- package/dist/cli/commands/assemblies.d.ts.map +1 -0
- package/dist/cli/commands/assemblies.js +1021 -0
- package/dist/cli/commands/assemblies.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +28 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +280 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/bills.d.ts +14 -0
- package/dist/cli/commands/bills.d.ts.map +1 -0
- package/dist/cli/commands/bills.js +69 -0
- package/dist/cli/commands/bills.js.map +1 -0
- package/dist/cli/commands/index.d.ts +3 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +39 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/notifications.d.ts +16 -0
- package/dist/cli/commands/notifications.d.ts.map +1 -0
- package/dist/cli/commands/notifications.js +44 -0
- package/dist/cli/commands/notifications.js.map +1 -0
- package/dist/cli/commands/templates.d.ts +81 -0
- package/dist/cli/commands/templates.d.ts.map +1 -0
- package/dist/cli/commands/templates.js +428 -0
- package/dist/cli/commands/templates.js.map +1 -0
- package/dist/cli/helpers.d.ts +13 -0
- package/dist/cli/helpers.d.ts.map +1 -0
- package/dist/cli/helpers.js +39 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/template-last-modified.d.ts +10 -0
- package/dist/cli/template-last-modified.d.ts.map +1 -0
- package/dist/cli/template-last-modified.js +134 -0
- package/dist/cli/template-last-modified.js.map +1 -0
- package/dist/cli/types.d.ts +152 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +64 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +2 -12
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -256
- package/dist/cli.js.map +1 -1
- package/dist/tus.d.ts +2 -1
- package/dist/tus.d.ts.map +1 -1
- package/dist/tus.js +33 -5
- package/dist/tus.js.map +1 -1
- package/package.json +12 -7
- package/src/ApiError.ts +2 -1
- package/src/Transloadit.ts +98 -39
- package/src/apiTypes.ts +1 -1
- package/src/cli/OutputCtl.ts +115 -0
- package/src/cli/commands/BaseCommand.ts +71 -0
- package/src/cli/commands/assemblies.ts +1373 -0
- package/src/cli/commands/auth.ts +354 -0
- package/src/cli/commands/bills.ts +91 -0
- package/src/cli/commands/index.ts +65 -0
- package/src/cli/commands/notifications.ts +63 -0
- package/src/cli/commands/templates.ts +556 -0
- package/src/cli/helpers.ts +50 -0
- package/src/cli/template-last-modified.ts +156 -0
- package/src/cli/types.ts +183 -0
- package/src/cli.ts +12 -305
- package/src/tus.ts +37 -5
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { Transloadit } from '../Transloadit.ts'
|
|
2
|
+
import { ensureError } from './types.ts'
|
|
3
|
+
|
|
4
|
+
interface TemplateItem {
|
|
5
|
+
id: string
|
|
6
|
+
modified: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type FetchCallback<T> = (err: Error | null, result?: T) => void
|
|
10
|
+
type PageFetcher<T> = (page: number, pagesize: number, cb: FetchCallback<T[]>) => void
|
|
11
|
+
|
|
12
|
+
class MemoizedPagination<T> {
|
|
13
|
+
private pagesize: number
|
|
14
|
+
private fetch: PageFetcher<T>
|
|
15
|
+
private cache: (T | undefined)[]
|
|
16
|
+
|
|
17
|
+
constructor(pagesize: number, fetch: PageFetcher<T>) {
|
|
18
|
+
this.pagesize = pagesize
|
|
19
|
+
this.fetch = fetch
|
|
20
|
+
this.cache = []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(i: number, cb: FetchCallback<T>): void {
|
|
24
|
+
const cached = this.cache[i]
|
|
25
|
+
if (cached !== undefined) {
|
|
26
|
+
process.nextTick(() => cb(null, cached))
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const page = Math.floor(i / this.pagesize) + 1
|
|
31
|
+
const start = (page - 1) * this.pagesize
|
|
32
|
+
|
|
33
|
+
this.fetch(page, this.pagesize, (err, result) => {
|
|
34
|
+
if (err) {
|
|
35
|
+
cb(err)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
if (!result) {
|
|
39
|
+
cb(new Error('No result returned from fetch'))
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
for (let j = 0; j < this.pagesize; j++) {
|
|
43
|
+
this.cache[start + j] = result[j]
|
|
44
|
+
}
|
|
45
|
+
cb(null, this.cache[i])
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default class ModifiedLookup {
|
|
51
|
+
private byOrdinal: MemoizedPagination<TemplateItem>
|
|
52
|
+
|
|
53
|
+
constructor(client: Transloadit, pagesize = 50) {
|
|
54
|
+
this.byOrdinal = new MemoizedPagination<TemplateItem>(pagesize, (page, pagesize, cb) => {
|
|
55
|
+
const params = {
|
|
56
|
+
sort: 'id' as const,
|
|
57
|
+
order: 'asc' as const,
|
|
58
|
+
fields: ['id', 'modified'] as ('id' | 'modified')[],
|
|
59
|
+
page,
|
|
60
|
+
pagesize,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
client
|
|
64
|
+
.listTemplates(params)
|
|
65
|
+
.then((result) => {
|
|
66
|
+
const items: TemplateItem[] = new Array(pagesize)
|
|
67
|
+
// Fill with sentinel value larger than any hex ID
|
|
68
|
+
items.fill({ id: 'gggggggggggggggggggggggggggggggg', modified: '' })
|
|
69
|
+
for (let i = 0; i < result.items.length; i++) {
|
|
70
|
+
const item = result.items[i]
|
|
71
|
+
if (item) {
|
|
72
|
+
items[i] = { id: item.id, modified: item.modified }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
cb(null, items)
|
|
76
|
+
})
|
|
77
|
+
.catch((err: unknown) => {
|
|
78
|
+
cb(ensureError(err))
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private idByOrd(ord: number, cb: FetchCallback<string>): void {
|
|
84
|
+
this.byOrdinal.get(ord, (err, result) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
cb(err)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
if (!result) {
|
|
90
|
+
cb(new Error('No result found'))
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
cb(null, result.id)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
byId(id: string, cb: FetchCallback<Date>): void {
|
|
98
|
+
const findUpperBound = (bound: number): void => {
|
|
99
|
+
this.idByOrd(bound, (err, idAtBound) => {
|
|
100
|
+
if (err) {
|
|
101
|
+
cb(err)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
if (idAtBound === id) {
|
|
105
|
+
complete(bound)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
if (idAtBound && idAtBound > id) {
|
|
109
|
+
refine(Math.floor(bound / 2), bound)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
findUpperBound(bound * 2)
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const refine = (lower: number, upper: number): void => {
|
|
117
|
+
if (lower >= upper - 1) {
|
|
118
|
+
cb(new Error(`Template ID ${id} not found in ModifiedLookup`))
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const middle = Math.floor((lower + upper) / 2)
|
|
123
|
+
this.idByOrd(middle, (err, idAtMiddle) => {
|
|
124
|
+
if (err) {
|
|
125
|
+
cb(err)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
if (idAtMiddle === id) {
|
|
129
|
+
complete(middle)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
if (idAtMiddle && idAtMiddle < id) {
|
|
133
|
+
refine(middle, upper)
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
refine(lower, middle)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const complete = (ord: number): void => {
|
|
141
|
+
this.byOrdinal.get(ord, (err, result) => {
|
|
142
|
+
if (err) {
|
|
143
|
+
cb(err)
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if (!result) {
|
|
147
|
+
cb(new Error('No result found'))
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
cb(null, new Date(result.modified))
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
findUpperBound(1)
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/cli/types.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { Steps } from '../alphalib/types/template.ts'
|
|
3
|
+
import { optionalStepsSchema } from '../alphalib/types/template.ts'
|
|
4
|
+
import type { BillResponse, ListedTemplate, TemplateResponse } from '../apiTypes.ts'
|
|
5
|
+
import type { AssemblyStatus, Transloadit } from '../Transloadit.ts'
|
|
6
|
+
import type { IOutputCtl } from './OutputCtl.ts'
|
|
7
|
+
|
|
8
|
+
// Re-export transloadit types for CLI use
|
|
9
|
+
export type { AssemblyStatus, BillResponse, ListedTemplate, TemplateResponse }
|
|
10
|
+
export type { Transloadit }
|
|
11
|
+
export type { CreateAssemblyOptions } from '../Transloadit.ts'
|
|
12
|
+
|
|
13
|
+
// Zod schemas for runtime validation
|
|
14
|
+
export const APIErrorSchema = z.object({
|
|
15
|
+
error: z.string(),
|
|
16
|
+
message: z.string(),
|
|
17
|
+
})
|
|
18
|
+
export type APIError = z.infer<typeof APIErrorSchema>
|
|
19
|
+
|
|
20
|
+
export const TransloaditAPIErrorSchema = z.object({
|
|
21
|
+
error: z.string().optional(),
|
|
22
|
+
message: z.string(),
|
|
23
|
+
code: z.string().optional(),
|
|
24
|
+
transloaditErrorCode: z.string().optional(),
|
|
25
|
+
response: z
|
|
26
|
+
.object({
|
|
27
|
+
body: z
|
|
28
|
+
.object({
|
|
29
|
+
error: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
.optional(),
|
|
32
|
+
statusCode: z.number().optional(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
})
|
|
36
|
+
export type TransloaditAPIError = z.infer<typeof TransloaditAPIErrorSchema>
|
|
37
|
+
|
|
38
|
+
// Template file data - explicit type to avoid TS inference limits
|
|
39
|
+
export interface TemplateFileData {
|
|
40
|
+
transloadit_template_id?: string
|
|
41
|
+
steps?: Steps
|
|
42
|
+
[key: string]: unknown // passthrough
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const TemplateFileDataSchema: z.ZodType<TemplateFileData> = z
|
|
46
|
+
.object({
|
|
47
|
+
transloadit_template_id: z.string().optional(),
|
|
48
|
+
steps: optionalStepsSchema,
|
|
49
|
+
})
|
|
50
|
+
.passthrough() as z.ZodType<TemplateFileData>
|
|
51
|
+
|
|
52
|
+
export interface TemplateFile {
|
|
53
|
+
file: string
|
|
54
|
+
data: TemplateFileData
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Template list item (from API)
|
|
58
|
+
export interface TemplateListItem {
|
|
59
|
+
id: string
|
|
60
|
+
modified: string
|
|
61
|
+
name?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// CLI Invocation types
|
|
65
|
+
export interface BaseInvocation {
|
|
66
|
+
error?: boolean
|
|
67
|
+
message?: string
|
|
68
|
+
mode: string
|
|
69
|
+
action?: string
|
|
70
|
+
logLevel?: number
|
|
71
|
+
jsonMode?: boolean
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface AssemblyInvocation extends BaseInvocation {
|
|
75
|
+
mode: 'assemblies'
|
|
76
|
+
action?: 'create' | 'get' | 'list' | 'delete' | 'replay'
|
|
77
|
+
inputs: string[]
|
|
78
|
+
output?: string
|
|
79
|
+
recursive?: boolean
|
|
80
|
+
watch?: boolean
|
|
81
|
+
del?: boolean
|
|
82
|
+
reprocessStale?: boolean
|
|
83
|
+
steps?: string
|
|
84
|
+
template?: string
|
|
85
|
+
fields?: Record<string, string>
|
|
86
|
+
assemblies?: string[]
|
|
87
|
+
before?: string
|
|
88
|
+
after?: string
|
|
89
|
+
keywords?: string[]
|
|
90
|
+
notify_url?: string
|
|
91
|
+
reparse?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface TemplateInvocation extends BaseInvocation {
|
|
95
|
+
mode: 'templates'
|
|
96
|
+
action?: 'create' | 'get' | 'list' | 'delete' | 'modify' | 'sync'
|
|
97
|
+
templates?: string[]
|
|
98
|
+
template?: string
|
|
99
|
+
name?: string
|
|
100
|
+
file?: string
|
|
101
|
+
files?: string[]
|
|
102
|
+
before?: string
|
|
103
|
+
after?: string
|
|
104
|
+
order?: 'asc' | 'desc'
|
|
105
|
+
sort?: string
|
|
106
|
+
fields?: string[]
|
|
107
|
+
recursive?: boolean
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface BillInvocation extends BaseInvocation {
|
|
111
|
+
mode: 'bills'
|
|
112
|
+
action?: 'get'
|
|
113
|
+
months: string[]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface NotificationInvocation extends BaseInvocation {
|
|
117
|
+
mode: 'assembly-notifications'
|
|
118
|
+
action?: 'list' | 'replay'
|
|
119
|
+
assemblies?: string[]
|
|
120
|
+
notify_url?: string
|
|
121
|
+
type?: string
|
|
122
|
+
assembly_id?: string
|
|
123
|
+
pagesize?: number
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface HelpInvocation extends BaseInvocation {
|
|
127
|
+
mode: 'help' | 'version' | 'register'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type Invocation =
|
|
131
|
+
| AssemblyInvocation
|
|
132
|
+
| TemplateInvocation
|
|
133
|
+
| BillInvocation
|
|
134
|
+
| NotificationInvocation
|
|
135
|
+
| HelpInvocation
|
|
136
|
+
|
|
137
|
+
// Command handler type
|
|
138
|
+
export type CommandHandler<T extends BaseInvocation = BaseInvocation> = (
|
|
139
|
+
output: IOutputCtl,
|
|
140
|
+
client: Transloadit | undefined,
|
|
141
|
+
invocation: T,
|
|
142
|
+
) => void | Promise<void>
|
|
143
|
+
|
|
144
|
+
// Type guard for Error
|
|
145
|
+
export function isError(value: unknown): value is Error {
|
|
146
|
+
return value instanceof Error
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Helper to ensure error is Error type
|
|
150
|
+
export function ensureError(value: unknown): Error {
|
|
151
|
+
if (value instanceof Error) {
|
|
152
|
+
return value
|
|
153
|
+
}
|
|
154
|
+
return new Error(`Non-error was thrown: ${String(value)}`)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Type guard for APIError
|
|
158
|
+
export function isAPIError(value: unknown): value is APIError {
|
|
159
|
+
return APIErrorSchema.safeParse(value).success
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Type guard for TransloaditAPIError
|
|
163
|
+
export function isTransloaditAPIError(value: unknown): value is TransloaditAPIError {
|
|
164
|
+
return TransloaditAPIErrorSchema.safeParse(value).success
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Type guard for NodeJS.ErrnoException
|
|
168
|
+
export function isErrnoException(value: unknown): value is NodeJS.ErrnoException {
|
|
169
|
+
return value instanceof Error && 'code' in value
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Safe array access helper
|
|
173
|
+
export function safeGet<T>(arr: T[], index: number): T | undefined {
|
|
174
|
+
return arr[index]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Assert defined helper
|
|
178
|
+
export function assertDefined<T>(value: T | undefined | null, message: string): T {
|
|
179
|
+
if (value === undefined || value === null) {
|
|
180
|
+
throw new Error(message)
|
|
181
|
+
}
|
|
182
|
+
return value
|
|
183
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -4,310 +4,8 @@ import { realpathSync } from 'node:fs'
|
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import process from 'node:process'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
assemblyAuthInstructionsSchema,
|
|
10
|
-
assemblyInstructionsSchema,
|
|
11
|
-
} from './alphalib/types/template.ts'
|
|
12
|
-
import type { OptionalAuthParams } from './apiTypes.ts'
|
|
13
|
-
import { Transloadit } from './Transloadit.ts'
|
|
14
|
-
|
|
15
|
-
type UrlParamPrimitive = string | number | boolean
|
|
16
|
-
type UrlParamArray = UrlParamPrimitive[]
|
|
17
|
-
type NormalizedUrlParams = Record<string, UrlParamPrimitive | UrlParamArray>
|
|
18
|
-
|
|
19
|
-
interface RunSigOptions {
|
|
20
|
-
providedInput?: string
|
|
21
|
-
algorithm?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface RunSmartSigOptions {
|
|
25
|
-
providedInput?: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const smartCdnParamsSchema = z
|
|
29
|
-
.object({
|
|
30
|
-
workspace: z.string().min(1, 'workspace is required'),
|
|
31
|
-
template: z.string().min(1, 'template is required'),
|
|
32
|
-
input: z.union([z.string(), z.number(), z.boolean()]),
|
|
33
|
-
url_params: z.record(z.unknown()).optional(),
|
|
34
|
-
expire_at_ms: z.union([z.number(), z.string()]).optional(),
|
|
35
|
-
})
|
|
36
|
-
.passthrough()
|
|
37
|
-
|
|
38
|
-
const cliSignatureParamsSchema = assemblyInstructionsSchema
|
|
39
|
-
.extend({ auth: assemblyAuthInstructionsSchema.partial().optional() })
|
|
40
|
-
.partial()
|
|
41
|
-
.passthrough()
|
|
42
|
-
|
|
43
|
-
export async function readStdin(): Promise<string> {
|
|
44
|
-
if (process.stdin.isTTY) return ''
|
|
45
|
-
|
|
46
|
-
process.stdin.setEncoding('utf8')
|
|
47
|
-
let data = ''
|
|
48
|
-
|
|
49
|
-
for await (const chunk of process.stdin) {
|
|
50
|
-
data += chunk
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return data
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function fail(message: string): void {
|
|
57
|
-
console.error(message)
|
|
58
|
-
process.exitCode = 1
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function formatIssues(issues: ZodIssue[]): string {
|
|
62
|
-
return issues
|
|
63
|
-
.map((issue) => {
|
|
64
|
-
const path = issue.path.join('.') || '(root)'
|
|
65
|
-
return `${path}: ${issue.message}`
|
|
66
|
-
})
|
|
67
|
-
.join('; ')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function normalizeUrlParam(value: unknown): UrlParamPrimitive | UrlParamArray | undefined {
|
|
71
|
-
if (value == null) return undefined
|
|
72
|
-
if (Array.isArray(value)) {
|
|
73
|
-
const normalized = value.filter(
|
|
74
|
-
(item): item is UrlParamPrimitive =>
|
|
75
|
-
typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean',
|
|
76
|
-
)
|
|
77
|
-
return normalized.length > 0 ? normalized : undefined
|
|
78
|
-
}
|
|
79
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
80
|
-
return value
|
|
81
|
-
}
|
|
82
|
-
return undefined
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function normalizeUrlParams(params?: Record<string, unknown>): NormalizedUrlParams | undefined {
|
|
86
|
-
if (params == null) return undefined
|
|
87
|
-
let normalized: NormalizedUrlParams | undefined
|
|
88
|
-
for (const [key, value] of Object.entries(params)) {
|
|
89
|
-
const normalizedValue = normalizeUrlParam(value)
|
|
90
|
-
if (normalizedValue === undefined) continue
|
|
91
|
-
if (normalized == null) normalized = {}
|
|
92
|
-
normalized[key] = normalizedValue
|
|
93
|
-
}
|
|
94
|
-
return normalized
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function ensureCredentials(): { authKey: string; authSecret: string } | null {
|
|
98
|
-
const authKey = process.env.TRANSLOADIT_KEY || process.env.TRANSLOADIT_AUTH_KEY
|
|
99
|
-
const authSecret = process.env.TRANSLOADIT_SECRET || process.env.TRANSLOADIT_AUTH_SECRET
|
|
100
|
-
|
|
101
|
-
if (!authKey || !authSecret) {
|
|
102
|
-
fail(
|
|
103
|
-
'Missing credentials. Please set TRANSLOADIT_KEY and TRANSLOADIT_SECRET environment variables.',
|
|
104
|
-
)
|
|
105
|
-
return null
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return { authKey, authSecret }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function runSig(options: RunSigOptions = {}): Promise<void> {
|
|
112
|
-
const credentials = ensureCredentials()
|
|
113
|
-
if (credentials == null) return
|
|
114
|
-
const { authKey, authSecret } = credentials
|
|
115
|
-
const { providedInput, algorithm } = options
|
|
116
|
-
|
|
117
|
-
const rawInput = providedInput ?? (await readStdin())
|
|
118
|
-
const input = rawInput.trim()
|
|
119
|
-
let params: Record<string, unknown>
|
|
120
|
-
|
|
121
|
-
if (input === '') {
|
|
122
|
-
params = { auth: { key: authKey } }
|
|
123
|
-
} else {
|
|
124
|
-
let parsed: unknown
|
|
125
|
-
try {
|
|
126
|
-
parsed = JSON.parse(input)
|
|
127
|
-
} catch (error) {
|
|
128
|
-
fail(`Failed to parse JSON from stdin: ${(error as Error).message}`)
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
133
|
-
fail('Invalid params provided via stdin. Expected a JSON object.')
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const parsedResult = cliSignatureParamsSchema.safeParse(parsed)
|
|
138
|
-
if (!parsedResult.success) {
|
|
139
|
-
fail(`Invalid params: ${formatIssues(parsedResult.error.issues)}`)
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const parsedParams = parsedResult.data as Record<string, unknown>
|
|
144
|
-
const existingAuth =
|
|
145
|
-
typeof parsedParams.auth === 'object' &&
|
|
146
|
-
parsedParams.auth != null &&
|
|
147
|
-
!Array.isArray(parsedParams.auth)
|
|
148
|
-
? (parsedParams.auth as Record<string, unknown>)
|
|
149
|
-
: {}
|
|
150
|
-
|
|
151
|
-
params = {
|
|
152
|
-
...parsedParams,
|
|
153
|
-
auth: {
|
|
154
|
-
...existingAuth,
|
|
155
|
-
key: authKey,
|
|
156
|
-
},
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const client = new Transloadit({ authKey, authSecret })
|
|
161
|
-
try {
|
|
162
|
-
const signature = client.calcSignature(params as OptionalAuthParams, algorithm)
|
|
163
|
-
process.stdout.write(`${JSON.stringify(signature)}\n`)
|
|
164
|
-
} catch (error) {
|
|
165
|
-
fail(`Failed to generate signature: ${(error as Error).message}`)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export async function runSmartSig(options: RunSmartSigOptions = {}): Promise<void> {
|
|
170
|
-
const credentials = ensureCredentials()
|
|
171
|
-
if (credentials == null) return
|
|
172
|
-
const { authKey, authSecret } = credentials
|
|
173
|
-
|
|
174
|
-
const rawInput = options.providedInput ?? (await readStdin())
|
|
175
|
-
const input = rawInput.trim()
|
|
176
|
-
if (input === '') {
|
|
177
|
-
fail(
|
|
178
|
-
'Missing params provided via stdin. Expected a JSON object with workspace, template, input, and optional Smart CDN parameters.',
|
|
179
|
-
)
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let parsed: unknown
|
|
184
|
-
try {
|
|
185
|
-
parsed = JSON.parse(input)
|
|
186
|
-
} catch (error) {
|
|
187
|
-
fail(`Failed to parse JSON from stdin: ${(error as Error).message}`)
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
192
|
-
fail('Invalid params provided via stdin. Expected a JSON object.')
|
|
193
|
-
return
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const parsedResult = smartCdnParamsSchema.safeParse(parsed)
|
|
197
|
-
if (!parsedResult.success) {
|
|
198
|
-
fail(`Invalid params: ${formatIssues(parsedResult.error.issues)}`)
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const { workspace, template, input: inputFieldRaw, url_params, expire_at_ms } = parsedResult.data
|
|
203
|
-
const urlParams = normalizeUrlParams(url_params as Record<string, unknown> | undefined)
|
|
204
|
-
|
|
205
|
-
let expiresAt: number | undefined
|
|
206
|
-
if (typeof expire_at_ms === 'string') {
|
|
207
|
-
const parsedNumber = Number.parseInt(expire_at_ms, 10)
|
|
208
|
-
if (Number.isNaN(parsedNumber)) {
|
|
209
|
-
fail('Invalid params: expire_at_ms must be a number.')
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
expiresAt = parsedNumber
|
|
213
|
-
} else {
|
|
214
|
-
expiresAt = expire_at_ms
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const inputField = typeof inputFieldRaw === 'string' ? inputFieldRaw : String(inputFieldRaw)
|
|
218
|
-
|
|
219
|
-
const client = new Transloadit({ authKey, authSecret })
|
|
220
|
-
try {
|
|
221
|
-
const signedUrl = client.getSignedSmartCDNUrl({
|
|
222
|
-
workspace,
|
|
223
|
-
template,
|
|
224
|
-
input: inputField,
|
|
225
|
-
urlParams,
|
|
226
|
-
expiresAt,
|
|
227
|
-
})
|
|
228
|
-
process.stdout.write(`${signedUrl}\n`)
|
|
229
|
-
} catch (error) {
|
|
230
|
-
fail(`Failed to generate Smart CDN URL: ${(error as Error).message}`)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function parseSigArguments(args: string[]): { algorithm?: string } {
|
|
235
|
-
let algorithm: string | undefined
|
|
236
|
-
let index = 0
|
|
237
|
-
while (index < args.length) {
|
|
238
|
-
const arg = args[index]
|
|
239
|
-
if (arg === '--algorithm' || arg === '-a') {
|
|
240
|
-
const next = args[index + 1]
|
|
241
|
-
if (next == null || next.startsWith('-')) {
|
|
242
|
-
throw new Error('Missing value for --algorithm option')
|
|
243
|
-
}
|
|
244
|
-
algorithm = next
|
|
245
|
-
index += 2
|
|
246
|
-
continue
|
|
247
|
-
}
|
|
248
|
-
if (arg.startsWith('--algorithm=')) {
|
|
249
|
-
const [, value] = arg.split('=', 2)
|
|
250
|
-
if (value === undefined || value === '') {
|
|
251
|
-
throw new Error('Missing value for --algorithm option')
|
|
252
|
-
}
|
|
253
|
-
algorithm = value
|
|
254
|
-
index += 1
|
|
255
|
-
continue
|
|
256
|
-
}
|
|
257
|
-
throw new Error(`Unknown option: ${arg}`)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return { algorithm }
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export async function main(args = process.argv.slice(2)): Promise<void> {
|
|
264
|
-
const [command, ...commandArgs] = args
|
|
265
|
-
|
|
266
|
-
switch (command) {
|
|
267
|
-
case 'smart_sig': {
|
|
268
|
-
await runSmartSig()
|
|
269
|
-
break
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
case 'sig': {
|
|
273
|
-
try {
|
|
274
|
-
const { algorithm } = parseSigArguments(commandArgs)
|
|
275
|
-
await runSig({ algorithm })
|
|
276
|
-
} catch (error) {
|
|
277
|
-
fail((error as Error).message)
|
|
278
|
-
}
|
|
279
|
-
break
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
case '-h':
|
|
283
|
-
case '--help':
|
|
284
|
-
case undefined: {
|
|
285
|
-
process.stdout.write(
|
|
286
|
-
[
|
|
287
|
-
'Usage:',
|
|
288
|
-
' npx transloadit smart_sig Read Smart CDN params JSON from stdin and output a signed URL.',
|
|
289
|
-
' npx transloadit sig [--algorithm <name>] Read params JSON from stdin and output signed payload JSON.',
|
|
290
|
-
'',
|
|
291
|
-
'Required JSON fields:',
|
|
292
|
-
' smart_sig: workspace, template, input',
|
|
293
|
-
' sig: none (object is optional)',
|
|
294
|
-
'Optional JSON fields:',
|
|
295
|
-
' smart_sig: expire_at_ms, url_params',
|
|
296
|
-
' sig: auth.expires and any supported assembly params',
|
|
297
|
-
'',
|
|
298
|
-
'Environment variables:',
|
|
299
|
-
' TRANSLOADIT_KEY, TRANSLOADIT_SECRET',
|
|
300
|
-
].join('\n'),
|
|
301
|
-
)
|
|
302
|
-
if (command === undefined) process.exitCode = 1
|
|
303
|
-
break
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
default: {
|
|
307
|
-
fail(`Unknown command: ${command}`)
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
7
|
+
import 'dotenv/config'
|
|
8
|
+
import { createCli } from './cli/commands/index.ts'
|
|
311
9
|
|
|
312
10
|
const currentFile = realpathSync(fileURLToPath(import.meta.url))
|
|
313
11
|
|
|
@@ -326,11 +24,20 @@ export function shouldRunCli(invoked?: string): boolean {
|
|
|
326
24
|
return resolved === currentFile
|
|
327
25
|
}
|
|
328
26
|
|
|
27
|
+
export async function main(args = process.argv.slice(2)): Promise<void> {
|
|
28
|
+
const cli = createCli()
|
|
29
|
+
const exitCode = await cli.run(args)
|
|
30
|
+
if (exitCode !== 0) {
|
|
31
|
+
process.exitCode = exitCode
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
329
35
|
export function runCliWhenExecuted(): void {
|
|
330
36
|
if (!shouldRunCli(process.argv[1])) return
|
|
331
37
|
|
|
332
38
|
void main().catch((error) => {
|
|
333
|
-
|
|
39
|
+
console.error((error as Error).message)
|
|
40
|
+
process.exitCode = 1
|
|
334
41
|
})
|
|
335
42
|
}
|
|
336
43
|
|