@drawcall/market 0.1.5 → 0.1.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.
Files changed (99) hide show
  1. package/dist/asset-implementation.d.ts +88 -0
  2. package/dist/asset-implementation.d.ts.map +1 -0
  3. package/dist/asset-implementation.js +2 -0
  4. package/dist/asset-implementation.js.map +1 -0
  5. package/dist/cli-client.d.ts +2 -2
  6. package/dist/cli-client.d.ts.map +1 -1
  7. package/dist/cli-client.js +4 -4
  8. package/dist/cli-client.js.map +1 -1
  9. package/dist/cli.js +104 -22
  10. package/dist/cli.js.map +1 -1
  11. package/dist/client.d.ts +1 -3
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +4 -12
  14. package/dist/client.js.map +1 -1
  15. package/dist/commands/generate.d.ts.map +1 -1
  16. package/dist/commands/generate.js +9 -4
  17. package/dist/commands/generate.js.map +1 -1
  18. package/dist/commands/install.d.ts.map +1 -1
  19. package/dist/commands/install.js +30 -28
  20. package/dist/commands/install.js.map +1 -1
  21. package/dist/commands/logout.js +3 -3
  22. package/dist/commands/logout.js.map +1 -1
  23. package/dist/commands/preview.d.ts +9 -0
  24. package/dist/commands/preview.d.ts.map +1 -0
  25. package/dist/commands/preview.js +50 -0
  26. package/dist/commands/preview.js.map +1 -0
  27. package/dist/commands/search.d.ts +4 -1
  28. package/dist/commands/search.d.ts.map +1 -1
  29. package/dist/commands/search.js +20 -10
  30. package/dist/commands/search.js.map +1 -1
  31. package/dist/commands/upload.d.ts +9 -0
  32. package/dist/commands/upload.d.ts.map +1 -0
  33. package/dist/commands/upload.js +220 -0
  34. package/dist/commands/upload.js.map +1 -0
  35. package/dist/config.d.ts +4 -7
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +8 -4
  38. package/dist/config.js.map +1 -1
  39. package/dist/constants.d.ts.map +1 -1
  40. package/dist/constants.js +2 -10
  41. package/dist/constants.js.map +1 -1
  42. package/dist/contract.d.ts +37 -134
  43. package/dist/contract.d.ts.map +1 -1
  44. package/dist/contract.js +10 -48
  45. package/dist/contract.js.map +1 -1
  46. package/dist/generate.d.ts +0 -2
  47. package/dist/generate.d.ts.map +1 -1
  48. package/dist/generate.js +5 -22
  49. package/dist/generate.js.map +1 -1
  50. package/dist/index.d.ts +5 -6
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +2 -3
  53. package/dist/index.js.map +1 -1
  54. package/dist/install.d.ts +1 -1
  55. package/dist/install.d.ts.map +1 -1
  56. package/dist/install.js +14 -11
  57. package/dist/install.js.map +1 -1
  58. package/dist/output.d.ts +26 -0
  59. package/dist/output.d.ts.map +1 -0
  60. package/dist/output.js +52 -0
  61. package/dist/output.js.map +1 -0
  62. package/dist/resolve.d.ts.map +1 -1
  63. package/dist/resolve.js +15 -26
  64. package/dist/resolve.js.map +1 -1
  65. package/dist/schemas.d.ts +31 -39
  66. package/dist/schemas.d.ts.map +1 -1
  67. package/dist/schemas.js +23 -25
  68. package/dist/schemas.js.map +1 -1
  69. package/package.json +13 -4
  70. package/src/asset-implementation.ts +114 -0
  71. package/src/cli-client.ts +7 -6
  72. package/src/cli.ts +144 -29
  73. package/src/client.ts +5 -15
  74. package/src/commands/generate.ts +9 -6
  75. package/src/commands/install.ts +34 -31
  76. package/src/commands/logout.ts +3 -3
  77. package/src/commands/preview.ts +69 -0
  78. package/src/commands/search.ts +27 -11
  79. package/src/commands/upload.ts +262 -0
  80. package/src/config.ts +11 -5
  81. package/src/constants.ts +2 -10
  82. package/src/contract.ts +22 -120
  83. package/src/generate.ts +5 -29
  84. package/src/index.ts +23 -14
  85. package/src/install.ts +24 -14
  86. package/src/output.ts +76 -0
  87. package/src/resolve.ts +22 -38
  88. package/src/schemas.ts +26 -27
  89. package/tsconfig.json +2 -1
  90. package/dist/commands/login.d.ts +0 -10
  91. package/dist/commands/login.d.ts.map +0 -1
  92. package/dist/commands/login.js +0 -92
  93. package/dist/commands/login.js.map +0 -1
  94. package/dist/internal-contract.d.ts +0 -19
  95. package/dist/internal-contract.d.ts.map +0 -1
  96. package/dist/internal-contract.js +0 -19
  97. package/dist/internal-contract.js.map +0 -1
  98. package/src/commands/login.ts +0 -113
  99. package/src/internal-contract.ts +0 -26
