@absolutejs/artifacts 0.0.1 → 0.0.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/README.md +73 -3
- package/dist/index.js +290 -17
- package/dist/manifest.js +18 -0
- package/dist/manifest.json +50 -0
- package/dist/rag.js +44 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/manifest.d.ts +9 -0
- package/dist/src/rag.d.ts +15 -0
- package/dist/src/registry.d.ts +6 -0
- package/dist/src/service.d.ts +12 -2
- package/dist/src/standardKinds.d.ts +157 -0
- package/dist/src/store.d.ts +15 -1
- package/dist/src/types.d.ts +29 -2
- package/package.json +18 -4
package/README.md
CHANGED
|
@@ -14,11 +14,15 @@ routes, authorization, UI, or hosting.
|
|
|
14
14
|
|
|
15
15
|
- Structured artifact-kind schemas and runtime validation
|
|
16
16
|
- Draft, published, and archived lifecycle states
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
17
|
+
- Immutable revision history, restoration, and optimistic updates
|
|
18
|
+
- Structured content plus opaque references to generated or source files
|
|
19
|
+
- Artifact, asset, renderer, and publisher storage interfaces
|
|
20
|
+
- In-memory artifact and asset stores for development and tests
|
|
20
21
|
- Owner-bound lifecycle tools structurally compatible with AI tool maps
|
|
21
22
|
- Provenance fields for model, tool, trace, and source entities
|
|
23
|
+
- Standard file-backed kinds for documents, presentations, spreadsheets,
|
|
24
|
+
datasets, code, images, audio, video, email, archives, and generic files
|
|
25
|
+
- An optional bridge to `@absolutejs/rag` ingestion
|
|
22
26
|
|
|
23
27
|
Your application retains authorization, durable persistence, public tokens,
|
|
24
28
|
URLs, notifications, analytics, submissions, and product-specific rendering.
|
|
@@ -29,6 +33,7 @@ URLs, notifications, analytics, submissions, and product-specific rendering.
|
|
|
29
33
|
import { Type } from "@sinclair/typebox";
|
|
30
34
|
import {
|
|
31
35
|
createArtifactService,
|
|
36
|
+
createMemoryArtifactAssetStore,
|
|
32
37
|
createMemoryArtifactStore,
|
|
33
38
|
defineArtifactRegistry,
|
|
34
39
|
} from "@absolutejs/artifacts";
|
|
@@ -51,6 +56,7 @@ const registry = defineArtifactRegistry({
|
|
|
51
56
|
});
|
|
52
57
|
|
|
53
58
|
const artifacts = createArtifactService({
|
|
59
|
+
assetStore: createMemoryArtifactAssetStore(),
|
|
54
60
|
registry,
|
|
55
61
|
store: createMemoryArtifactStore(),
|
|
56
62
|
});
|
|
@@ -67,6 +73,70 @@ const page = await artifacts.create("owner-123", {
|
|
|
67
73
|
});
|
|
68
74
|
```
|
|
69
75
|
|
|
76
|
+
Every successful create or lifecycle mutation appends an immutable snapshot.
|
|
77
|
+
Restoring history creates a new private draft instead of rewriting or
|
|
78
|
+
republishing an old revision:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const history = await artifacts.listRevisions("owner-123", page.id);
|
|
82
|
+
const restored = await artifacts.restore("owner-123", page.id, 1);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## File-backed artifact kinds
|
|
86
|
+
|
|
87
|
+
Use the bundled definitions directly or compose them with application-specific
|
|
88
|
+
kinds:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import {
|
|
92
|
+
defineArtifactRegistry,
|
|
93
|
+
standardArtifactDefinitions,
|
|
94
|
+
} from "@absolutejs/artifacts";
|
|
95
|
+
|
|
96
|
+
const registry = defineArtifactRegistry({
|
|
97
|
+
...standardArtifactDefinitions,
|
|
98
|
+
page: myPageDefinition,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
File bytes stay in host storage. Artifact records retain opaque references with
|
|
103
|
+
name, media type, size, checksum, role, and storage URI. The URI is not treated
|
|
104
|
+
as a public URL and the package reads it only through the configured asset
|
|
105
|
+
store. Detaching a file does not delete its bytes because older immutable
|
|
106
|
+
revisions may still reference it.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const report = await artifacts.create("owner-123", {
|
|
110
|
+
content: { summary: "Quarterly results" },
|
|
111
|
+
createdBy: "agent",
|
|
112
|
+
kind: "document",
|
|
113
|
+
title: "Q3 report",
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await artifacts.attach("owner-123", report.id, {
|
|
117
|
+
data: pdfBytes,
|
|
118
|
+
mediaType: "application/pdf",
|
|
119
|
+
name: "q3-report.pdf",
|
|
120
|
+
role: "primary",
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## RAG ingestion
|
|
125
|
+
|
|
126
|
+
The optional `@absolutejs/artifacts/rag` entry point resolves one current or
|
|
127
|
+
historical artifact record into the upload contract already accepted by
|
|
128
|
+
`@absolutejs/rag`. Structured content is included as JSON and every attached
|
|
129
|
+
file is included without exposing its storage URI:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { artifactToRAGUploads } from "@absolutejs/artifacts/rag";
|
|
133
|
+
import { buildRAGUpsertInputFromUploads } from "@absolutejs/rag";
|
|
134
|
+
|
|
135
|
+
const revision = await artifacts.getRevision("owner-123", report.id, 2);
|
|
136
|
+
const uploads = await artifactToRAGUploads(revision, assetStore);
|
|
137
|
+
const upsert = await buildRAGUpsertInputFromUploads({ uploads });
|
|
138
|
+
```
|
|
139
|
+
|
|
70
140
|
## Compose publishing and rendering
|
|
71
141
|
|
|
72
142
|
Publishing is an adapter because public access is a host policy:
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,76 @@ var defineArtifactRegistry = (definitions) => ({
|
|
|
31
31
|
return content;
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
+
// src/standardKinds.ts
|
|
35
|
+
import { Type } from "@sinclair/typebox";
|
|
36
|
+
var FileArtifactContentSchema = Type.Object({
|
|
37
|
+
description: Type.Optional(Type.String()),
|
|
38
|
+
instructions: Type.Optional(Type.String()),
|
|
39
|
+
summary: Type.Optional(Type.String())
|
|
40
|
+
});
|
|
41
|
+
var capabilities = [
|
|
42
|
+
"attach",
|
|
43
|
+
"archive",
|
|
44
|
+
"edit",
|
|
45
|
+
"export",
|
|
46
|
+
"preview",
|
|
47
|
+
"refine"
|
|
48
|
+
];
|
|
49
|
+
var fileKind = (label, acceptedMediaTypes, maxCount = 1) => ({
|
|
50
|
+
assets: { acceptedMediaTypes, maxCount },
|
|
51
|
+
capabilities: [...capabilities],
|
|
52
|
+
content: FileArtifactContentSchema,
|
|
53
|
+
label,
|
|
54
|
+
schemaVersion: 1
|
|
55
|
+
});
|
|
56
|
+
var standardArtifactDefinitions = {
|
|
57
|
+
archive: fileKind("Archive", [
|
|
58
|
+
"application/gzip",
|
|
59
|
+
"application/vnd.rar",
|
|
60
|
+
"application/x-7z-compressed",
|
|
61
|
+
"application/x-bzip2",
|
|
62
|
+
"application/x-tar",
|
|
63
|
+
"application/zip"
|
|
64
|
+
], 20),
|
|
65
|
+
audio: fileKind("Audio", ["audio/*"]),
|
|
66
|
+
code: fileKind("Code", ["application/json", "application/xml", "text/*"], 100),
|
|
67
|
+
dataset: fileKind("Dataset", [
|
|
68
|
+
"application/json",
|
|
69
|
+
"application/x-ndjson",
|
|
70
|
+
"application/xml",
|
|
71
|
+
"text/csv",
|
|
72
|
+
"text/tab-separated-values",
|
|
73
|
+
"text/plain",
|
|
74
|
+
"text/yaml"
|
|
75
|
+
], 20),
|
|
76
|
+
document: fileKind("Document", [
|
|
77
|
+
"application/epub+zip",
|
|
78
|
+
"application/msword",
|
|
79
|
+
"application/pdf",
|
|
80
|
+
"application/rtf",
|
|
81
|
+
"application/vnd.oasis.opendocument.text",
|
|
82
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
83
|
+
"text/*"
|
|
84
|
+
], 20),
|
|
85
|
+
email: fileKind("Email", ["application/mbox", "application/vnd.ms-outlook", "message/*", "text/*"], 100),
|
|
86
|
+
file: fileKind("File", ["*/*"], 100),
|
|
87
|
+
image: fileKind("Image", ["image/*"], 20),
|
|
88
|
+
presentation: fileKind("Presentation", [
|
|
89
|
+
"application/pdf",
|
|
90
|
+
"application/vnd.ms-powerpoint",
|
|
91
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
92
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
93
|
+
], 20),
|
|
94
|
+
spreadsheet: fileKind("Spreadsheet", [
|
|
95
|
+
"application/vnd.ms-excel",
|
|
96
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
97
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
98
|
+
"text/csv",
|
|
99
|
+
"text/tab-separated-values"
|
|
100
|
+
], 20),
|
|
101
|
+
video: fileKind("Video", ["video/*"])
|
|
102
|
+
};
|
|
103
|
+
var STANDARD_ARTIFACT_KIND_NAMES = Object.keys(standardArtifactDefinitions);
|
|
34
104
|
// src/renderers.ts
|
|
35
105
|
var createArtifactRendererRegistry = (initial = []) => {
|
|
36
106
|
const key = (kind, format) => `${kind}:${format}`;
|
|
@@ -55,9 +125,45 @@ var requireCapability = (artifact, capability) => {
|
|
|
55
125
|
throw new ArtifactError("unsupported_capability", `${artifact.kind} artifacts do not support ${capability}`);
|
|
56
126
|
}
|
|
57
127
|
};
|
|
128
|
+
var mediaTypeMatches = (accepted, actual) => {
|
|
129
|
+
if (accepted === "*/*" || accepted === actual)
|
|
130
|
+
return true;
|
|
131
|
+
if (!accepted.endsWith("/*"))
|
|
132
|
+
return false;
|
|
133
|
+
return actual.startsWith(accepted.slice(0, -1));
|
|
134
|
+
};
|
|
58
135
|
var createArtifactService = (options) => {
|
|
59
136
|
const now = () => (options.clock ?? (() => new Date))().toISOString();
|
|
60
137
|
const idFactory = options.idFactory ?? (() => crypto.randomUUID());
|
|
138
|
+
const validateAssets = (kind, assets) => {
|
|
139
|
+
const policy = options.registry.definitions[kind]?.assets;
|
|
140
|
+
if (!policy) {
|
|
141
|
+
if (assets.length > 0) {
|
|
142
|
+
throw new ArtifactError("invalid_content", `${kind} artifacts do not accept file assets`);
|
|
143
|
+
}
|
|
144
|
+
return assets;
|
|
145
|
+
}
|
|
146
|
+
if (policy.maxCount !== undefined && assets.length > policy.maxCount) {
|
|
147
|
+
throw new ArtifactError("invalid_content", `${kind} artifacts accept at most ${policy.maxCount} file assets`);
|
|
148
|
+
}
|
|
149
|
+
const rejected = assets.find((asset) => policy.acceptedMediaTypes?.length && !policy.acceptedMediaTypes.some((accepted) => mediaTypeMatches(accepted, asset.mediaType)));
|
|
150
|
+
if (rejected) {
|
|
151
|
+
throw new ArtifactError("invalid_content", `${rejected.mediaType} is not accepted by ${kind} artifacts`);
|
|
152
|
+
}
|
|
153
|
+
return assets;
|
|
154
|
+
};
|
|
155
|
+
const validateNewAsset = (kind, input, currentCount) => {
|
|
156
|
+
const policy = options.registry.definitions[kind]?.assets;
|
|
157
|
+
if (!policy) {
|
|
158
|
+
throw new ArtifactError("invalid_content", `${kind} artifacts do not accept file assets`);
|
|
159
|
+
}
|
|
160
|
+
if (policy.maxCount !== undefined && currentCount >= policy.maxCount) {
|
|
161
|
+
throw new ArtifactError("invalid_content", `${kind} artifacts accept at most ${policy.maxCount} file assets`);
|
|
162
|
+
}
|
|
163
|
+
if (policy.acceptedMediaTypes?.length && !policy.acceptedMediaTypes.some((accepted) => mediaTypeMatches(accepted, input.mediaType))) {
|
|
164
|
+
throw new ArtifactError("invalid_content", `${input.mediaType} is not accepted by ${kind} artifacts`);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
61
167
|
const get = async (ownerId, artifactId) => {
|
|
62
168
|
const artifact = await options.store.get(ownerId, artifactId);
|
|
63
169
|
if (!artifact) {
|
|
@@ -83,6 +189,28 @@ var createArtifactService = (options) => {
|
|
|
83
189
|
updatedAt: now()
|
|
84
190
|
}, current.revision);
|
|
85
191
|
},
|
|
192
|
+
attach: async (ownerId, artifactId, input, expectedRevision) => {
|
|
193
|
+
const current = await get(ownerId, artifactId);
|
|
194
|
+
requireCapability(current, "attach");
|
|
195
|
+
validateNewAsset(current.kind, input, current.assets.length);
|
|
196
|
+
if (!options.assetStore) {
|
|
197
|
+
throw new ArtifactError("asset_store_unavailable", "No artifact asset store is configured");
|
|
198
|
+
}
|
|
199
|
+
const reference = await options.assetStore.write(input, {
|
|
200
|
+
artifact: current,
|
|
201
|
+
idempotencyKey: `artifact:${current.id}:asset:${current.revision + 1}`
|
|
202
|
+
});
|
|
203
|
+
const assets = validateAssets(current.kind, [
|
|
204
|
+
...current.assets.filter((asset) => asset.id !== reference.id),
|
|
205
|
+
reference
|
|
206
|
+
]);
|
|
207
|
+
return saveRevision({
|
|
208
|
+
...current,
|
|
209
|
+
assets,
|
|
210
|
+
revision: current.revision + 1,
|
|
211
|
+
updatedAt: now()
|
|
212
|
+
}, expectedRevision ?? current.revision);
|
|
213
|
+
},
|
|
86
214
|
create: async (ownerId, input) => {
|
|
87
215
|
const definition = options.registry.definitions[input.kind];
|
|
88
216
|
if (!definition) {
|
|
@@ -91,6 +219,7 @@ var createArtifactService = (options) => {
|
|
|
91
219
|
const content = options.registry.parse(input.kind, input.content);
|
|
92
220
|
const timestamp = now();
|
|
93
221
|
const artifact = {
|
|
222
|
+
assets: validateAssets(input.kind, input.assets ?? []),
|
|
94
223
|
capabilities: definition.capabilities ?? ["archive", "edit", "preview"],
|
|
95
224
|
content,
|
|
96
225
|
createdAt: timestamp,
|
|
@@ -109,8 +238,30 @@ var createArtifactService = (options) => {
|
|
|
109
238
|
await options.store.create(artifact);
|
|
110
239
|
return artifact;
|
|
111
240
|
},
|
|
241
|
+
detach: async (ownerId, artifactId, assetId, expectedRevision) => {
|
|
242
|
+
const current = await get(ownerId, artifactId);
|
|
243
|
+
requireCapability(current, "attach");
|
|
244
|
+
const assets = current.assets.filter((asset) => asset.id !== assetId);
|
|
245
|
+
if (assets.length === current.assets.length) {
|
|
246
|
+
throw new ArtifactError("not_found", "Artifact asset not found");
|
|
247
|
+
}
|
|
248
|
+
return saveRevision({
|
|
249
|
+
...current,
|
|
250
|
+
assets,
|
|
251
|
+
revision: current.revision + 1,
|
|
252
|
+
updatedAt: now()
|
|
253
|
+
}, expectedRevision ?? current.revision);
|
|
254
|
+
},
|
|
112
255
|
get,
|
|
256
|
+
getRevision: async (ownerId, artifactId, revision) => {
|
|
257
|
+
const snapshot = await options.store.getRevision(ownerId, artifactId, revision);
|
|
258
|
+
if (!snapshot) {
|
|
259
|
+
throw new ArtifactError("not_found", "Artifact revision not found");
|
|
260
|
+
}
|
|
261
|
+
return snapshot;
|
|
262
|
+
},
|
|
113
263
|
list: (ownerId, query) => options.store.list(ownerId, query),
|
|
264
|
+
listRevisions: (ownerId, artifactId) => options.store.listRevisions(ownerId, artifactId),
|
|
114
265
|
publish: async (ownerId, artifactId) => {
|
|
115
266
|
const current = await get(ownerId, artifactId);
|
|
116
267
|
requireCapability(current, "publish");
|
|
@@ -151,6 +302,40 @@ var createArtifactService = (options) => {
|
|
|
151
302
|
updatedAt: now()
|
|
152
303
|
}, current.revision);
|
|
153
304
|
},
|
|
305
|
+
readAsset: async (ownerId, artifactId, assetId) => {
|
|
306
|
+
const artifact = await get(ownerId, artifactId);
|
|
307
|
+
const asset = artifact.assets.find((candidate) => candidate.id === assetId);
|
|
308
|
+
if (!asset) {
|
|
309
|
+
throw new ArtifactError("not_found", "Artifact asset not found");
|
|
310
|
+
}
|
|
311
|
+
if (!options.assetStore) {
|
|
312
|
+
throw new ArtifactError("asset_store_unavailable", "No artifact asset store is configured");
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
asset,
|
|
316
|
+
data: await options.assetStore.read(asset, { artifact })
|
|
317
|
+
};
|
|
318
|
+
},
|
|
319
|
+
restore: async (ownerId, artifactId, revision, expectedRevision) => {
|
|
320
|
+
const current = await get(ownerId, artifactId);
|
|
321
|
+
requireCapability(current, "edit");
|
|
322
|
+
const snapshot = await options.store.getRevision(ownerId, artifactId, revision);
|
|
323
|
+
if (!snapshot) {
|
|
324
|
+
throw new ArtifactError("not_found", "Artifact revision not found");
|
|
325
|
+
}
|
|
326
|
+
const timestamp = now();
|
|
327
|
+
return saveRevision({
|
|
328
|
+
...current,
|
|
329
|
+
assets: validateAssets(current.kind, snapshot.assets),
|
|
330
|
+
content: options.registry.parse(current.kind, snapshot.content),
|
|
331
|
+
metadata: snapshot.metadata,
|
|
332
|
+
publication: undefined,
|
|
333
|
+
revision: current.revision + 1,
|
|
334
|
+
status: "draft",
|
|
335
|
+
title: snapshot.title,
|
|
336
|
+
updatedAt: timestamp
|
|
337
|
+
}, expectedRevision ?? current.revision);
|
|
338
|
+
},
|
|
154
339
|
update: async (ownerId, artifactId, input) => {
|
|
155
340
|
const current = await get(ownerId, artifactId);
|
|
156
341
|
requireCapability(current, "edit");
|
|
@@ -158,6 +343,7 @@ var createArtifactService = (options) => {
|
|
|
158
343
|
const content = input.content === undefined ? current.content : options.registry.parse(current.kind, input.content);
|
|
159
344
|
return saveRevision({
|
|
160
345
|
...current,
|
|
346
|
+
assets: input.assets === undefined ? current.assets : validateAssets(current.kind, input.assets),
|
|
161
347
|
content,
|
|
162
348
|
metadata: input.metadata ?? current.metadata,
|
|
163
349
|
revision: current.revision + 1,
|
|
@@ -168,32 +354,89 @@ var createArtifactService = (options) => {
|
|
|
168
354
|
};
|
|
169
355
|
};
|
|
170
356
|
// src/store.ts
|
|
357
|
+
import { createHash } from "crypto";
|
|
171
358
|
var clone = (value) => structuredClone(value);
|
|
359
|
+
var createMemoryArtifactAssetStore = () => {
|
|
360
|
+
const bytes = new Map;
|
|
361
|
+
const references = new Map;
|
|
362
|
+
const idempotency = new Map;
|
|
363
|
+
return {
|
|
364
|
+
read: async (reference) => {
|
|
365
|
+
const data = bytes.get(reference.id);
|
|
366
|
+
if (!data)
|
|
367
|
+
throw new Error(`Artifact asset not found: ${reference.id}`);
|
|
368
|
+
return clone(data);
|
|
369
|
+
},
|
|
370
|
+
write: async (input, context) => {
|
|
371
|
+
const existingId = idempotency.get(context.idempotencyKey);
|
|
372
|
+
if (existingId)
|
|
373
|
+
return clone(references.get(existingId));
|
|
374
|
+
const id = crypto.randomUUID();
|
|
375
|
+
const reference = {
|
|
376
|
+
checksum: {
|
|
377
|
+
algorithm: "sha256",
|
|
378
|
+
value: createHash("sha256").update(input.data).digest("hex")
|
|
379
|
+
},
|
|
380
|
+
id,
|
|
381
|
+
mediaType: input.mediaType,
|
|
382
|
+
metadata: input.metadata,
|
|
383
|
+
name: input.name,
|
|
384
|
+
role: input.role ?? "attachment",
|
|
385
|
+
size: input.data.byteLength,
|
|
386
|
+
uri: `memory://${id}`
|
|
387
|
+
};
|
|
388
|
+
bytes.set(id, clone(input.data));
|
|
389
|
+
references.set(id, clone(reference));
|
|
390
|
+
idempotency.set(context.idempotencyKey, id);
|
|
391
|
+
return reference;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
};
|
|
172
395
|
var createMemoryArtifactStore = (initial = []) => {
|
|
173
396
|
const records = new Map(initial.map((record) => [record.id, clone(record)]));
|
|
397
|
+
const revisions = new Map;
|
|
398
|
+
for (const record of initial)
|
|
399
|
+
revisions.set(record.id, [clone(record)]);
|
|
174
400
|
return {
|
|
175
401
|
create: async (record) => {
|
|
176
402
|
if (records.has(record.id))
|
|
177
403
|
throw new Error(`Duplicate artifact id: ${record.id}`);
|
|
178
404
|
records.set(record.id, clone(record));
|
|
405
|
+
revisions.set(record.id, [clone(record)]);
|
|
179
406
|
},
|
|
180
407
|
get: async (ownerId, artifactId) => {
|
|
181
408
|
const record = records.get(artifactId);
|
|
182
409
|
return record?.ownerId === ownerId ? clone(record) : null;
|
|
183
410
|
},
|
|
411
|
+
getRevision: async (ownerId, artifactId, revision) => {
|
|
412
|
+
const current = records.get(artifactId);
|
|
413
|
+
if (current?.ownerId !== ownerId)
|
|
414
|
+
return null;
|
|
415
|
+
return clone(revisions.get(artifactId)?.find((candidate) => candidate.revision === revision) ?? null);
|
|
416
|
+
},
|
|
184
417
|
list: async (ownerId, query = {}) => [...records.values()].filter((record) => record.ownerId === ownerId && (!query.kind || record.kind === query.kind) && (!query.status || record.status === query.status)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, query.limit ?? Number.POSITIVE_INFINITY).map(clone),
|
|
418
|
+
listRevisions: async (ownerId, artifactId) => {
|
|
419
|
+
const current = records.get(artifactId);
|
|
420
|
+
if (current?.ownerId !== ownerId)
|
|
421
|
+
return [];
|
|
422
|
+
return (revisions.get(artifactId) ?? []).toSorted((left, right) => right.revision - left.revision).map(clone);
|
|
423
|
+
},
|
|
185
424
|
save: async (record, expectedRevision) => {
|
|
186
425
|
const current = records.get(record.id);
|
|
187
426
|
if (!current || current.ownerId !== record.ownerId || current.revision !== expectedRevision) {
|
|
188
427
|
return false;
|
|
189
428
|
}
|
|
190
429
|
records.set(record.id, clone(record));
|
|
430
|
+
revisions.set(record.id, [
|
|
431
|
+
...revisions.get(record.id) ?? [],
|
|
432
|
+
clone(record)
|
|
433
|
+
]);
|
|
191
434
|
return true;
|
|
192
435
|
}
|
|
193
436
|
};
|
|
194
437
|
};
|
|
195
438
|
// src/tools.ts
|
|
196
|
-
import { Type } from "@sinclair/typebox";
|
|
439
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
197
440
|
var record = (input) => input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
198
441
|
var stringValue = (input, key) => typeof input[key] === "string" ? input[key] : undefined;
|
|
199
442
|
var createArtifactTools = (options) => ({
|
|
@@ -214,10 +457,10 @@ var createArtifactTools = (options) => ({
|
|
|
214
457
|
});
|
|
215
458
|
return JSON.stringify(artifact);
|
|
216
459
|
},
|
|
217
|
-
input:
|
|
218
|
-
content:
|
|
219
|
-
kind:
|
|
220
|
-
title:
|
|
460
|
+
input: Type2.Object({
|
|
461
|
+
content: Type2.Unknown(),
|
|
462
|
+
kind: Type2.String({ minLength: 1 }),
|
|
463
|
+
title: Type2.String({ minLength: 1 })
|
|
221
464
|
})
|
|
222
465
|
},
|
|
223
466
|
artifact_get: {
|
|
@@ -229,7 +472,7 @@ var createArtifactTools = (options) => ({
|
|
|
229
472
|
return "Provide artifactId.";
|
|
230
473
|
return JSON.stringify(await options.service.get(options.ownerId, artifactId));
|
|
231
474
|
},
|
|
232
|
-
input:
|
|
475
|
+
input: Type2.Object({ artifactId: Type2.String({ minLength: 1 }) })
|
|
233
476
|
},
|
|
234
477
|
artifact_list: {
|
|
235
478
|
annotations: { readOnlyHint: true },
|
|
@@ -242,11 +485,22 @@ var createArtifactTools = (options) => ({
|
|
|
242
485
|
status: ARTIFACT_STATUSES.find((candidate) => candidate === status)
|
|
243
486
|
}));
|
|
244
487
|
},
|
|
245
|
-
input:
|
|
246
|
-
kind:
|
|
247
|
-
status:
|
|
488
|
+
input: Type2.Object({
|
|
489
|
+
kind: Type2.Optional(Type2.String()),
|
|
490
|
+
status: Type2.Optional(Type2.Union(ARTIFACT_STATUSES.map((status) => Type2.Literal(status))))
|
|
248
491
|
})
|
|
249
492
|
},
|
|
493
|
+
artifact_history: {
|
|
494
|
+
annotations: { readOnlyHint: true },
|
|
495
|
+
description: "List the immutable revisions of one owned artifact.",
|
|
496
|
+
handler: async (raw) => {
|
|
497
|
+
const artifactId = stringValue(record(raw), "artifactId");
|
|
498
|
+
if (!artifactId)
|
|
499
|
+
return "Provide artifactId.";
|
|
500
|
+
return JSON.stringify(await options.service.listRevisions(options.ownerId, artifactId));
|
|
501
|
+
},
|
|
502
|
+
input: Type2.Object({ artifactId: Type2.String({ minLength: 1 }) })
|
|
503
|
+
},
|
|
250
504
|
artifact_publish: {
|
|
251
505
|
description: "Publish or unpublish an artifact. Hosts should expose this tool only when the user explicitly controls public access.",
|
|
252
506
|
handler: async (raw) => {
|
|
@@ -258,9 +512,25 @@ var createArtifactTools = (options) => ({
|
|
|
258
512
|
const artifact = input.published ? await options.service.publish(options.ownerId, artifactId) : await options.service.unpublish(options.ownerId, artifactId);
|
|
259
513
|
return JSON.stringify(artifact);
|
|
260
514
|
},
|
|
261
|
-
input:
|
|
262
|
-
artifactId:
|
|
263
|
-
published:
|
|
515
|
+
input: Type2.Object({
|
|
516
|
+
artifactId: Type2.String({ minLength: 1 }),
|
|
517
|
+
published: Type2.Boolean()
|
|
518
|
+
})
|
|
519
|
+
},
|
|
520
|
+
artifact_restore: {
|
|
521
|
+
description: "Restore an immutable artifact revision as a new private draft revision.",
|
|
522
|
+
handler: async (raw) => {
|
|
523
|
+
const input = record(raw);
|
|
524
|
+
const artifactId = stringValue(input, "artifactId");
|
|
525
|
+
if (!artifactId || typeof input.revision !== "number") {
|
|
526
|
+
return "Provide artifactId and revision.";
|
|
527
|
+
}
|
|
528
|
+
return JSON.stringify(await options.service.restore(options.ownerId, artifactId, input.revision, typeof input.expectedRevision === "number" ? input.expectedRevision : undefined));
|
|
529
|
+
},
|
|
530
|
+
input: Type2.Object({
|
|
531
|
+
artifactId: Type2.String({ minLength: 1 }),
|
|
532
|
+
expectedRevision: Type2.Optional(Type2.Integer({ minimum: 1 })),
|
|
533
|
+
revision: Type2.Integer({ minimum: 1 })
|
|
264
534
|
})
|
|
265
535
|
},
|
|
266
536
|
artifact_update: {
|
|
@@ -277,20 +547,23 @@ var createArtifactTools = (options) => ({
|
|
|
277
547
|
});
|
|
278
548
|
return JSON.stringify(artifact);
|
|
279
549
|
},
|
|
280
|
-
input:
|
|
281
|
-
artifactId:
|
|
282
|
-
content:
|
|
283
|
-
expectedRevision:
|
|
284
|
-
title:
|
|
550
|
+
input: Type2.Object({
|
|
551
|
+
artifactId: Type2.String({ minLength: 1 }),
|
|
552
|
+
content: Type2.Optional(Type2.Unknown()),
|
|
553
|
+
expectedRevision: Type2.Optional(Type2.Integer({ minimum: 1 })),
|
|
554
|
+
title: Type2.Optional(Type2.String({ minLength: 1 }))
|
|
285
555
|
})
|
|
286
556
|
}
|
|
287
557
|
});
|
|
288
558
|
export {
|
|
559
|
+
standardArtifactDefinitions,
|
|
289
560
|
defineArtifactRegistry,
|
|
290
561
|
createMemoryArtifactStore,
|
|
562
|
+
createMemoryArtifactAssetStore,
|
|
291
563
|
createArtifactTools,
|
|
292
564
|
createArtifactService,
|
|
293
565
|
createArtifactRendererRegistry,
|
|
566
|
+
STANDARD_ARTIFACT_KIND_NAMES,
|
|
294
567
|
ArtifactError,
|
|
295
568
|
ARTIFACT_STATUSES
|
|
296
569
|
};
|
package/dist/manifest.js
CHANGED
|
@@ -5954,6 +5954,24 @@ var manifest = defineManifest()({
|
|
|
5954
5954
|
kind: Type2.Optional(Type2.String({ minLength: 1 })),
|
|
5955
5955
|
ownerId: Type2.String({ minLength: 1 })
|
|
5956
5956
|
})
|
|
5957
|
+
}),
|
|
5958
|
+
artifact_history: tool.runtime({
|
|
5959
|
+
annotations: { readOnlyHint: true },
|
|
5960
|
+
description: "List immutable revisions of one artifact owned by a user.",
|
|
5961
|
+
handler: async ({ artifactId, ownerId }, service) => JSON.stringify(await service.listRevisions(ownerId, artifactId)),
|
|
5962
|
+
input: Type2.Object({
|
|
5963
|
+
artifactId: Type2.String({ minLength: 1 }),
|
|
5964
|
+
ownerId: Type2.String({ minLength: 1 })
|
|
5965
|
+
})
|
|
5966
|
+
}),
|
|
5967
|
+
artifact_restore: tool.runtime({
|
|
5968
|
+
description: "Restore an old artifact revision as a new private draft.",
|
|
5969
|
+
handler: async ({ artifactId, ownerId, revision }, service) => JSON.stringify(await service.restore(ownerId, artifactId, revision)),
|
|
5970
|
+
input: Type2.Object({
|
|
5971
|
+
artifactId: Type2.String({ minLength: 1 }),
|
|
5972
|
+
ownerId: Type2.String({ minLength: 1 }),
|
|
5973
|
+
revision: Type2.Integer({ minimum: 1 })
|
|
5974
|
+
})
|
|
5957
5975
|
})
|
|
5958
5976
|
},
|
|
5959
5977
|
wiring: [
|
package/dist/manifest.json
CHANGED
|
@@ -95,6 +95,56 @@
|
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
"kind": "runtime"
|
|
98
|
+
},
|
|
99
|
+
"artifact_history": {
|
|
100
|
+
"annotations": {
|
|
101
|
+
"readOnlyHint": true
|
|
102
|
+
},
|
|
103
|
+
"description": "List immutable revisions of one artifact owned by a user.",
|
|
104
|
+
"input": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"required": [
|
|
107
|
+
"artifactId",
|
|
108
|
+
"ownerId"
|
|
109
|
+
],
|
|
110
|
+
"properties": {
|
|
111
|
+
"artifactId": {
|
|
112
|
+
"minLength": 1,
|
|
113
|
+
"type": "string"
|
|
114
|
+
},
|
|
115
|
+
"ownerId": {
|
|
116
|
+
"minLength": 1,
|
|
117
|
+
"type": "string"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"kind": "runtime"
|
|
122
|
+
},
|
|
123
|
+
"artifact_restore": {
|
|
124
|
+
"description": "Restore an old artifact revision as a new private draft.",
|
|
125
|
+
"input": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"required": [
|
|
128
|
+
"artifactId",
|
|
129
|
+
"ownerId",
|
|
130
|
+
"revision"
|
|
131
|
+
],
|
|
132
|
+
"properties": {
|
|
133
|
+
"artifactId": {
|
|
134
|
+
"minLength": 1,
|
|
135
|
+
"type": "string"
|
|
136
|
+
},
|
|
137
|
+
"ownerId": {
|
|
138
|
+
"minLength": 1,
|
|
139
|
+
"type": "string"
|
|
140
|
+
},
|
|
141
|
+
"revision": {
|
|
142
|
+
"minimum": 1,
|
|
143
|
+
"type": "integer"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"kind": "runtime"
|
|
98
148
|
}
|
|
99
149
|
}
|
|
100
150
|
}
|
package/dist/rag.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/rag.ts
|
|
3
|
+
import { Buffer } from "buffer";
|
|
4
|
+
var artifactMetadata = (artifact) => ({
|
|
5
|
+
artifactId: artifact.id,
|
|
6
|
+
artifactKind: artifact.kind,
|
|
7
|
+
artifactRevision: artifact.revision,
|
|
8
|
+
artifactStatus: artifact.status,
|
|
9
|
+
...artifact.metadata
|
|
10
|
+
});
|
|
11
|
+
var artifactToRAGUploads = async (artifact, reader, options = {}) => {
|
|
12
|
+
const metadata = artifactMetadata(artifact);
|
|
13
|
+
const uploads = await Promise.all(artifact.assets.map(async (asset) => ({
|
|
14
|
+
content: Buffer.from(await reader.read(asset, { artifact })).toString("base64"),
|
|
15
|
+
contentType: asset.mediaType,
|
|
16
|
+
encoding: "base64",
|
|
17
|
+
metadata: {
|
|
18
|
+
...metadata,
|
|
19
|
+
artifactAssetId: asset.id,
|
|
20
|
+
artifactAssetRole: asset.role,
|
|
21
|
+
...asset.metadata
|
|
22
|
+
},
|
|
23
|
+
name: asset.name,
|
|
24
|
+
source: `artifact:${artifact.id}:revision:${artifact.revision}:asset:${asset.id}`,
|
|
25
|
+
title: artifact.title
|
|
26
|
+
})));
|
|
27
|
+
if (options.includeStructuredContent === false)
|
|
28
|
+
return uploads;
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
content: JSON.stringify(artifact.content),
|
|
32
|
+
contentType: "application/json",
|
|
33
|
+
encoding: "utf8",
|
|
34
|
+
metadata: { ...metadata, artifactStructuredContent: true },
|
|
35
|
+
name: `${artifact.kind}-${artifact.id}-r${artifact.revision}.json`,
|
|
36
|
+
source: `artifact:${artifact.id}:revision:${artifact.revision}:content`,
|
|
37
|
+
title: artifact.title
|
|
38
|
+
},
|
|
39
|
+
...uploads
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
export {
|
|
43
|
+
artifactToRAGUploads
|
|
44
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* retains authorization, persistence, URLs, UI, and delivery policy.
|
|
8
8
|
*/
|
|
9
9
|
export { defineArtifactRegistry, type ArtifactContent, type ArtifactKindDefinition, type ArtifactKindDefinitions, type ArtifactRegistry, } from "./registry";
|
|
10
|
+
export { STANDARD_ARTIFACT_KIND_NAMES, standardArtifactDefinitions, } from "./standardKinds";
|
|
10
11
|
export { createArtifactRendererRegistry, type ArtifactRenderer, type ArtifactRendererRegistry, type ArtifactRenderResult, } from "./renderers";
|
|
11
12
|
export { createArtifactService, type ArtifactPublisher, type ArtifactService, type ArtifactServiceOptions, } from "./service";
|
|
12
|
-
export { createMemoryArtifactStore, type ArtifactStore } from "./store";
|
|
13
|
+
export { createMemoryArtifactStore, createMemoryArtifactAssetStore, type ArtifactAssetStore, type ArtifactStore, } from "./store";
|
|
13
14
|
export { createArtifactTools, type ArtifactToolDefinition, type ArtifactToolMap, type ArtifactToolOptions, } from "./tools";
|
|
14
|
-
export { ARTIFACT_STATUSES, ArtifactError, type ArtifactCapability, type ArtifactCreateInput, type ArtifactErrorCode, type ArtifactListQuery, type ArtifactProvenance, type ArtifactPublication, type ArtifactRecord, type ArtifactStatus, type ArtifactUpdateInput, } from "./types";
|
|
15
|
+
export { ARTIFACT_STATUSES, ArtifactError, type ArtifactAssetReference, type ArtifactAssetRole, type ArtifactAssetWriteInput, type ArtifactCapability, type ArtifactCreateInput, type ArtifactErrorCode, type ArtifactListQuery, type ArtifactProvenance, type ArtifactPublication, type ArtifactRecord, type ArtifactRevision, type ArtifactStatus, type ArtifactUpdateInput, } from "./types";
|
package/dist/src/manifest.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
export declare const manifest: import("@absolutejs/manifest").PackageManifest<Record<string, never>, {
|
|
2
2
|
archive: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
|
|
3
|
+
attach: (ownerId: string, artifactId: string, input: import("./types").ArtifactAssetWriteInput, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
|
|
3
4
|
create: (ownerId: string, input: import("./types").ArtifactCreateInput) => Promise<import("./types").ArtifactRecord>;
|
|
5
|
+
detach: (ownerId: string, artifactId: string, assetId: string, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
|
|
4
6
|
get: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
|
|
7
|
+
getRevision: (ownerId: string, artifactId: string, revision: number) => Promise<Readonly<import("./types").ArtifactRecord<unknown>>>;
|
|
5
8
|
list: (ownerId: string, query?: import("./types").ArtifactListQuery) => Promise<import("./types").ArtifactRecord[]>;
|
|
9
|
+
listRevisions: (ownerId: string, artifactId: string) => Promise<Readonly<import("./types").ArtifactRecord<unknown>>[]>;
|
|
6
10
|
publish: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
|
|
7
11
|
unpublish: (ownerId: string, artifactId: string) => Promise<import("./types").ArtifactRecord>;
|
|
12
|
+
readAsset: (ownerId: string, artifactId: string, assetId: string) => Promise<{
|
|
13
|
+
asset: import("./types").ArtifactAssetReference;
|
|
14
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
15
|
+
}>;
|
|
16
|
+
restore: (ownerId: string, artifactId: string, revision: number, expectedRevision?: number) => Promise<import("./types").ArtifactRecord>;
|
|
8
17
|
update: (ownerId: string, artifactId: string, input: import("./types").ArtifactUpdateInput) => Promise<import("./types").ArtifactRecord>;
|
|
9
18
|
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RAGDocumentUploadInput } from "@absolutejs/rag";
|
|
2
|
+
import type { ArtifactAssetReference, ArtifactRecord } from "./types";
|
|
3
|
+
export type ArtifactRAGAssetReader = {
|
|
4
|
+
read(reference: ArtifactAssetReference, context: {
|
|
5
|
+
artifact: ArtifactRecord;
|
|
6
|
+
}): Promise<Uint8Array>;
|
|
7
|
+
};
|
|
8
|
+
export type ArtifactRAGUploadOptions = {
|
|
9
|
+
includeStructuredContent?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Resolve an artifact revision into upload inputs accepted by @absolutejs/rag.
|
|
13
|
+
* Storage URIs remain opaque; only the supplied reader is allowed to access bytes.
|
|
14
|
+
*/
|
|
15
|
+
export declare const artifactToRAGUploads: (artifact: ArtifactRecord, reader: ArtifactRAGAssetReader, options?: ArtifactRAGUploadOptions) => Promise<RAGDocumentUploadInput[]>;
|
package/dist/src/registry.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Static, TSchema } from "@sinclair/typebox";
|
|
2
2
|
import { type ArtifactCapability } from "./types";
|
|
3
|
+
export type ArtifactAssetPolicy = {
|
|
4
|
+
/** Exact media types or wildcards such as image/* and application/*. */
|
|
5
|
+
acceptedMediaTypes?: string[];
|
|
6
|
+
maxCount?: number;
|
|
7
|
+
};
|
|
3
8
|
export type ArtifactKindDefinition<TContent extends TSchema = TSchema> = {
|
|
9
|
+
assets?: ArtifactAssetPolicy;
|
|
4
10
|
capabilities?: ArtifactCapability[];
|
|
5
11
|
content: TContent;
|
|
6
12
|
description?: string;
|
package/dist/src/service.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ArtifactKindDefinitions, ArtifactRegistry } from "./registry";
|
|
2
|
-
import type { ArtifactStore } from "./store";
|
|
3
|
-
import { type ArtifactCreateInput, type ArtifactListQuery, type ArtifactRecord, type ArtifactUpdateInput } from "./types";
|
|
2
|
+
import type { ArtifactAssetStore, ArtifactStore } from "./store";
|
|
3
|
+
import { type ArtifactAssetReference, type ArtifactAssetWriteInput, type ArtifactCreateInput, type ArtifactListQuery, type ArtifactRecord, type ArtifactUpdateInput } from "./types";
|
|
4
4
|
export type ArtifactPublisher = {
|
|
5
5
|
publish(artifact: ArtifactRecord, options: {
|
|
6
6
|
idempotencyKey: string;
|
|
@@ -13,6 +13,7 @@ export type ArtifactPublisher = {
|
|
|
13
13
|
}): Promise<void>;
|
|
14
14
|
};
|
|
15
15
|
export type ArtifactServiceOptions<TDefinitions extends ArtifactKindDefinitions = ArtifactKindDefinitions> = {
|
|
16
|
+
assetStore?: ArtifactAssetStore;
|
|
16
17
|
clock?: () => Date;
|
|
17
18
|
idFactory?: () => string;
|
|
18
19
|
publisher?: ArtifactPublisher;
|
|
@@ -22,10 +23,19 @@ export type ArtifactServiceOptions<TDefinitions extends ArtifactKindDefinitions
|
|
|
22
23
|
export type ArtifactService = ReturnType<typeof createArtifactService>;
|
|
23
24
|
export declare const createArtifactService: <TDefinitions extends ArtifactKindDefinitions>(options: ArtifactServiceOptions<TDefinitions>) => {
|
|
24
25
|
archive: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
|
|
26
|
+
attach: (ownerId: string, artifactId: string, input: ArtifactAssetWriteInput, expectedRevision?: number) => Promise<ArtifactRecord>;
|
|
25
27
|
create: (ownerId: string, input: ArtifactCreateInput) => Promise<ArtifactRecord>;
|
|
28
|
+
detach: (ownerId: string, artifactId: string, assetId: string, expectedRevision?: number) => Promise<ArtifactRecord>;
|
|
26
29
|
get: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
|
|
30
|
+
getRevision: (ownerId: string, artifactId: string, revision: number) => Promise<Readonly<ArtifactRecord<unknown>>>;
|
|
27
31
|
list: (ownerId: string, query?: ArtifactListQuery) => Promise<ArtifactRecord[]>;
|
|
32
|
+
listRevisions: (ownerId: string, artifactId: string) => Promise<Readonly<ArtifactRecord<unknown>>[]>;
|
|
28
33
|
publish: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
|
|
29
34
|
unpublish: (ownerId: string, artifactId: string) => Promise<ArtifactRecord>;
|
|
35
|
+
readAsset: (ownerId: string, artifactId: string, assetId: string) => Promise<{
|
|
36
|
+
asset: ArtifactAssetReference;
|
|
37
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
38
|
+
}>;
|
|
39
|
+
restore: (ownerId: string, artifactId: string, revision: number, expectedRevision?: number) => Promise<ArtifactRecord>;
|
|
30
40
|
update: (ownerId: string, artifactId: string, input: ArtifactUpdateInput) => Promise<ArtifactRecord>;
|
|
31
41
|
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export declare const standardArtifactDefinitions: {
|
|
2
|
+
archive: {
|
|
3
|
+
assets: {
|
|
4
|
+
acceptedMediaTypes: string[];
|
|
5
|
+
maxCount: number;
|
|
6
|
+
};
|
|
7
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
8
|
+
content: import("@sinclair/typebox").TObject<{
|
|
9
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
10
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
11
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
12
|
+
}>;
|
|
13
|
+
label: string;
|
|
14
|
+
schemaVersion: number;
|
|
15
|
+
};
|
|
16
|
+
audio: {
|
|
17
|
+
assets: {
|
|
18
|
+
acceptedMediaTypes: string[];
|
|
19
|
+
maxCount: number;
|
|
20
|
+
};
|
|
21
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
22
|
+
content: import("@sinclair/typebox").TObject<{
|
|
23
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
24
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
25
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
26
|
+
}>;
|
|
27
|
+
label: string;
|
|
28
|
+
schemaVersion: number;
|
|
29
|
+
};
|
|
30
|
+
code: {
|
|
31
|
+
assets: {
|
|
32
|
+
acceptedMediaTypes: string[];
|
|
33
|
+
maxCount: number;
|
|
34
|
+
};
|
|
35
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
36
|
+
content: import("@sinclair/typebox").TObject<{
|
|
37
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
38
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
39
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
40
|
+
}>;
|
|
41
|
+
label: string;
|
|
42
|
+
schemaVersion: number;
|
|
43
|
+
};
|
|
44
|
+
dataset: {
|
|
45
|
+
assets: {
|
|
46
|
+
acceptedMediaTypes: string[];
|
|
47
|
+
maxCount: number;
|
|
48
|
+
};
|
|
49
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
50
|
+
content: import("@sinclair/typebox").TObject<{
|
|
51
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
52
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
53
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
54
|
+
}>;
|
|
55
|
+
label: string;
|
|
56
|
+
schemaVersion: number;
|
|
57
|
+
};
|
|
58
|
+
document: {
|
|
59
|
+
assets: {
|
|
60
|
+
acceptedMediaTypes: string[];
|
|
61
|
+
maxCount: number;
|
|
62
|
+
};
|
|
63
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
64
|
+
content: import("@sinclair/typebox").TObject<{
|
|
65
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
66
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
67
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
68
|
+
}>;
|
|
69
|
+
label: string;
|
|
70
|
+
schemaVersion: number;
|
|
71
|
+
};
|
|
72
|
+
email: {
|
|
73
|
+
assets: {
|
|
74
|
+
acceptedMediaTypes: string[];
|
|
75
|
+
maxCount: number;
|
|
76
|
+
};
|
|
77
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
78
|
+
content: import("@sinclair/typebox").TObject<{
|
|
79
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
80
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
81
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
82
|
+
}>;
|
|
83
|
+
label: string;
|
|
84
|
+
schemaVersion: number;
|
|
85
|
+
};
|
|
86
|
+
file: {
|
|
87
|
+
assets: {
|
|
88
|
+
acceptedMediaTypes: string[];
|
|
89
|
+
maxCount: number;
|
|
90
|
+
};
|
|
91
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
92
|
+
content: import("@sinclair/typebox").TObject<{
|
|
93
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
94
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
95
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
96
|
+
}>;
|
|
97
|
+
label: string;
|
|
98
|
+
schemaVersion: number;
|
|
99
|
+
};
|
|
100
|
+
image: {
|
|
101
|
+
assets: {
|
|
102
|
+
acceptedMediaTypes: string[];
|
|
103
|
+
maxCount: number;
|
|
104
|
+
};
|
|
105
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
106
|
+
content: import("@sinclair/typebox").TObject<{
|
|
107
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
108
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
109
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
110
|
+
}>;
|
|
111
|
+
label: string;
|
|
112
|
+
schemaVersion: number;
|
|
113
|
+
};
|
|
114
|
+
presentation: {
|
|
115
|
+
assets: {
|
|
116
|
+
acceptedMediaTypes: string[];
|
|
117
|
+
maxCount: number;
|
|
118
|
+
};
|
|
119
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
120
|
+
content: import("@sinclair/typebox").TObject<{
|
|
121
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
122
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
123
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
124
|
+
}>;
|
|
125
|
+
label: string;
|
|
126
|
+
schemaVersion: number;
|
|
127
|
+
};
|
|
128
|
+
spreadsheet: {
|
|
129
|
+
assets: {
|
|
130
|
+
acceptedMediaTypes: string[];
|
|
131
|
+
maxCount: number;
|
|
132
|
+
};
|
|
133
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
134
|
+
content: import("@sinclair/typebox").TObject<{
|
|
135
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
136
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
137
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
138
|
+
}>;
|
|
139
|
+
label: string;
|
|
140
|
+
schemaVersion: number;
|
|
141
|
+
};
|
|
142
|
+
video: {
|
|
143
|
+
assets: {
|
|
144
|
+
acceptedMediaTypes: string[];
|
|
145
|
+
maxCount: number;
|
|
146
|
+
};
|
|
147
|
+
capabilities: ("attach" | "archive" | "edit" | "export" | "preview" | "refine")[];
|
|
148
|
+
content: import("@sinclair/typebox").TObject<{
|
|
149
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
150
|
+
instructions: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
151
|
+
summary: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
152
|
+
}>;
|
|
153
|
+
label: string;
|
|
154
|
+
schemaVersion: number;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
export declare const STANDARD_ARTIFACT_KIND_NAMES: Array<keyof typeof standardArtifactDefinitions>;
|
package/dist/src/store.d.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import type { ArtifactListQuery, ArtifactRecord } from "./types";
|
|
1
|
+
import type { ArtifactAssetReference, ArtifactAssetWriteInput, ArtifactListQuery, ArtifactRecord, ArtifactRevision } from "./types";
|
|
2
|
+
export type ArtifactAssetStore = {
|
|
3
|
+
read(reference: ArtifactAssetReference, context: {
|
|
4
|
+
artifact: ArtifactRecord;
|
|
5
|
+
}): Promise<Uint8Array>;
|
|
6
|
+
write(input: ArtifactAssetWriteInput, context: {
|
|
7
|
+
artifact: ArtifactRecord;
|
|
8
|
+
idempotencyKey: string;
|
|
9
|
+
}): Promise<ArtifactAssetReference>;
|
|
10
|
+
};
|
|
2
11
|
export type ArtifactStore = {
|
|
12
|
+
/** Persist the current record and its first immutable revision atomically. */
|
|
3
13
|
create(record: ArtifactRecord): Promise<void>;
|
|
4
14
|
get(ownerId: string, artifactId: string): Promise<ArtifactRecord | null>;
|
|
15
|
+
getRevision(ownerId: string, artifactId: string, revision: number): Promise<ArtifactRevision | null>;
|
|
5
16
|
list(ownerId: string, query?: ArtifactListQuery): Promise<ArtifactRecord[]>;
|
|
17
|
+
listRevisions(ownerId: string, artifactId: string): Promise<ArtifactRevision[]>;
|
|
18
|
+
/** Compare, replace current state, and append its revision atomically. */
|
|
6
19
|
save(record: ArtifactRecord, expectedRevision: number): Promise<boolean>;
|
|
7
20
|
};
|
|
21
|
+
export declare const createMemoryArtifactAssetStore: () => ArtifactAssetStore;
|
|
8
22
|
export declare const createMemoryArtifactStore: (initial?: ArtifactRecord[]) => ArtifactStore;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const ARTIFACT_STATUSES: readonly ["draft", "published", "archived"];
|
|
2
2
|
export type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];
|
|
3
|
-
export type ArtifactCapability = "archive" | "edit" | "export" | "preview" | "publish" | "refine";
|
|
3
|
+
export type ArtifactCapability = "attach" | "archive" | "edit" | "export" | "preview" | "publish" | "refine";
|
|
4
4
|
export type ArtifactProvenance = {
|
|
5
5
|
model?: string;
|
|
6
6
|
sourceIds?: string[];
|
|
@@ -12,7 +12,30 @@ export type ArtifactPublication = {
|
|
|
12
12
|
publishedAt: string;
|
|
13
13
|
url: string;
|
|
14
14
|
};
|
|
15
|
+
export type ArtifactAssetRole = "attachment" | "primary" | "preview" | "source";
|
|
16
|
+
export type ArtifactAssetReference = {
|
|
17
|
+
checksum?: {
|
|
18
|
+
algorithm: "sha256";
|
|
19
|
+
value: string;
|
|
20
|
+
};
|
|
21
|
+
id: string;
|
|
22
|
+
mediaType: string;
|
|
23
|
+
metadata?: Record<string, unknown>;
|
|
24
|
+
name: string;
|
|
25
|
+
role: ArtifactAssetRole;
|
|
26
|
+
size: number;
|
|
27
|
+
/** Opaque host storage locator. It is not required to be publicly fetchable. */
|
|
28
|
+
uri: string;
|
|
29
|
+
};
|
|
30
|
+
export type ArtifactAssetWriteInput = {
|
|
31
|
+
data: Uint8Array;
|
|
32
|
+
mediaType: string;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
name: string;
|
|
35
|
+
role?: ArtifactAssetRole;
|
|
36
|
+
};
|
|
15
37
|
export type ArtifactRecord<TContent = unknown> = {
|
|
38
|
+
assets: ArtifactAssetReference[];
|
|
16
39
|
capabilities: ArtifactCapability[];
|
|
17
40
|
content: TContent;
|
|
18
41
|
createdAt: string;
|
|
@@ -29,12 +52,15 @@ export type ArtifactRecord<TContent = unknown> = {
|
|
|
29
52
|
title: string;
|
|
30
53
|
updatedAt: string;
|
|
31
54
|
};
|
|
55
|
+
/** An immutable point-in-time copy of an artifact record. */
|
|
56
|
+
export type ArtifactRevision<TContent = unknown> = Readonly<ArtifactRecord<TContent>>;
|
|
32
57
|
export type ArtifactListQuery = {
|
|
33
58
|
kind?: string;
|
|
34
59
|
limit?: number;
|
|
35
60
|
status?: ArtifactStatus;
|
|
36
61
|
};
|
|
37
62
|
export type ArtifactCreateInput = {
|
|
63
|
+
assets?: ArtifactAssetReference[];
|
|
38
64
|
content: unknown;
|
|
39
65
|
createdBy: string;
|
|
40
66
|
kind: string;
|
|
@@ -43,12 +69,13 @@ export type ArtifactCreateInput = {
|
|
|
43
69
|
title: string;
|
|
44
70
|
};
|
|
45
71
|
export type ArtifactUpdateInput = {
|
|
72
|
+
assets?: ArtifactAssetReference[];
|
|
46
73
|
content?: unknown;
|
|
47
74
|
expectedRevision?: number;
|
|
48
75
|
metadata?: Record<string, unknown>;
|
|
49
76
|
title?: string;
|
|
50
77
|
};
|
|
51
|
-
export type ArtifactErrorCode = "conflict" | "invalid_content" | "not_found" | "publisher_unavailable" | "renderer_unavailable" | "unsupported_capability" | "unknown_kind";
|
|
78
|
+
export type ArtifactErrorCode = "asset_store_unavailable" | "conflict" | "invalid_content" | "not_found" | "publisher_unavailable" | "renderer_unavailable" | "unsupported_capability" | "unknown_kind";
|
|
52
79
|
export declare class ArtifactError extends Error {
|
|
53
80
|
readonly code: ArtifactErrorCode;
|
|
54
81
|
constructor(code: ArtifactErrorCode, message: string);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/artifacts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Typed, versioned artifacts for AI products — schemas, lifecycle, storage, rendering, publishing, revisions, and agent tools without prescribing a database or host.",
|
|
5
5
|
"author": "Alex Kahn",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
],
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "https://github.com/absolutejs/artifacts.git"
|
|
17
|
+
"url": "git+https://github.com/absolutejs/artifacts.git"
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://github.com/absolutejs/artifacts",
|
|
20
20
|
"bugs": {
|
|
21
21
|
"url": "https://github.com/absolutejs/artifacts/issues"
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
|
-
"build": "rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --root ./src --target=bun --external @sinclair/typebox --external @sinclair/typebox/value && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
|
|
24
|
+
"build": "rm -rf dist && bun build src/index.ts src/manifest.ts src/rag.ts --outdir dist --root ./src --target=bun --external @absolutejs/rag --external @sinclair/typebox --external @sinclair/typebox/value && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
|
|
25
25
|
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
26
26
|
"release": "bun run format && bun run test && bun run build && bun publish",
|
|
27
27
|
"test": "bun test",
|
|
@@ -44,13 +44,27 @@
|
|
|
44
44
|
"import": "./dist/manifest.js",
|
|
45
45
|
"default": "./dist/manifest.js"
|
|
46
46
|
},
|
|
47
|
-
"./manifest.json": "./dist/manifest.json"
|
|
47
|
+
"./manifest.json": "./dist/manifest.json",
|
|
48
|
+
"./rag": {
|
|
49
|
+
"types": "./dist/src/rag.d.ts",
|
|
50
|
+
"import": "./dist/rag.js",
|
|
51
|
+
"default": "./dist/rag.js"
|
|
52
|
+
}
|
|
48
53
|
},
|
|
49
54
|
"dependencies": {
|
|
50
55
|
"@absolutejs/manifest": "^0.1.0",
|
|
51
56
|
"@sinclair/typebox": "^0.34.0"
|
|
52
57
|
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@absolutejs/rag": ">=0.0.29"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@absolutejs/rag": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
53
66
|
"devDependencies": {
|
|
67
|
+
"@absolutejs/rag": "0.0.29",
|
|
54
68
|
"@types/bun": "latest",
|
|
55
69
|
"prettier": "3.5.3",
|
|
56
70
|
"typescript": "5.8.3"
|