Skip to content

Commit 2ab4ad0

Browse files
committed
w
1 parent 08f0a83 commit 2ab4ad0

File tree

6 files changed

+96
-28
lines changed

6 files changed

+96
-28
lines changed

src/alphalib/types/assemblyStatus.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,24 @@ export function hasError(
680680
return errorExists
681681
}
682682

683+
export function hasSpecificError(
684+
assembly: AssemblyStatus | null | undefined,
685+
errorType: string,
686+
): boolean {
687+
if (!assembly) return false
688+
689+
if (hasError(assembly) && assembly.error === errorType) {
690+
return true
691+
}
692+
693+
if (typeof assembly === 'object' && assembly !== null && errorType in assembly) {
694+
const candidate = Reflect.get(assembly, errorType)
695+
return Boolean(candidate)
696+
}
697+
698+
return false
699+
}
700+
683701
/**
684702
* Type guard to check if an assembly has an ok status
685703
*/

src/alphalib/types/robots/_instructions-primitives.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,25 @@ export const robotMetaSchema = z.object({
211211

212212
export type RobotMetaInput = z.input<typeof robotMetaSchema>
213213

214-
export const interpolationSchemaFull = z
215-
.string()
216-
.regex(/^\${.+}$/, 'Must be a full interpolation string')
217-
export const interpolationSchemaPartial = z
218-
.string()
219-
.regex(/\${.+}/, 'Must be a partially interpolatable string')
214+
// These schemas can be reproduced with z.string().regex(). However, this causes some issues.
215+
// We use this in combination with unions. Internally Zod normalizes unions. A string schema and
216+
// enums merged in some way. Both are validated. Normally, if a Zod union has errors, all of them
217+
// are surfaced. However, if the regex isn’t match, and none of the enum values overlap, then the
218+
// regex error is raised instead of the union error. As a result, the best error we could give back
219+
// to the user, is that there’s a problem with the interpolation syntax. But really the other error
220+
// is more useful in pretty much every case. To work around this, we use z.custom() instead, as Zod
221+
// can’t normalize that.
222+
const interpolationRegexFull = /^\${.+}$/
223+
export const interpolationSchemaFull = z.custom<`\${${string}}`>(
224+
(input) => typeof input === 'string' && interpolationRegexFull.test(input),
225+
'Must be a full interpolation string',
226+
)
227+
const interpolationRegexPartial = /\${.+}/
228+
export const interpolationSchemaPartial = z.custom<string>(
229+
(input) => typeof input === 'string' && interpolationRegexPartial.test(input),
230+
'Must be a partially interpolatable string',
231+
)
232+
220233
export const booleanStringSchema = z.enum(['true', 'false'])
221234

222235
type InterpolatableTuple<Schemas extends readonly z.ZodTypeAny[]> = Schemas extends readonly [
@@ -277,9 +290,12 @@ export function interpolateRecursive<Schema extends z.ZodFirstPartySchemaTypes>(
277290

278291
switch (def.typeName) {
279292
case z.ZodFirstPartyTypeKind.ZodBoolean:
280-
return z
281-
.union([interpolationSchemaFull, schema, booleanStringSchema])
282-
.transform((value) => value === true || value === false) as InterpolatableSchema<Schema>
293+
return z.union([
294+
interpolationSchemaFull,
295+
z
296+
.union([schema, booleanStringSchema])
297+
.transform((value) => value === true || value === false),
298+
]) as InterpolatableSchema<Schema>
283299
case z.ZodFirstPartyTypeKind.ZodArray: {
284300
let replacement = z.array(interpolateRecursive(def.type), def)
285301

@@ -631,7 +647,7 @@ export const robotFFmpeg = z.object({
631647
shortest: z.boolean().nullish(),
632648
filter_complex: z.union([z.string(), z.record(z.string())]).optional(),
633649
'level:v': z.union([z.string(), z.number()]).optional(),
634-
'profile:v': z.union([z.number(), z.enum(['baseline', 'main', 'high'])]).optional(),
650+
'profile:v': z.union([z.number(), z.enum(['baseline', 'main', 'high', 'main10'])]).optional(),
635651
'qscale:a': z.number().optional(),
636652
'qscale:v': z.number().optional(),
637653
'x264-params': z.string().optional(),

src/alphalib/types/robots/document-thumbs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ If you set this to \`false\`, the robot will not emit files as they become avail
172172
173173
Also, extracted pages will be resized a lot faster as they are sent off to other machines for the resizing. This is especially useful for large documents with many pages to get up to 20 times faster processing.
174174
175-
Turbo mode increases pricing, though, in that the input document's file size is added for every extracted page. There are no performance benefits nor increased charges for single-page documents.
175+
Turbo Mode increases pricing, though, in that the input document's file size is added for every extracted page. There are no performance benefits nor increased charges for single-page documents.
176176
`),
177177
})
178178
.strict()

src/alphalib/types/robots/file-serve.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,40 @@ While theoretically possible, you could use [🤖/file/serve](/docs/robots/file-
4242
4343
Also consider configuring caching headers and cache-control directives to control how content is cached and invalidated on the CDN edge servers, balancing between freshness and efficiency.
4444
45-
More information on:
45+
## Smart CDN Security with Signature Authentication
4646
47-
- [Content Delivery](/services/content-delivery/).
48-
- [🤖/file/serve](/docs/robots/file-serve/) pricing.
49-
- [🤖/tlcdn/deliver](/docs/robots/tlcdn-deliver/) pricing.
50-
- [File Preview Feature](/blog/2024/06/file-preview-with-smart-cdn/) blog post.
47+
You can leverage [Signature Authentication](/docs/api/authentication/#smart-cdn) to avoid abuse of our encoding platform. Below is a quick Node.js example using our Node SDK, but there are [examples for other languages and SDKs](/docs/api/authentication/#example-code) as well.
48+
49+
\`\`\`javascript
50+
// yarn add transloadit
51+
// or
52+
// npm install --save transloadit
53+
54+
import { Transloadit } from 'transloadit'
55+
56+
const transloadit = new Transloadit({
57+
authKey: 'YOUR_TRANSLOADIT_KEY',
58+
authSecret: 'YOUR_TRANSLOADIT_SECRET',
59+
})
60+
61+
const url = transloadit.getSignedSmartCDNUrl({
62+
workspace: 'YOUR_WORKSPACE',
63+
template: 'YOUR_TEMPLATE',
64+
input: 'image.png',
65+
urlParams: { height: 100, width: 100 },
66+
})
67+
68+
console.log(url)
69+
\`\`\`
70+
71+
This will generate a signed Smart CDN URL that includes authentication parameters, preventing unauthorized access to your transformation endpoints.
72+
73+
## More information
74+
75+
- [Content Delivery](/services/content-delivery/)
76+
- [🤖/file/serve](/docs/robots/file-serve/) pricing
77+
- [🤖/tlcdn/deliver](/docs/robots/tlcdn-deliver/) pricing
78+
- [File Preview Feature](/blog/2024/06/file-preview-with-smart-cdn/) blog post
5179
`),
5280
headers: z
5381
.record(z.string())

src/alphalib/types/robots/s3-store.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ Set to \`true\` if you use a custom host and run into access denied errors.
149149
.optional()
150150
.describe(`
151151
This parameter provides signed URLs in the result JSON (in the \`signed_url\` and \`signed_ssl_url\` properties). The number that you set this parameter to is the URL expiry time in seconds. If this parameter is not used, no URL signing is done.
152+
`),
153+
session_token: z
154+
.string()
155+
.optional()
156+
.describe(`
157+
The session token to use for the S3 store. This is only used if the credentials are from an IAM user with the \`sts:AssumeRole\` permission.
152158
`),
153159
})
154160
.strict()

src/alphalib/zodParseWithContext.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export function zodParseWithContext<T extends z.ZodType>(
113113
else if ('unionErrors' in zodIssue && zodIssue.unionErrors) {
114114
// --- Moved initialization out of the loop ---
115115
const collectedLiterals: Record<string, (string | number | boolean)[]> = {}
116-
const collectedMessages: Record<string, string[]> = {}
116+
const collectedMessages: Record<string, Set<string>> = {}
117117

118118
// Process nested issues within the union
119119
for (const unionError of zodIssue.unionErrors) {
@@ -123,8 +123,11 @@ export function zodParseWithContext<T extends z.ZodType>(
123123

124124
// Ensure paths exist in collection maps
125125
if (!collectedLiterals[nestedPath]) collectedLiterals[nestedPath] = []
126-
if (!collectedMessages[nestedPath]) collectedMessages[nestedPath] = []
126+
if (!collectedMessages[nestedPath]) collectedMessages[nestedPath] = new Set()
127127

128+
if (issue.code === 'custom' && issue.message.includes('interpolation string')) {
129+
continue
130+
}
128131
if (issue.code === 'invalid_literal') {
129132
const { expected } = issue
130133
if (
@@ -137,23 +140,23 @@ export function zodParseWithContext<T extends z.ZodType>(
137140
collectedLiterals[nestedPath].push(expected)
138141
}
139142
// Still add the raw message for fallback
140-
collectedMessages[nestedPath].push(issue.message)
143+
collectedMessages[nestedPath].add(issue.message)
141144
}
142145
// Keep existing enum handling if needed, but literal should cover most cases
143146
else if (issue.code === 'invalid_enum_value') {
144147
const { options } = issue
145148
if (options && options.length > 0) {
146149
collectedLiterals[nestedPath].push(...options.map(String)) // Assuming options are compatible
147150
}
148-
collectedMessages[nestedPath].push(issue.message)
151+
collectedMessages[nestedPath].add(issue.message)
149152
}
150153
// Keep existing unrecognized keys handling
151154
else if (issue.code === 'unrecognized_keys') {
152155
const maxKeysToShow = 3
153156
const { keys } = issue
154157
const truncatedKeys = keys.slice(0, maxKeysToShow)
155158
const ellipsis = keys.length > maxKeysToShow ? '...' : ''
156-
collectedMessages[nestedPath].push(
159+
collectedMessages[nestedPath].add(
157160
`has unrecognized keys: ${truncatedKeys.map((k) => `\`${k}\``).join(', ')}${ellipsis}`,
158161
)
159162
}
@@ -177,13 +180,13 @@ export function zodParseWithContext<T extends z.ZodType>(
177180
}
178181
}
179182

180-
collectedMessages[nestedPath].push(
183+
collectedMessages[nestedPath].add(
181184
`got invalid type: ${received} (value: \`${actualValueStr}\`, expected: ${expectedOutput})`,
182185
)
183186
}
184187
// <-- End added handling -->
185188
else {
186-
collectedMessages[nestedPath].push(issue.message) // Handle other nested codes
189+
collectedMessages[nestedPath].add(issue.message) // Handle other nested codes
187190
}
188191
}
189192
}
@@ -201,10 +204,10 @@ export function zodParseWithContext<T extends z.ZodType>(
201204
}
202205

203206
// Prioritize more specific messages (like invalid type with details)
204-
const invalidTypeMessages = collectedMessages[nestedPath].filter((m) =>
207+
const invalidTypeMessages = Array.from(collectedMessages[nestedPath]).filter((m) =>
205208
m.startsWith('got invalid type:'),
206209
)
207-
const unrecognizedKeyMessages = collectedMessages[nestedPath].filter((m) =>
210+
const unrecognizedKeyMessages = Array.from(collectedMessages[nestedPath]).filter((m) =>
208211
m.startsWith('has unrecognized keys:'),
209212
)
210213
const literalMessages = collectedLiterals[nestedPath] ?? []
@@ -221,9 +224,6 @@ export function zodParseWithContext<T extends z.ZodType>(
221224
targetMessages.push(...collectedMessages[nestedPath])
222225
}
223226
}
224-
225-
// Prevent the main `messages` array from being populated further for this union issue
226-
continue // Skip adding messages directly from the top-level union issue itself
227227
}
228228
// Handle other specific error codes (only if not handled above)
229229
else {

0 commit comments

Comments
 (0)