@drawcall/market 0.1.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.
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +26 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -0
- package/dist/contract.d.ts +198 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +64 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +20 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +67 -0
- package/dist/install.js.map +1 -0
- package/dist/internal-contract.d.ts +19 -0
- package/dist/internal-contract.d.ts.map +1 -0
- package/dist/internal-contract.js +19 -0
- package/dist/internal-contract.js.map +1 -0
- package/dist/resolve.d.ts +32 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +145 -0
- package/dist/resolve.js.map +1 -0
- package/dist/schemas.d.ts +65 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +53 -0
- package/dist/schemas.js.map +1 -0
- package/package.json +31 -0
- package/src/cli.ts +72 -0
- package/src/client.ts +38 -0
- package/src/constants.ts +19 -0
- package/src/contract.ts +188 -0
- package/src/index.ts +46 -0
- package/src/install.ts +101 -0
- package/src/internal-contract.ts +26 -0
- package/src/resolve.ts +215 -0
- package/src/schemas.ts +70 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency resolver for market assets.
|
|
3
|
+
*
|
|
4
|
+
* 1. Collects the target assets and all transitive asset dependencies.
|
|
5
|
+
* 2. For each asset, finds a version that satisfies ALL constraints from
|
|
6
|
+
* every dependent.
|
|
7
|
+
* 3. Merges npm dependency ranges across all resolved assets and checks
|
|
8
|
+
* that they are compatible.
|
|
9
|
+
*/
|
|
10
|
+
import type { MarketClient } from './client.js';
|
|
11
|
+
export interface ResolvedAsset {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
npmDependencies: Record<string, string>;
|
|
15
|
+
assetDependencies: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface ResolveResult {
|
|
18
|
+
assets: ResolvedAsset[];
|
|
19
|
+
npmDependencies: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
interface AssetRequest {
|
|
22
|
+
name: string;
|
|
23
|
+
range: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class ResolutionError extends Error {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
export declare function resolve(client: MarketClient['asset'], requests: AssetRequest[], opts?: {
|
|
29
|
+
includeUnapproved?: boolean;
|
|
30
|
+
}): Promise<ResolveResult>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG/C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACxC;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,EAC7B,QAAQ,EAAE,YAAY,EAAE,EACxB,IAAI,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,OAAO,CAAC,aAAa,CAAC,CA6FxB"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency resolver for market assets.
|
|
3
|
+
*
|
|
4
|
+
* 1. Collects the target assets and all transitive asset dependencies.
|
|
5
|
+
* 2. For each asset, finds a version that satisfies ALL constraints from
|
|
6
|
+
* every dependent.
|
|
7
|
+
* 3. Merges npm dependency ranges across all resolved assets and checks
|
|
8
|
+
* that they are compatible.
|
|
9
|
+
*/
|
|
10
|
+
import * as semver from 'semver';
|
|
11
|
+
export class ResolutionError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'ResolutionError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function resolve(client, requests, opts = {}) {
|
|
18
|
+
// constraints[assetName] = list of { range, from } constraints
|
|
19
|
+
const constraints = new Map();
|
|
20
|
+
// resolved[assetName] = ResolvedAsset
|
|
21
|
+
const resolved = new Map();
|
|
22
|
+
// cache of fetched metadata
|
|
23
|
+
const metaCache = new Map();
|
|
24
|
+
// Seed constraints from the requested assets
|
|
25
|
+
for (const req of requests) {
|
|
26
|
+
addConstraint(constraints, req.name, req.range, '<root>');
|
|
27
|
+
}
|
|
28
|
+
// Iteratively resolve until stable
|
|
29
|
+
let unresolved = getUnresolved(constraints, resolved);
|
|
30
|
+
while (unresolved.length > 0) {
|
|
31
|
+
for (const assetName of unresolved) {
|
|
32
|
+
const meta = await fetchMeta(client, metaCache, assetName);
|
|
33
|
+
if (!meta) {
|
|
34
|
+
throw new ResolutionError(`Asset "${assetName}" not found.`);
|
|
35
|
+
}
|
|
36
|
+
// Filter to approved versions (unless --unapproved)
|
|
37
|
+
const candidates = meta.versions.filter((v) => opts.includeUnapproved || v.approved);
|
|
38
|
+
if (candidates.length === 0) {
|
|
39
|
+
throw new ResolutionError(`Asset "${assetName}" has no ${opts.includeUnapproved ? '' : 'approved '}versions.`);
|
|
40
|
+
}
|
|
41
|
+
// Collect all constraints for this asset
|
|
42
|
+
const assetConstraints = constraints.get(assetName) ?? [];
|
|
43
|
+
// Find the best (highest) version that satisfies ALL constraints
|
|
44
|
+
const candidateVersions = candidates.map((c) => c.version);
|
|
45
|
+
const satisfying = candidateVersions.filter((v) => assetConstraints.every((c) => semver.satisfies(v, c.range)));
|
|
46
|
+
if (satisfying.length === 0) {
|
|
47
|
+
const constraintDesc = assetConstraints
|
|
48
|
+
.map((c) => ` ${c.range} (from ${c.from})`)
|
|
49
|
+
.join('\n');
|
|
50
|
+
throw new ResolutionError(`No version of "${assetName}" satisfies all constraints:\n${constraintDesc}\n` +
|
|
51
|
+
`Available${opts.includeUnapproved ? '' : ' approved'}: ${candidateVersions.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
const best = semver.maxSatisfying(satisfying, '*');
|
|
54
|
+
const versionMeta = candidates.find((c) => c.version === best);
|
|
55
|
+
const npmDeps = JSON.parse(versionMeta.npmDependencies);
|
|
56
|
+
const assetDeps = JSON.parse(versionMeta.assetDependencies);
|
|
57
|
+
resolved.set(assetName, {
|
|
58
|
+
name: assetName,
|
|
59
|
+
version: best,
|
|
60
|
+
npmDependencies: npmDeps,
|
|
61
|
+
assetDependencies: assetDeps,
|
|
62
|
+
});
|
|
63
|
+
// Add transitive asset dependency constraints
|
|
64
|
+
for (const [depName, depRange] of Object.entries(assetDeps)) {
|
|
65
|
+
addConstraint(constraints, depName, depRange, `${assetName}@${best}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
unresolved = getUnresolved(constraints, resolved);
|
|
69
|
+
}
|
|
70
|
+
// Verify all resolved versions still satisfy constraints (transitive deps
|
|
71
|
+
// may have added new constraints after we resolved a version)
|
|
72
|
+
for (const [assetName, asset] of resolved) {
|
|
73
|
+
const assetConstraints = constraints.get(assetName) ?? [];
|
|
74
|
+
for (const c of assetConstraints) {
|
|
75
|
+
if (!semver.satisfies(asset.version, c.range)) {
|
|
76
|
+
throw new ResolutionError(`Conflict: "${assetName}@${asset.version}" does not satisfy ` +
|
|
77
|
+
`${c.range} (required by ${c.from}).`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Merge npm dependencies across all resolved assets
|
|
82
|
+
const mergedNpm = mergeNpmDependencies(Array.from(resolved.values()));
|
|
83
|
+
return {
|
|
84
|
+
assets: Array.from(resolved.values()),
|
|
85
|
+
npmDependencies: mergedNpm,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function addConstraint(constraints, name, range, from) {
|
|
89
|
+
const existing = constraints.get(name) ?? [];
|
|
90
|
+
existing.push({ range, from });
|
|
91
|
+
constraints.set(name, existing);
|
|
92
|
+
}
|
|
93
|
+
function getUnresolved(constraints, resolved) {
|
|
94
|
+
return Array.from(constraints.keys()).filter((name) => !resolved.has(name));
|
|
95
|
+
}
|
|
96
|
+
async function fetchMeta(client, cache, name) {
|
|
97
|
+
if (cache.has(name))
|
|
98
|
+
return cache.get(name);
|
|
99
|
+
const meta = await client.getByName({ name });
|
|
100
|
+
if (meta)
|
|
101
|
+
cache.set(name, meta);
|
|
102
|
+
return meta;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Merge npm dependency ranges from all resolved assets.
|
|
106
|
+
* For each package, check that all declared ranges are compatible
|
|
107
|
+
* (using semver.intersects). Return the narrowest range.
|
|
108
|
+
*/
|
|
109
|
+
function mergeNpmDependencies(assets) {
|
|
110
|
+
// Collect all ranges per package
|
|
111
|
+
const rangesPerPkg = new Map();
|
|
112
|
+
for (const asset of assets) {
|
|
113
|
+
for (const [pkg, range] of Object.entries(asset.npmDependencies)) {
|
|
114
|
+
const existing = rangesPerPkg.get(pkg) ?? [];
|
|
115
|
+
existing.push({ range, from: `${asset.name}@${asset.version}` });
|
|
116
|
+
rangesPerPkg.set(pkg, existing);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const merged = {};
|
|
120
|
+
for (const [pkg, ranges] of rangesPerPkg) {
|
|
121
|
+
// Check pairwise compatibility
|
|
122
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
123
|
+
for (let j = i + 1; j < ranges.length; j++) {
|
|
124
|
+
if (!semver.intersects(ranges[i].range, ranges[j].range)) {
|
|
125
|
+
throw new ResolutionError(`npm dependency conflict for "${pkg}":\n` +
|
|
126
|
+
` ${ranges[i].range} (from ${ranges[i].from})\n` +
|
|
127
|
+
` ${ranges[j].range} (from ${ranges[j].from})`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Use the narrowest (most constrained) range.
|
|
132
|
+
// Simple heuristic: pick the range with the highest minimum version.
|
|
133
|
+
let narrowest = ranges[0].range;
|
|
134
|
+
for (const r of ranges.slice(1)) {
|
|
135
|
+
const minCurrent = semver.minVersion(narrowest);
|
|
136
|
+
const minNew = semver.minVersion(r.range);
|
|
137
|
+
if (minCurrent && minNew && semver.gt(minNew, minCurrent)) {
|
|
138
|
+
narrowest = r.range;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
merged[pkg] = narrowest;
|
|
142
|
+
}
|
|
143
|
+
return merged;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ASSET_TYPES: readonly ["generic", "model", "hdri", "material", "music"];
|
|
3
|
+
export type AssetType = (typeof ASSET_TYPES)[number];
|
|
4
|
+
export declare const assetTypeSchema: z.ZodEnum<{
|
|
5
|
+
generic: "generic";
|
|
6
|
+
model: "model";
|
|
7
|
+
hdri: "hdri";
|
|
8
|
+
material: "material";
|
|
9
|
+
music: "music";
|
|
10
|
+
}>;
|
|
11
|
+
export declare const semverSchema: z.ZodString;
|
|
12
|
+
export declare const assetNameSchema: z.ZodString;
|
|
13
|
+
export declare const npmDependenciesSchema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
14
|
+
export declare const assetDependenciesSchema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
15
|
+
export declare const uploadGenericSchema: z.ZodObject<{
|
|
16
|
+
name: z.ZodString;
|
|
17
|
+
version: z.ZodString;
|
|
18
|
+
description: z.ZodOptional<z.ZodString>;
|
|
19
|
+
npmDependencies: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
20
|
+
assetDependencies: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
21
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
export declare const uploadTypedSchema: z.ZodObject<{
|
|
24
|
+
name: z.ZodString;
|
|
25
|
+
version: z.ZodString;
|
|
26
|
+
description: z.ZodOptional<z.ZodString>;
|
|
27
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
export declare const uploadMaterialSchema: z.ZodObject<{
|
|
30
|
+
name: z.ZodString;
|
|
31
|
+
version: z.ZodString;
|
|
32
|
+
description: z.ZodOptional<z.ZodString>;
|
|
33
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
34
|
+
properties: z.ZodObject<{
|
|
35
|
+
color: z.ZodDefault<z.ZodString>;
|
|
36
|
+
roughness: z.ZodDefault<z.ZodNumber>;
|
|
37
|
+
metalness: z.ZodDefault<z.ZodNumber>;
|
|
38
|
+
normalScale: z.ZodDefault<z.ZodNumber>;
|
|
39
|
+
emissive: z.ZodDefault<z.ZodString>;
|
|
40
|
+
emissiveIntensity: z.ZodDefault<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export declare const updateProfileSchema: z.ZodObject<{
|
|
44
|
+
name: z.ZodOptional<z.ZodString>;
|
|
45
|
+
image: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
export declare const listAssetsSchema: z.ZodObject<{
|
|
48
|
+
page: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
51
|
+
generic: "generic";
|
|
52
|
+
model: "model";
|
|
53
|
+
hdri: "hdri";
|
|
54
|
+
material: "material";
|
|
55
|
+
music: "music";
|
|
56
|
+
}>>;
|
|
57
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
58
|
+
search: z.ZodOptional<z.ZodString>;
|
|
59
|
+
sort: z.ZodDefault<z.ZodEnum<{
|
|
60
|
+
newest: "newest";
|
|
61
|
+
alphabetical: "alphabetical";
|
|
62
|
+
relevance: "relevance";
|
|
63
|
+
}>>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,WAAW,4DAA6D,CAAA;AACrF,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD,eAAO,MAAM,eAAe;;;;;;EAAsB,CAAA;AAElD,eAAO,MAAM,YAAY,aAKtB,CAAA;AAEH,eAAO,MAAM,eAAe,aAUxB,CAAA;AAEJ,eAAO,MAAM,qBAAqB,qDAA+C,CAAA;AAEjF,eAAO,MAAM,uBAAuB,qDAA+C,CAAA;AAEnF,eAAO,MAAM,mBAAmB;;;;;;;iBAO9B,CAAA;AAEF,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAA;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;iBAS/B,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;iBAG9B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;iBAO3B,CAAA"}
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const ASSET_TYPES = ['generic', 'model', 'hdri', 'material', 'music'];
|
|
3
|
+
export const assetTypeSchema = z.enum(ASSET_TYPES);
|
|
4
|
+
export const semverSchema = z
|
|
5
|
+
.string()
|
|
6
|
+
.regex(/^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/, 'Must be a valid semver version (e.g. 1.0.0)');
|
|
7
|
+
export const assetNameSchema = z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(128)
|
|
11
|
+
.regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/, 'Must be lowercase alphanumeric with hyphens, no leading/trailing hyphens')
|
|
12
|
+
.refine((name) => !name.endsWith('-example'), {
|
|
13
|
+
message: "Asset names cannot end with '-example' (reserved for generated examples)",
|
|
14
|
+
});
|
|
15
|
+
export const npmDependenciesSchema = z.record(z.string(), z.string()).default({});
|
|
16
|
+
export const assetDependenciesSchema = z.record(z.string(), z.string()).default({});
|
|
17
|
+
export const uploadGenericSchema = z.object({
|
|
18
|
+
name: assetNameSchema,
|
|
19
|
+
version: semverSchema,
|
|
20
|
+
description: z.string().max(1000).optional(),
|
|
21
|
+
npmDependencies: npmDependenciesSchema,
|
|
22
|
+
assetDependencies: assetDependenciesSchema,
|
|
23
|
+
tags: z.array(z.string()).default([]),
|
|
24
|
+
});
|
|
25
|
+
export const uploadTypedSchema = z.object({
|
|
26
|
+
name: assetNameSchema,
|
|
27
|
+
version: semverSchema,
|
|
28
|
+
description: z.string().max(1000).optional(),
|
|
29
|
+
tags: z.array(z.string()).default([]),
|
|
30
|
+
});
|
|
31
|
+
export const uploadMaterialSchema = uploadTypedSchema.extend({
|
|
32
|
+
properties: z.object({
|
|
33
|
+
color: z.string().default('#ffffff'),
|
|
34
|
+
roughness: z.number().min(0).max(1).default(0.5),
|
|
35
|
+
metalness: z.number().min(0).max(1).default(0),
|
|
36
|
+
normalScale: z.number().min(0).max(2).default(1),
|
|
37
|
+
emissive: z.string().default('#000000'),
|
|
38
|
+
emissiveIntensity: z.number().min(0).max(10).default(0),
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
export const updateProfileSchema = z.object({
|
|
42
|
+
name: z.string().min(1).max(100).optional(),
|
|
43
|
+
image: z.string().url().optional(),
|
|
44
|
+
});
|
|
45
|
+
export const listAssetsSchema = z.object({
|
|
46
|
+
page: z.number().int().min(1).default(1),
|
|
47
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
48
|
+
type: assetTypeSchema.optional(),
|
|
49
|
+
tag: z.string().optional(),
|
|
50
|
+
search: z.string().max(200).optional(),
|
|
51
|
+
sort: z.enum(['newest', 'alphabetical', 'relevance']).default('newest'),
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drawcall/market",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./src/index.ts"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"market": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsx src/cli.ts",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@orpc/contract": "^1.0.0",
|
|
18
|
+
"@orpc/client": "^1.0.0",
|
|
19
|
+
"chalk": "^5.6.2",
|
|
20
|
+
"commander": "^14.0.3",
|
|
21
|
+
"nypm": "^0.6.0",
|
|
22
|
+
"semver": "^7.7.0",
|
|
23
|
+
"zod": "^4.3.6"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.6.0",
|
|
27
|
+
"@types/semver": "^7.5.0",
|
|
28
|
+
"tsx": "^4.19.0",
|
|
29
|
+
"typescript": "^5.7.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { createMarketClient } from './client.js'
|
|
6
|
+
import { resolve, ResolutionError, type ResolveResult } from './resolve.js'
|
|
7
|
+
import { install } from './install.js'
|
|
8
|
+
|
|
9
|
+
const program = new Command()
|
|
10
|
+
|
|
11
|
+
program.name('market').description('Install assets from the drawcall.ai market').version('0.1.0')
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command('install')
|
|
15
|
+
.description('Install assets into your project')
|
|
16
|
+
.argument('<assets...>', 'Assets to install (e.g. my-model, my-model@^2.0.0)')
|
|
17
|
+
.option('--unapproved', 'Include unapproved versions', false)
|
|
18
|
+
.option('--api <url>', 'API base URL', process.env.MARKET_API_URL ?? 'http://localhost:8787')
|
|
19
|
+
.option('--cwd <dir>', 'Project directory', process.cwd())
|
|
20
|
+
.action(async (assetArgs: string[], opts) => {
|
|
21
|
+
const client = createMarketClient({ baseUrl: opts.api })
|
|
22
|
+
|
|
23
|
+
// Parse asset[@range] arguments
|
|
24
|
+
const requests = assetArgs.map((arg) => {
|
|
25
|
+
const atIdx = arg.indexOf('@', 1)
|
|
26
|
+
if (atIdx > 0) {
|
|
27
|
+
return { name: arg.slice(0, atIdx), range: arg.slice(atIdx + 1) }
|
|
28
|
+
}
|
|
29
|
+
return { name: arg, range: '*' }
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Resolve
|
|
33
|
+
console.log(chalk.bold('Resolving dependencies...\n'))
|
|
34
|
+
|
|
35
|
+
let resolution: ResolveResult
|
|
36
|
+
try {
|
|
37
|
+
resolution = await resolve(client.asset, requests, {
|
|
38
|
+
includeUnapproved: opts.unapproved,
|
|
39
|
+
})
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err instanceof ResolutionError) {
|
|
42
|
+
console.error(chalk.red('Resolution failed:\n') + err.message)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
throw err
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(chalk.green(` ${resolution.assets.length} asset(s) resolved:\n`))
|
|
49
|
+
for (const asset of resolution.assets) {
|
|
50
|
+
console.log(` ${chalk.cyan(asset.name)}${chalk.dim('@' + asset.version)}`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const npmCount = Object.keys(resolution.npmDependencies).length
|
|
54
|
+
if (npmCount > 0) {
|
|
55
|
+
console.log(chalk.green(`\n ${npmCount} npm dependenc${npmCount === 1 ? 'y' : 'ies'}:\n`))
|
|
56
|
+
for (const [pkg, range] of Object.entries(resolution.npmDependencies)) {
|
|
57
|
+
console.log(` ${pkg} ${chalk.dim(range)}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Install
|
|
62
|
+
console.log(chalk.bold('\nInstalling...\n'))
|
|
63
|
+
|
|
64
|
+
await install(client, resolution, {
|
|
65
|
+
cwd: opts.cwd,
|
|
66
|
+
onProgress: (msg) => console.log(chalk.dim(` ${msg}`)),
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
console.log(chalk.bold.green('\nDone!'))
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
program.parse()
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createORPCClient } from '@orpc/client'
|
|
2
|
+
import { RPCLink } from '@orpc/client/fetch'
|
|
3
|
+
import type { ContractRouterClient } from '@orpc/contract'
|
|
4
|
+
import type { AppContract } from './contract.js'
|
|
5
|
+
import type { InternalContract } from './internal-contract.js'
|
|
6
|
+
|
|
7
|
+
export type MarketClient = ContractRouterClient<AppContract>
|
|
8
|
+
export type InternalClient = ContractRouterClient<InternalContract>
|
|
9
|
+
|
|
10
|
+
export interface MarketClientOptions {
|
|
11
|
+
baseUrl: string
|
|
12
|
+
fetch?: typeof globalThis.fetch
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolveUrl(baseUrl: string, path: string): string {
|
|
16
|
+
if (baseUrl) return `${baseUrl}${path}`
|
|
17
|
+
// In browser environments, use the current origin for relative URLs
|
|
18
|
+
if (typeof globalThis !== 'undefined' && 'location' in globalThis) {
|
|
19
|
+
return `${(globalThis as unknown as { location: { origin: string } }).location.origin}${path}`
|
|
20
|
+
}
|
|
21
|
+
throw new Error('baseUrl is required in non-browser environments')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createMarketClient(opts: MarketClientOptions): MarketClient {
|
|
25
|
+
const link = new RPCLink({
|
|
26
|
+
url: resolveUrl(opts.baseUrl, '/api/rpc'),
|
|
27
|
+
fetch: opts.fetch,
|
|
28
|
+
})
|
|
29
|
+
return createORPCClient<MarketClient>(link)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createInternalClient(opts: MarketClientOptions): InternalClient {
|
|
33
|
+
const link = new RPCLink({
|
|
34
|
+
url: resolveUrl(opts.baseUrl, '/api/internal-rpc'),
|
|
35
|
+
fetch: opts.fetch,
|
|
36
|
+
})
|
|
37
|
+
return createORPCClient<InternalClient>(link)
|
|
38
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AssetType } from './schemas.js'
|
|
2
|
+
|
|
3
|
+
export const MAX_FILE_SIZE = 100 * 1024 * 1024 // 100MB
|
|
4
|
+
|
|
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'],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ASSET_TYPE_LABELS: Record<AssetType, string> = {
|
|
14
|
+
generic: 'Generic',
|
|
15
|
+
model: '3D Model',
|
|
16
|
+
hdri: 'HDRI',
|
|
17
|
+
material: 'Material',
|
|
18
|
+
music: 'Audio',
|
|
19
|
+
}
|
package/src/contract.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { oc } from '@orpc/contract'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import {
|
|
4
|
+
assetNameSchema,
|
|
5
|
+
semverSchema,
|
|
6
|
+
listAssetsSchema,
|
|
7
|
+
updateProfileSchema,
|
|
8
|
+
uploadGenericSchema,
|
|
9
|
+
uploadTypedSchema,
|
|
10
|
+
uploadMaterialSchema,
|
|
11
|
+
} from './schemas.js'
|
|
12
|
+
|
|
13
|
+
// ─── Output types ─────────────────────────────────────────────────────────────
|
|
14
|
+
// These mirror what the server handlers return (Drizzle query results).
|
|
15
|
+
// We use z.custom<T>() so the contract carries full types for the client
|
|
16
|
+
// without duplicating every DB column as a Zod field.
|
|
17
|
+
|
|
18
|
+
export interface AssetVersion {
|
|
19
|
+
id: number
|
|
20
|
+
assetId: number
|
|
21
|
+
version: string
|
|
22
|
+
approved: boolean
|
|
23
|
+
npmDependencies: string
|
|
24
|
+
assetDependencies: string
|
|
25
|
+
sourceKey: string
|
|
26
|
+
buildOutputKey: string | null
|
|
27
|
+
thumbnailKey: string | null
|
|
28
|
+
buildError: string | null
|
|
29
|
+
readme: string | null
|
|
30
|
+
createdAt: Date
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Asset {
|
|
34
|
+
id: number
|
|
35
|
+
name: string
|
|
36
|
+
type: string
|
|
37
|
+
description: string | null
|
|
38
|
+
ownerId: string
|
|
39
|
+
createdAt: Date
|
|
40
|
+
updatedAt: Date
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AssetWithVersionsAndTags extends Asset {
|
|
44
|
+
versions: AssetVersion[]
|
|
45
|
+
tags: string[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface AssetListItem {
|
|
49
|
+
id: number
|
|
50
|
+
name: string
|
|
51
|
+
type: string
|
|
52
|
+
description: string | null
|
|
53
|
+
ownerId: string
|
|
54
|
+
createdAt: Date
|
|
55
|
+
updatedAt: Date
|
|
56
|
+
latestVersion: string
|
|
57
|
+
thumbnailKey: string | null
|
|
58
|
+
approved: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PaginatedList<T> {
|
|
62
|
+
items: T[]
|
|
63
|
+
total: number
|
|
64
|
+
page: number
|
|
65
|
+
limit: number
|
|
66
|
+
totalPages: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface User {
|
|
70
|
+
id: string
|
|
71
|
+
name: string
|
|
72
|
+
email: string
|
|
73
|
+
emailVerified: boolean
|
|
74
|
+
image: string | null
|
|
75
|
+
role: string
|
|
76
|
+
createdAt: Date
|
|
77
|
+
updatedAt: Date
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface AssetWithVersions extends Asset {
|
|
81
|
+
versions: AssetVersion[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface UnapprovedItem {
|
|
85
|
+
versionId: number
|
|
86
|
+
assetId: number
|
|
87
|
+
assetName: string
|
|
88
|
+
assetType: string
|
|
89
|
+
version: string
|
|
90
|
+
buildError: string | null
|
|
91
|
+
buildOutputKey: string | null
|
|
92
|
+
thumbnailKey: string | null
|
|
93
|
+
createdAt: Date
|
|
94
|
+
ownerName: string
|
|
95
|
+
ownerEmail: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface TagWithCount {
|
|
99
|
+
id: number
|
|
100
|
+
name: string
|
|
101
|
+
count: number
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface FileTreeEntry {
|
|
105
|
+
path: string
|
|
106
|
+
size: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── Contract ─────────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
export const contract = {
|
|
112
|
+
asset: {
|
|
113
|
+
getByName: oc
|
|
114
|
+
.input(z.object({ name: assetNameSchema }))
|
|
115
|
+
.output(z.custom<AssetWithVersionsAndTags | null>()),
|
|
116
|
+
|
|
117
|
+
list: oc
|
|
118
|
+
.input(listAssetsSchema)
|
|
119
|
+
.output(z.custom<PaginatedList<AssetListItem>>()),
|
|
120
|
+
|
|
121
|
+
getVersionTree: oc
|
|
122
|
+
.input(z.object({ name: z.string(), version: semverSchema }))
|
|
123
|
+
.output(z.custom<FileTreeEntry[]>()),
|
|
124
|
+
|
|
125
|
+
getRawFile: oc
|
|
126
|
+
.input(z.object({ name: z.string(), version: semverSchema, path: z.string() }))
|
|
127
|
+
.output(z.instanceof(Blob)),
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
upload: {
|
|
131
|
+
generic: oc
|
|
132
|
+
.input(uploadGenericSchema.extend({ file: z.instanceof(File) }))
|
|
133
|
+
.output(z.custom<AssetVersion>()),
|
|
134
|
+
|
|
135
|
+
model: oc
|
|
136
|
+
.input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
|
|
137
|
+
.output(z.custom<AssetVersion>()),
|
|
138
|
+
|
|
139
|
+
hdri: oc
|
|
140
|
+
.input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
|
|
141
|
+
.output(z.custom<AssetVersion>()),
|
|
142
|
+
|
|
143
|
+
music: oc
|
|
144
|
+
.input(uploadTypedSchema.extend({ file: z.instanceof(File) }))
|
|
145
|
+
.output(z.custom<AssetVersion>()),
|
|
146
|
+
|
|
147
|
+
material: oc
|
|
148
|
+
.input(uploadMaterialSchema)
|
|
149
|
+
.output(z.custom<AssetVersion>()),
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
admin: {
|
|
153
|
+
listUnapproved: oc
|
|
154
|
+
.output(z.custom<UnapprovedItem[]>()),
|
|
155
|
+
|
|
156
|
+
approve: oc
|
|
157
|
+
.input(z.object({ assetName: z.string(), version: z.string() }))
|
|
158
|
+
.output(z.object({ success: z.boolean() })),
|
|
159
|
+
|
|
160
|
+
backfillEmbeddings: oc
|
|
161
|
+
.output(z.object({ indexed: z.number() })),
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
user: {
|
|
165
|
+
getProfile: oc
|
|
166
|
+
.output(z.custom<User | null>()),
|
|
167
|
+
|
|
168
|
+
updateProfile: oc
|
|
169
|
+
.input(updateProfileSchema)
|
|
170
|
+
.output(z.custom<User>()),
|
|
171
|
+
|
|
172
|
+
getApiKey: oc
|
|
173
|
+
.output(z.custom<{ prefix: string; createdAt: Date } | null>()),
|
|
174
|
+
|
|
175
|
+
regenerateApiKey: oc
|
|
176
|
+
.output(z.object({ key: z.string(), prefix: z.string() })),
|
|
177
|
+
|
|
178
|
+
myAssets: oc
|
|
179
|
+
.output(z.custom<AssetWithVersions[]>()),
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
tag: {
|
|
183
|
+
list: oc
|
|
184
|
+
.output(z.custom<TagWithCount[]>()),
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export type AppContract = typeof contract
|