@@ -0,0 +1,262 @@
1
+ import * as fs from 'fs/promises'
2
+ import * as path from 'path'
3
+ import { zipSync } from 'fflate'
4
+ import ora from 'ora'
5
+ import semver from 'semver'
6
+ import { getCliClient } from '../cli-client.js'
7
+ import { unchangedUploadResult, uploadResult } from '../output.js'
8
+ import { assetNameSchema, semverSchema, type AssetType } from '../schemas.js'
9
+
10
+ export interface UploadCommandOptions {
11
+ type?: AssetType
12
+ version?: string
13
+ cwd?: string
14
+ baseUrl?: string
15
+ }
16
+
17
+ export async function uploadCommand(
18
+ name: string,
19
+ fileFilter: string,
20
+ description: string,
21
+ opts: UploadCommandOptions,
22
+ ): Promise<void> {
23
+ const spinner = ora({
24
+ text: 'Preparing upload',
25
+ isEnabled: Boolean(process.stderr.isTTY),
26
+ isSilent: !process.stderr.isTTY,
27
+ }).start()
28
+
29
+ try {
30
+ const parsedName = assetNameSchema.parse(name)
31
+ const parsedVersion = opts.version ? semverSchema.parse(opts.version) : undefined
32
+ const cwd = opts.cwd ?? process.cwd()
33
+ const file = await resolveOneFile(cwd, fileFilter)
34
+ const zip = await buildModelZip(file, description)
35
+ const { client } = await getCliClient({ baseUrl: opts.baseUrl, requireAuth: true })
36
+ const profile = await client.user.getProfile()
37
+ if (!profile) {
38
+ throw new Error('Not logged in. Run `market login` first.')
39
+ }
40
+ const existing = await client.asset.exact({
41
+ name: parsedName,
42
+ type: opts.type ?? 'model',
43
+ includeUnapproved: true,
44
+ })
45
+
46
+ if (existing && existing.ownerId !== profile.id) {
47
+ throw new Error(`Asset "${parsedName}" already exists and is owned by another user`)
48
+ }
49
+
50
+ if (existing) {
51
+ const latestZip = await client.asset.downloadZip({
52
+ name: parsedName,
53
+ version: existing.latestVersion,
54
+ })
55
+ const latestBytes = new Uint8Array(await latestZip.arrayBuffer())
56
+ if (existing.description === description && bytesEqual(latestBytes, zip)) {
57
+ spinner.stop()
58
+ console.log(unchangedUploadResult(parsedName, existing.latestVersion))
59
+ return
60
+ }
61
+ }
62
+
63
+ const version = parsedVersion ?? nextVersion(existing?.latestVersion)
64
+ spinner.text = `Uploading ${parsedName}@${version}`
65
+ const uploaded = await client.asset.uploadZip({
66
+ name: parsedName,
67
+ type: opts.type ?? 'model',
68
+ version,
69
+ description,
70
+ npmDependencies: {},
71
+ assetDependencies: {},
72
+ tags: [],
73
+ zip: new File([toArrayBuffer(zip)], `${parsedName}-${version}.zip`, {
74
+ type: 'application/zip',
75
+ }),
76
+ })
77
+
78
+ spinner.stop()
79
+ console.log(uploadResult(parsedName, uploaded.version))
80
+ } catch (err) {
81
+ spinner.stop()
82
+ throw err
83
+ }
84
+ }
85
+
86
+ async function buildModelZip(file: string, description: string): Promise<Uint8Array> {
87
+ const name = path.basename(file)
88
+ if (!/\.(glb|gltf)$/i.test(name)) {
89
+ throw new Error('Upload file filter must resolve to a .glb or .gltf file')
90
+ }
91
+
92
+ const files: Record<string, Uint8Array> = {
93
+ [`public/${name}`]: new Uint8Array(await fs.readFile(file)),
94
+ 'README.md': new TextEncoder().encode(description),
95
+ }
96
+
97
+ if (/\.gltf$/i.test(name)) {
98
+ for (const dependency of await gltfDependencies(file)) {
99
+ files[`public/${dependency.publicPath}`] = new Uint8Array(
100
+ await fs.readFile(dependency.source),
101
+ )
102
+ }
103
+ }
104
+
105
+ return zipSync(files, { mtime: new Date('1980-01-01T00:00:00.000Z'), level: 0 })
106
+ }
107
+
108
+ interface GltfDependency {
109
+ source: string
110
+ publicPath: string
111
+ }
112
+
113
+ async function gltfDependencies(file: string): Promise<GltfDependency[]> {
114
+ const gltf = JSON.parse(await fs.readFile(file, 'utf-8')) as {
115
+ buffers?: Array<{ uri?: string }>
116
+ images?: Array<{ uri?: string }>
117
+ }
118
+ const uris = [...(gltf.buffers ?? []), ...(gltf.images ?? [])]
119
+ .map((entry) => entry.uri)
120
+ .filter((uri): uri is string => Boolean(uri && !uri.startsWith('data:')))
121
+ const dependencies = new Map<string, GltfDependency>()
122
+
123
+ for (const uri of uris) {
124
+ const decodedUri = decodeURIComponent(uri)
125
+ const publicPath = toPublicDependencyPath(decodedUri)
126
+ dependencies.set(publicPath, {
127
+ source: await resolveGltfDependency(file, decodedUri),
128
+ publicPath,
129
+ })
130
+ }
131
+
132
+ return [...dependencies.values()]
133
+ }
134
+
135
+ async function resolveGltfDependency(file: string, uri: string): Promise<string> {
136
+ const dir = path.dirname(file)
137
+ const direct = path.resolve(dir, uri)
138
+ if (await isFile(direct)) return direct
139
+
140
+ const basename = path.basename(uri)
141
+ const alternateBasenames = [basename]
142
+ const withoutBlenderSuffix = basename.replace(/_\d{3}(\.[^.]+)$/u, '$1')
143
+ if (withoutBlenderSuffix !== basename) {
144
+ alternateBasenames.push(withoutBlenderSuffix)
145
+ }
146
+ let current = dir
147
+ for (let depth = 0; depth < 8; depth += 1) {
148
+ for (const candidateBasename of alternateBasenames) {
149
+ const candidates = [
150
+ path.join(current, 'Textures', candidateBasename),
151
+ path.join(current, 'textures', candidateBasename),
152
+ path.join(current, candidateBasename),
153
+ ]
154
+ for (const candidate of candidates) {
155
+ if (await isFile(candidate)) return candidate
156
+ }
157
+ }
158
+
159
+ const parent = path.dirname(current)
160
+ if (parent === current) break
161
+ current = parent
162
+ }
163
+
164
+ throw new Error(`Could not find glTF dependency "${uri}" for ${path.basename(file)}`)
165
+ }
166
+
167
+ async function isFile(file: string): Promise<boolean> {
168
+ return (await maybeStat(file))?.isFile() ?? false
169
+ }
170
+
171
+ function toPublicDependencyPath(uri: string): string {
172
+ if (/^[a-z]+:/i.test(uri) || path.isAbsolute(uri)) {
173
+ throw new Error(`Unsupported external glTF dependency "${uri}"`)
174
+ }
175
+
176
+ const normalized = path
177
+ .normalize(uri)
178
+ .split(path.sep)
179
+ .filter((part) => part.length > 0 && part !== '.')
180
+ .join('/')
181
+ if (!normalized || normalized.startsWith('../') || normalized === '..') {
182
+ throw new Error(`Unsupported glTF dependency path "${uri}"`)
183
+ }
184
+ return normalized
185
+ }
186
+
187
+ async function resolveOneFile(cwd: string, fileFilter: string): Promise<string> {
188
+ const absolute = path.resolve(cwd, fileFilter)
189
+ const stat = await maybeStat(absolute)
190
+ if (stat?.isFile()) return absolute
191
+
192
+ const files = await listFiles(cwd)
193
+ const matches = files
194
+ .filter((file) => matchesFilter(path.relative(cwd, file), fileFilter))
195
+ .filter((file) => /\.(glb|gltf)$/i.test(file))
196
+ .sort()
197
+
198
+ if (matches.length === 0) {
199
+ throw new Error(`No .glb or .gltf files matched "${fileFilter}"`)
200
+ }
201
+ if (matches.length > 1) {
202
+ throw new Error(`File filter matched ${matches.length} models; upload one asset at a time`)
203
+ }
204
+
205
+ return matches[0]
206
+ }
207
+
208
+ async function maybeStat(file: string) {
209
+ try {
210
+ return await fs.stat(file)
211
+ } catch {
212
+ return null
213
+ }
214
+ }
215
+
216
+ async function listFiles(dir: string): Promise<string[]> {
217
+ const entries = await fs.readdir(dir, { withFileTypes: true })
218
+ const files: string[] = []
219
+ for (const entry of entries) {
220
+ if (entry.name === 'node_modules' || entry.name === '.git') continue
221
+ const fullPath = path.join(dir, entry.name)
222
+ if (entry.isDirectory()) {
223
+ files.push(...(await listFiles(fullPath)))
224
+ } else if (entry.isFile()) {
225
+ files.push(fullPath)
226
+ }
227
+ }
228
+ return files
229
+ }
230
+
231
+ function matchesFilter(file: string, filter: string): boolean {
232
+ const normalizedFile = file.split(path.sep).join('/')
233
+ const normalizedFilter = filter.split(path.sep).join('/')
234
+ const pattern =
235
+ '^' +
236
+ escapeRegExp(normalizedFilter)
237
+ .replace(/\\\*\\\*/g, '.*')
238
+ .replace(/\\\*/g, '[^/]*') +
239
+ '$'
240
+ return new RegExp(pattern).test(normalizedFile)
241
+ }
242
+
243
+ function nextVersion(latest?: string): string {
244
+ if (!latest) return '1.0.0'
245
+ return semver.inc(latest, 'patch') ?? '1.0.0'
246
+ }
247
+
248
+ function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
249
+ if (a.length !== b.length) return false
250
+ for (let i = 0; i < a.length; i += 1) {
251
+ if (a[i] !== b[i]) return false
252
+ }
253
+ return true
254
+ }
255
+
256
+ function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
257
+ return new Uint8Array(bytes).buffer
258
+ }
259
+
260
+ function escapeRegExp(value: string): string {
261
+ return value.replace(/[|\\{}()[\]^$+?.*]/g, '\\$&')
262
+ }
package/src/config.ts CHANGED
@@ -3,12 +3,16 @@ import * as os from 'os'
3
3
  import * as path from 'path'
