@yargote/artifacts-contracts 0.1.0-beta.7 → 0.1.0-beta.9

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 CHANGED
@@ -3,11 +3,15 @@
3
3
  `@yargote/artifacts-contracts` contains strict domain schemas, types, `ArtifactError`, snapshot serialization, and pure retained-edit helpers. It has no database, ZIP, server, or CLI dependency. Node 24 or later is required.
4
4
 
5
5
  ```sh
6
- npm install @yargote/artifacts-contracts@0.1.0-beta.7
6
+ npm install @yargote/artifacts-contracts@0.1.0-beta.9
7
7
  ```
8
8
 
9
9
  The root export contains domain schemas and types. `/workflow` contains `prepareArtifactEdit`, revision comparison, and compact projections. `/serialization` contains canonical JSON and SHA-256. `/http` contains HTTP response schemas, identity, canonical resource validation, and route URL helpers.
10
10
 
11
+ `protectedResourceMetadataSchema` accepts standard protected-resource metadata and an optional `artifacts_rest_resource`. The relation names the exact absolute canonical REST OAuth resource URL. It may name the resource itself or one distinct REST resource. Metadata for the distinct resource may omit the relation or name itself, but cannot name another resource.
12
+
13
+ `slugSchema` validates page slugs such as `concepts/rag` without normalizing them. `readSchema` takes exactly one of `artifactId` or a `{ project, slug }` locator, and `readByIdSchema` takes only an ID. `moveSchema` takes `project`, `slug`, or both, each optional and nullable. `moveResultSchema` is unchanged. `ArtifactRead` and the HTTP summary schema include `slug`, and `ArtifactErrorCode` includes `slug_conflict`.
14
+
11
15
  The existing `@artifacts/artifacts` root, `/contracts`, and `/workflow` exports re-export these implementations. Store and REST packages share one `ArtifactError` runtime.
12
16
 
13
17
  This package is MIT licensed. The license applies to this package and does not license the private Artifacts application repository.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yargote/artifacts-contracts",
3
- "version": "0.1.0-beta.7",
3
+ "version": "0.1.0-beta.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Shared schemas and contracts for Artifacts clients.",
package/src/http.d.ts CHANGED
@@ -52,6 +52,7 @@ export declare const compactArtifactReadSchema: z.ZodObject<{
52
52
  ownerId: z.ZodString;
53
53
  headRevisionId: z.ZodString;
54
54
  project: z.ZodNullable<z.ZodString>;
55
+ slug: z.ZodNullable<z.ZodString>;
55
56
  artifactId: z.ZodString;
56
57
  publication: z.ZodNullable<z.ZodObject<{
57
58
  revisionId: z.ZodString;
@@ -97,6 +98,7 @@ export declare const artifactSummarySchema: z.ZodObject<{
97
98
  headRevisionId: z.ZodString;
98
99
  createdAt: z.ZodISODateTime;
99
100
  project: z.ZodNullable<z.ZodString>;
101
+ slug: z.ZodNullable<z.ZodString>;
100
102
  publication: z.ZodNullable<z.ZodObject<{
101
103
  revisionId: z.ZodString;
102
104
  version: z.ZodNumber;
@@ -110,6 +112,7 @@ export declare const listResultSchema: z.ZodObject<{
110
112
  headRevisionId: z.ZodString;
111
113
  createdAt: z.ZodISODateTime;
112
114
  project: z.ZodNullable<z.ZodString>;
115
+ slug: z.ZodNullable<z.ZodString>;
113
116
  publication: z.ZodNullable<z.ZodObject<{
114
117
  revisionId: z.ZodString;
115
118
  version: z.ZodNumber;
@@ -208,5 +211,10 @@ export declare const httpErrorSchema: z.ZodObject<{
208
211
  }, z.core.$strict>;
209
212
  }, z.core.$strict>;
210
213
  export declare function canonicalResource(value: string): string;
214
+ export declare const protectedResourceMetadataSchema: z.ZodObject<{
215
+ resource: z.ZodString;
216
+ authorization_servers: z.ZodArray<z.ZodString>;
217
+ artifacts_rest_resource: z.ZodOptional<z.ZodString>;
218
+ }, z.core.$strip>;
211
219
  export declare function protectedResourceMetadataUrl(resource: string): URL;
212
220
  export declare function restBase(resource: string): string;
package/src/http.js CHANGED
@@ -10,7 +10,7 @@ export const revisionSummarySchema = revisionSchema.omit({ snapshot: true }).ext
10
10
  files: z.array(attachmentSummarySchema).max(32),
11
11
  });
12
12
  export const compactArtifactReadSchema = artifactReadSchema.omit({ revision: true }).extend({ revision: revisionSummarySchema });
13
- export const artifactSummarySchema = z.strictObject({ artifactId: identifier, title: z.string(), headRevisionId: identifier, createdAt: z.iso.datetime(), project: z.string().nullable(), publication: publicationSchema.nullable() });
13
+ export const artifactSummarySchema = z.strictObject({ artifactId: identifier, title: z.string(), headRevisionId: identifier, createdAt: z.iso.datetime(), project: z.string().nullable(), slug: z.string().nullable(), publication: publicationSchema.nullable() });
14
14
  export const listResultSchema = z.strictObject({ items: z.array(artifactSummarySchema).max(100), nextCursor: identifier.nullable() });
15
15
  export const projectSummarySchema = z.strictObject({ name: z.string(), artifactCount: z.number().int().positive() });
16
16
  export const projectsResultSchema = z.strictObject({ items: z.array(projectSummarySchema) });
@@ -26,6 +26,19 @@ export function canonicalResource(value) {
26
26
  throw new TypeError('Use the exact canonical resource URL without a trailing slash');
27
27
  return value;
28
28
  }
29
+ export const protectedResourceMetadataSchema = z.object({
30
+ resource: z.string(),
31
+ authorization_servers: z.array(z.string()).min(1),
32
+ artifacts_rest_resource: z.string().refine((value) => {
33
+ try {
34
+ canonicalResource(value);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }).optional(),
41
+ });
29
42
  export function protectedResourceMetadataUrl(resource) {
30
43
  const url = new URL(canonicalResource(resource));
31
44
  return new URL(`/.well-known/oauth-protected-resource${url.pathname === '/' ? '' : url.pathname}`, url.origin);
package/src/index.d.ts CHANGED
@@ -59,6 +59,7 @@ export type Canvas = {
59
59
  };
60
60
  export declare function parseCanvas(snapshot: Pick<Snapshot, 'content' | 'files'>): Canvas;
61
61
  export declare const projectNameSchema: z.ZodString;
62
+ export declare const slugSchema: z.ZodString;
62
63
  export declare const createSchema: z.ZodObject<{
63
64
  requestId: z.ZodString;
64
65
  snapshot: z.ZodObject<{
@@ -87,12 +88,13 @@ export declare const createSchema: z.ZodObject<{
87
88
  }, z.core.$strict>>;
88
89
  }, z.core.$strict>;
89
90
  project: z.ZodOptional<z.ZodString>;
91
+ slug: z.ZodOptional<z.ZodString>;
90
92
  response: z.ZodOptional<z.ZodEnum<{
91
93
  full: "full";
92
94
  compact: "compact";
93
95
  }>>;
94
96
  }, z.core.$strict>;
95
- export declare const readSchema: z.ZodObject<{
97
+ export declare const readByIdSchema: z.ZodObject<{
96
98
  artifactId: z.ZodString;
97
99
  revisionId: z.ZodOptional<z.ZodString>;
98
100
  response: z.ZodOptional<z.ZodEnum<{
@@ -100,6 +102,16 @@ export declare const readSchema: z.ZodObject<{
100
102
  compact: "compact";
101
103
  }>>;
102
104
  }, z.core.$strict>;
105
+ export declare const readSchema: z.ZodObject<{
106
+ artifactId: z.ZodOptional<z.ZodString>;
107
+ project: z.ZodOptional<z.ZodString>;
108
+ slug: z.ZodOptional<z.ZodString>;
109
+ revisionId: z.ZodOptional<z.ZodString>;
110
+ response: z.ZodOptional<z.ZodEnum<{
111
+ full: "full";
112
+ compact: "compact";
113
+ }>>;
114
+ }, z.core.$strict>;
103
115
  export declare const updateSchema: z.ZodObject<{
104
116
  requestId: z.ZodString;
105
117
  artifactId: z.ZodString;
@@ -154,7 +166,8 @@ export type DeleteResult = z.infer<typeof deleteResultSchema>;
154
166
  export declare const moveSchema: z.ZodObject<{
155
167
  requestId: z.ZodString;
156
168
  artifactId: z.ZodString;
157
- project: z.ZodNullable<z.ZodString>;
169
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
170
+ slug: z.ZodOptional<z.ZodNullable<z.ZodString>>;
158
171
  }, z.core.$strict>;
159
172
  export declare const moveResultSchema: z.ZodObject<{
160
173
  artifactId: z.ZodString;
@@ -304,6 +317,7 @@ export type ArtifactSummary = {
304
317
  headRevisionId: string;
305
318
  createdAt: string;
306
319
  project: string | null;
320
+ slug: string | null;
307
321
  publication: Publication | null;
308
322
  };
309
323
  export type ProjectSummary = {
@@ -327,7 +341,7 @@ export type Archive = {
327
341
  base64: string;
328
342
  sha256: string;
329
343
  };
330
- export type ArtifactErrorCode = 'forbidden' | 'not_found' | 'conflict' | 'request_conflict' | 'invalid_grant';
344
+ export type ArtifactErrorCode = 'forbidden' | 'not_found' | 'conflict' | 'request_conflict' | 'slug_conflict' | 'invalid_grant';
331
345
  export declare class ArtifactError extends Error {
332
346
  readonly code: ArtifactErrorCode;
333
347
  readonly details: Record<string, unknown>;
@@ -381,6 +395,7 @@ export declare const artifactReadSchema: z.ZodObject<{
381
395
  ownerId: z.ZodString;
382
396
  headRevisionId: z.ZodString;
383
397
  project: z.ZodNullable<z.ZodString>;
398
+ slug: z.ZodNullable<z.ZodString>;
384
399
  publication: z.ZodNullable<z.ZodObject<{
385
400
  revisionId: z.ZodString;
386
401
  version: z.ZodNumber;
package/src/index.js CHANGED
@@ -122,13 +122,20 @@ export function parseCanvas(snapshot) {
122
122
  }
123
123
  export const projectNameSchema = z.string().normalize('NFC').trim().min(1).max(80).refine((name) => name.isWellFormed() && !/\p{Cc}/u.test(name), 'Use a project name without control characters');
124
124
  const response = z.enum(['full', 'compact']).optional();
125
- export const createSchema = z.strictObject({ requestId, snapshot: snapshotSchema, project: projectNameSchema.optional(), response });
126
- export const readSchema = z.strictObject({ artifactId: identifier, revisionId: identifier.optional(), response });
125
+ // The .md check aborts so a file-style name gets the suffix hint instead of the generic pattern message.
126
+ export const slugSchema = z.string().min(1).max(160)
127
+ .refine((slug) => !slug.endsWith('.md'), { message: 'Remove the .md suffix: a slug names a page, not a file', abort: true })
128
+ .regex(/^[a-z0-9]+(-[a-z0-9]+)*(\/[a-z0-9]+(-[a-z0-9]+)*)*$/, 'Use lowercase kebab-case segments separated by /, such as concepts/rag');
129
+ export const createSchema = z.strictObject({ requestId, snapshot: snapshotSchema, project: projectNameSchema.optional(), slug: slugSchema.optional(), response });
130
+ export const readByIdSchema = z.strictObject({ artifactId: identifier, revisionId: identifier.optional(), response });
131
+ export const readSchema = z.strictObject({ artifactId: identifier.optional(), project: projectNameSchema.optional(), slug: slugSchema.optional(), revisionId: identifier.optional(), response })
132
+ .refine((input) => (input.artifactId === undefined) !== (input.slug === undefined) && (input.project === undefined || input.slug !== undefined), 'Specify exactly one of artifactId or slug; project applies only with slug');
127
133
  export const updateSchema = z.strictObject({ requestId, artifactId: identifier, expectedRevisionId: identifier, snapshot: snapshotSchema, response });
128
134
  export const publishSchema = z.strictObject({ requestId, artifactId: identifier, revisionId: identifier, expectedPublicationVersion: z.number().int().nonnegative() });
129
135
  export const deleteSchema = z.strictObject({ requestId, artifactId: identifier, expectedRevisionId: identifier, expectedPublicationVersion: z.number().int().nonnegative() });
130
136
  export const deleteResultSchema = z.strictObject({ artifactId: identifier });
131
- export const moveSchema = z.strictObject({ requestId, artifactId: identifier, project: projectNameSchema.nullable() });
137
+ export const moveSchema = z.strictObject({ requestId, artifactId: identifier, project: projectNameSchema.nullable().optional(), slug: slugSchema.nullable().optional() })
138
+ .refine((input) => input.project !== undefined || input.slug !== undefined, 'Specify project, slug, or both; null clears either');
132
139
  export const moveResultSchema = z.strictObject({ artifactId: identifier, project: z.string().nullable() });
133
140
  export const shareSchema = z.strictObject({ requestId, artifactId: identifier, email: z.string().trim().toLowerCase().pipe(z.email().max(254)).meta({ format: 'email', description: 'Email address, trimmed and lowercased; at most 254 characters after trimming' }), notify: z.boolean().optional(), requireNew: z.literal(true).optional() });
134
141
  export const unshareSchema = z.strictObject({ requestId, artifactId: identifier, shareId: identifier });
@@ -167,6 +174,6 @@ export function decodeFile(file) {
167
174
  }
168
175
  export const publicationSchema = z.strictObject({ revisionId: identifier, version: z.number().int().positive(), url: z.url() });
169
176
  export const revisionSchema = z.strictObject({ id: identifier, parentRevisionId: identifier.nullable(), sequence: z.number().int().positive(), authorUserId: identifier, authorClientId: identifier, createdAt: z.iso.datetime(), sha256: z.string().regex(/^[a-f0-9]{64}$/), snapshot: snapshotSchema });
170
- export const artifactReadSchema = z.strictObject({ artifactId: identifier, ownerId: identifier, headRevisionId: identifier, project: z.string().nullable(), publication: publicationSchema.nullable(), revision: revisionSchema });
177
+ export const artifactReadSchema = z.strictObject({ artifactId: identifier, ownerId: identifier, headRevisionId: identifier, project: z.string().nullable(), slug: z.string().nullable(), publication: publicationSchema.nullable(), revision: revisionSchema });
171
178
  export const sharedReadSchema = z.strictObject({ artifactId: identifier, ownerName: z.string().default('Usuario de Artifacts'), publication: publicationSchema, revision: revisionSchema.pick({ id: true, createdAt: true, sha256: true, snapshot: true }) });
172
179
  export const publishResultSchema = publicationSchema.extend({ artifactId: identifier });
package/src/workflow.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { ArtifactError, artifactReadSchema, createSchema, decodeFile, readSchema, snapshotSchema, updateSchema } from "./index.js";
2
+ import { ArtifactError, artifactReadSchema, createSchema, decodeFile, readByIdSchema, snapshotSchema, updateSchema } from "./index.js";
3
3
  import { canonicalJson, sha256 } from "./serialization.js";
4
4
  async function summarizeFiles(files) {
5
5
  return Promise.all([...files].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0).map(async ({ path, mediaType, encoding, data }) => {
@@ -57,7 +57,7 @@ export async function compareRevisions(before, after) {
57
57
  };
58
58
  }
59
59
  const prepareEditSchema = z.strictObject({
60
- artifactId: readSchema.shape.artifactId,
60
+ artifactId: readByIdSchema.shape.artifactId,
61
61
  requestId: createSchema.shape.requestId,
62
62
  changes: z.strictObject({
63
63
  metadata: snapshotSchema.shape.metadata.partial().optional(),