4
4
  import { z } from 'zod'
5
5
 
6
- const configSchema = z.object({
7
- apiKey: z.string().startsWith('mk_'),
6
+ const rawConfigSchema = z.object({
7
+ authToken: z.string().min(1).optional(),
8
+ apiKey: z.string().min(1).optional(),
8
9
  baseUrl: z.string().url().optional(),
9
10
  })
10
11
 
11
- export type Config = z.infer<typeof configSchema>
12
+ export interface Config {
13
+ authToken: string
14
+ baseUrl?: string
15
+ }
12
16
 
13
17
  function configDir(): string {
14
18
  if (process.platform === 'win32' && process.env.APPDATA) {
@@ -25,8 +29,10 @@ function configPath(): string {
25
29
  export async function loadConfig(): Promise<Config | null> {
26
30
  try {
27
31
  const raw = await fs.readFile(configPath(), 'utf-8')
28
- const parsed = configSchema.safeParse(JSON.parse(raw))
29
- return parsed.success ? parsed.data : null
32
+ const parsed = rawConfigSchema.safeParse(JSON.parse(raw))
33
+ if (!parsed.success) return null
34
+ const authToken = parsed.data.authToken ?? parsed.data.apiKey
35
+ return authToken ? { authToken, baseUrl: parsed.data.baseUrl } : null
30
36
  } catch {
31
37
  return null
32
38
  }
package/src/constants.ts CHANGED
@@ -3,17 +3,9 @@ import type { AssetType } from './schemas.js'
3
3
  export const MAX_FILE_SIZE = 100 * 1024 * 1024 // 100MB
4
4
 
5
5
  export const ALLOWED_EXTENSIONS: Record<AssetType, string[]> = {
6
- generic: ['.zip'],
7
- model: ['.gltf', '.glb'],
8
- hdri: ['.hdr', '.exr'],
9
- material: [], // material is submitted as JSON, no file upload
10
- music: ['.mp3', '.wav', '.ogg', '.flac'],
6
+ model: ['.zip'],
11
7
  }
12
8
 
13
9
  export const ASSET_TYPE_LABELS: Record<AssetType, string> = {
14
- generic: 'Generic',
15
- model: '3D Model',
16
- hdri: 'HDRI',
17
- material: 'Material',
18
- music: 'Audio',
10
+ model: 'Model',
19
11
  }
package/src/contract.ts CHANGED
@@ -1,38 +1,27 @@
1
1
  import { oc } from '@orpc/contract'
2
2
  import { z } from 'zod'
3
3
  import {
4
- assetNameSchema,
5
- assetTypeSchema,
6
- semverSchema,
4
+ downloadZipSchema,
5
+ exactAssetSchema,
6
+ generateAssetSchema,
7
7
  listAssetsSchema,
8
8
  updateProfileSchema,
9
- uploadGenericSchema,
10
- uploadTypedSchema,
11
- uploadMaterialSchema,
9
+ uploadZipSchema,
12
10
  } from './schemas.js'
13
11
 
14
- // ─── Output types ─────────────────────────────────────────────────────────────
15
- // These mirror what the server handlers return (Drizzle query results).
16
- // We use z.custom<T>() so the contract carries full types for the client
17
- // without duplicating every DB column as a Zod field.
18
-
19
12
  export interface AssetVersion {
20
- id: number
21
- assetId: number
13
+ id: string
14
+ assetId: string
22
15
  version: string
23
16
  approved: boolean
24
17
  npmDependencies: string
25
18
  assetDependencies: string
26
19
  sourceKey: string
27
- buildOutputKey: string | null
28
- thumbnailKey: string | null
29
- buildError: string | null
30
- readme: string | null
31
20
  createdAt: Date
32
21
  }
33
22
 
34
23
  export interface Asset {
35
- id: number
24
+ id: string
36
25
  name: string
37
26
  type: string
38
27
  description: string | null
@@ -41,13 +30,8 @@ export interface Asset {
41
30
  updatedAt: Date
42
31
  }
43
32
 
44
- export interface AssetWithVersionsAndTags extends Asset {
45
- versions: AssetVersion[]
46
- tags: string[]
47
- }
48
-
49
- export interface AssetListItem {
50
- id: number
33
+ export interface AssetSearchResult {
34
+ id: string
51
35
  name: string
52
36
  type: string
53
37
  description: string | null
@@ -55,8 +39,9 @@ export interface AssetListItem {
55
39
  createdAt: Date
56
40
  updatedAt: Date
57
41
  latestVersion: string
58
- thumbnailKey: string | null
59
42
  approved: boolean
43
+ npmDependencies: string
44
+ assetDependencies: string
60
45
  }
61
46
 
62
47
  export interface PaginatedList<T> {
@@ -74,95 +59,31 @@ export interface User {
74
59
  emailVerified: boolean
75
60
  image: string | null
76
61
  role: string
62
+ isAdmin: boolean
77
63
  createdAt: Date
78
64
  updatedAt: Date
79
65
  }
80
66
 
81
- export interface AssetWithVersions extends Asset {
82
- versions: AssetVersion[]
83
- }
84
-
85
- export interface UnapprovedItem {
86
- versionId: number
87
- assetId: number
67
+ export interface GenerateAssetResult {
88
68
  assetName: string
89
- assetType: string
90
69
  version: string
91
- buildError: string | null
92
- buildOutputKey: string | null
93
- thumbnailKey: string | null
94
- createdAt: Date
95
- ownerName: string
96
- ownerEmail: string
97
- }
98
-
99
- export interface TagWithCount {
100
- id: number
101
- name: string
102
- count: number
103
- }
104
-
105
- export interface FileTreeEntry {
106
- path: string
107
- size: number
108
- }
109
-
110
- export interface GenerateJobStatus {
111
- jobId: string
112
- state: 'queued' | 'running' | 'done' | 'failed'
113
- message: string | null
114
- assetName: string | null
115
- version: string | null
116
- error: string | null
117
70
  }
118
71
 
119
- // ─── Contract ─────────────────────────────────────────────────────────────────
120
-
121
72
  export const contract = {
122
73
  asset: {
123
- getByName: oc
124
- .input(z.object({ name: assetNameSchema }))
125
- .output(z.custom<AssetWithVersionsAndTags | null>()),
126
-
127
- list: oc.input(listAssetsSchema).output(z.custom<PaginatedList<AssetListItem>>()),
74
+ search: oc.input(listAssetsSchema).output(z.custom<PaginatedList<AssetSearchResult>>()),
128
75
 
129
- getVersionTree: oc
130
- .input(z.object({ name: z.string(), version: semverSchema }))
131
- .output(z.custom<FileTreeEntry[]>()),
76
+ exact: oc.input(exactAssetSchema).output(z.custom<AssetSearchResult | null>()),
132
77
 
133
- getRawFile: oc
134
- .input(z.object({ name: z.string(), version: semverSchema, path: z.string() }))
135
- .output(z.instanceof(Blob)),
136
- },
137
-
138
- upload: {
139
- generic: oc
140
- .input(uploadGenericSchema.extend({ file: z.instanceof(File) }))
78
+ uploadZip: oc
79
+ .input(uploadZipSchema.extend({ zip: z.instanceof(File) }))
141
80
  .output(z.custom<AssetVersion>()),
142
81
 
143
- model: oc
144
- .input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
145
- .output(z.custom<AssetVersion>()),
82
+ downloadZip: oc.input(downloadZipSchema).output(z.instanceof(Blob)),
146
83
 
147
- hdri: oc
148
- .input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
149
- .output(z.custom<AssetVersion>()),
150
-
151
- music: oc
152
- .input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
153
- .output(z.custom<AssetVersion>()),
84
+ downloadPreviewImage: oc.input(downloadZipSchema).output(z.instanceof(Blob)),
154
85
 
155
- material: oc.input(uploadMaterialSchema).output(z.custom<AssetVersion>()),
156
- },
157
-
158
- admin: {
159
- listUnapproved: oc.output(z.custom<UnapprovedItem[]>()),
160
-
161
- approve: oc
162
- .input(z.object({ assetName: z.string(), version: z.string() }))
163
- .output(z.object({ success: z.boolean() })),
164
-
165
- backfillEmbeddings: oc.output(z.object({ indexed: z.number() })),
86
+ generate: oc.input(generateAssetSchema).output(z.custom<GenerateAssetResult>()),
166
87
  },
167
88
 
168
89
  user: {
@@ -170,28 +91,9 @@ export const contract = {
170
91
 
171
92
  updateProfile: oc.input(updateProfileSchema).output(z.custom<User>()),
172
93
 
173
- getApiKey: oc.output(z.custom<{ prefix: string; createdAt: Date } | null>()),
174
-
175
- regenerateApiKey: oc.output(z.object({ key: z.string(), prefix: z.string() })),
176
-
177
- myAssets: oc.output(z.custom<AssetWithVersions[]>()),
178
- },
179
-
180
- tag: {
181
- list: oc.output(z.custom<TagWithCount[]>()),
182
- },
94
+ getAuthToken: oc.output(z.custom<{ prefix: string; createdAt: Date } | null>()),
183
95
 
184
- generate: {
185
- start: oc
186
- .input(
187
- z.object({
188
- description: z.string().min(3).max(1000),
189
- type: assetTypeSchema.optional(),
190
- }),
191
- )
192
- .output(z.object({ jobId: z.string() })),
193
-
194
- status: oc.input(z.object({ jobId: z.string() })).output(z.custom<GenerateJobStatus>()),
96
+ createAuthToken: oc.output(z.object({ token: z.string(), prefix: z.string() })),
195
97
  },
196
98
  }
197
99
 
package/src/generate.ts CHANGED
@@ -20,8 +20,6 @@ export interface GenerateResult {
20
20
 
21
21
  export interface GenerateOptions {
22
22
  onProgress?: (message: string) => void
23
- pollIntervalMs?: number
24
- timeoutMs?: number
25
23
  }
26
24
 
27
25
  export async function generateAndWait(
@@ -29,34 +27,12 @@ export async function generateAndWait(
29
27
  input: GenerateInput,
30
28
  opts: GenerateOptions = {},
31
29
  ): Promise<GenerateResult> {
32
- const pollIntervalMs = opts.pollIntervalMs ?? 1500
33
- const timeoutMs = opts.timeoutMs ?? 60_000
34
30
  const report = opts.onProgress ?? (() => {})
35
31
 
36
- const { jobId } = await client.generate.start(input)
37
-
38
- const deadline = Date.now() + timeoutMs
39
- while (Date.now() < deadline) {
40
- const status = await client.generate.status({ jobId })
41
- if (status.message) report(status.message)
42
-
43
- if (status.state === 'done') {
44
- if (!status.assetName || !status.version) {
45
- throw new GenerateError('Generation completed without an asset name.')
46
- }
47
- return { assetName: status.assetName, version: status.version }
48
- }
49
-
50
- if (status.state === 'failed') {
51
- throw new GenerateError(status.error ?? 'Generation failed.')
52
- }
53
-
54
- await sleep(pollIntervalMs)
32
+ report('Generating asset')
33
+ const result = await client.asset.generate(input)
34
+ if (!result.assetName || !result.version) {
35
+ throw new GenerateError('Generation completed without an asset name.')
55
36
  }
56
-
57
- throw new GenerateError(`Generation timed out after ${timeoutMs}ms.`)
58
- }
59
-
60
- function sleep(ms: number): Promise<void> {
61
- return new Promise((resolve) => setTimeout(resolve, ms))
37
+ return result
62
38
  }
package/src/index.ts CHANGED
@@ -1,28 +1,36 @@
1
1
  // Client
2
- export { createMarketClient, createInternalClient } from './client.js'
3
- export type { MarketClient, InternalClient, MarketClientOptions } from './client.js'
2
+ export { createMarketClient } from './client.js'
3
+ export type { MarketClient, MarketClientOptions } from './client.js'
4
4
 
5
5
  // Contracts
6
6
  export { contract } from './contract.js'
7
7
  export type { AppContract } from './contract.js'
8
- export { internalContract } from './internal-contract.js'
9
- export type { InternalContract } from './internal-contract.js'
10
8
 
11
9
  // Contract output types
12
10
  export type {
13
11
  Asset,
14
12
  AssetVersion,
15
- AssetWithVersionsAndTags,
16
- AssetWithVersions,
17
- AssetListItem,
13
+ AssetSearchResult,
18
14
  PaginatedList,
19
15
  User,
20
- UnapprovedItem,
21
- TagWithCount,
22
- FileTreeEntry,
23
- GenerateJobStatus,
16
+ GenerateAssetResult,
24
17
  } from './contract.js'
25
18
 
19
+ export type {
20
+ AssetCatalog,
21
+ AssetDownloadZipInput,
22
+ AssetExactInput,
23
+ AssetGenerateInput,
24
+ AssetImplementation,
25
+ AssetImplementationContext,
26
+ AssetSearchIndex,
27
+ AssetSearchIndexEntry,
28
+ AssetSearchInput,
29
+ AssetUploadZipInput,
30
+ BlobStore,
31
+ SaveAssetVersionInput,
32
+ } from './asset-implementation.js'
33
+
26
34
  // Resolve
27
35
  export { resolve, ResolutionError } from './resolve.js'
28
36
  export type { ResolvedAsset, ResolveResult } from './resolve.js'
@@ -39,9 +47,10 @@ export {
39
47
  assetNameSchema,
40
48
  npmDependenciesSchema,
41
49
  assetDependenciesSchema,
42
- uploadGenericSchema,
43
- uploadTypedSchema,
44
- uploadMaterialSchema,
50
+ uploadZipSchema,
51
+ downloadZipSchema,
52
+ exactAssetSchema,
53
+ generateAssetSchema,
45
54
  updateProfileSchema,
46
55
  listAssetsSchema,
47
56
  } from './schemas.js'