@labelgrid/core 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -8,6 +8,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  `@labelgrid/core` is primarily an internal shared client for the LabelGrid MCP
9
9
  server and CLI — there are no API-stability promises before 1.0.
10
10
 
11
+ ## [0.2.2] - 2026-08-20
12
+
13
+ ### Added
14
+
15
+ - Catalog entities now declare whether their delete endpoint accepts a
16
+ replacement, allowing clients to expose reassignment only for writers and
17
+ publishers.
18
+ - `LabelGridClient.delete()` accepts optional query parameters while preserving
19
+ the existing request URL byte-for-byte when none are supplied.
20
+
21
+ ### Changed
22
+
23
+ - Writer and publisher delete guidance now names every reference that can block
24
+ deletion.
25
+
26
+ ## [0.2.1] - 2026-08-05
27
+
28
+ ### Changed
29
+
30
+ - The catalog entity descriptions carried by this package were tightened. The
31
+ `RELEASE_LOCKED_FIELDS` caveat now lives only on the tool it applies to
32
+ instead of being repeated for every entity. Field requirements, paths and
33
+ exported types are unchanged.
34
+
11
35
  ## [0.2.0] - 2026-07-23
12
36
 
13
37
  ### Added
@@ -72,7 +72,12 @@ export declare class LabelGridClient {
72
72
  idempotency?: boolean;
73
73
  idempotencyKey?: string;
74
74
  }): Promise<ApiResult<T>>;
75
- delete<T>(path: string): Promise<ApiResult<T>>;
75
+ /**
76
+ * A delete carries its parameters in the query string, never in a body: RFC
77
+ * 9110 gives a DELETE body no defined semantics and intermediaries drop it.
78
+ * Omitting `query` sends exactly the URL it always did.
79
+ */
80
+ delete<T>(path: string, query?: Record<string, unknown>): Promise<ApiResult<T>>;
76
81
  /**
77
82
  * Sends a multipart/form-data POST with a single file field plus optional
78
83
  * extra string fields. A missing/unreadable file yields a FILE_NOT_FOUND
package/dist/api/http.js CHANGED
@@ -357,8 +357,13 @@ export class LabelGridClient {
357
357
  idempotencyKey: opts?.idempotencyKey,
358
358
  });
359
359
  }
360
- delete(path) {
361
- return this.send('DELETE', path);
360
+ /**
361
+ * A delete carries its parameters in the query string, never in a body: RFC
362
+ * 9110 gives a DELETE body no defined semantics and intermediaries drop it.
363
+ * Omitting `query` sends exactly the URL it always did.
364
+ */
365
+ delete(path, query) {
366
+ return this.send('DELETE', path, { query });
362
367
  }
363
368
  /**
364
369
  * Sends a multipart/form-data POST with a single file field plus optional
@@ -7,7 +7,9 @@
7
7
  * This is data, not behavior — the catalog tools stay thin wrappers and the
8
8
  * API owns all validation. The wording here carries the caveats from the
9
9
  * per-entity tool descriptions it replaces (recording_country on track create,
10
- * RELEASE_LOCKED_FIELDS on release update, the delete refusals).
10
+ * the delete refusals). Caveats that belong to ONE tool stay in that tool's own
11
+ * description — the RELEASE_LOCKED_FIELDS rule lives on update_catalog_item, so
12
+ * repeating it here would only pad every client's catalog.
11
13
  */
12
14
  export type EntityName = 'label' | 'artist' | 'writer' | 'publisher' | 'release' | 'track';
13
15
  /** The entity names as a tuple, for zod enum inputs. */
@@ -21,5 +23,19 @@ export type EntitySpec = {
21
23
  fieldsDoc: string;
22
24
  /** One-line doc of the server-side delete refusals. */
23
25
  deleteNote: string;
26
+ /**
27
+ * Whether this entity's DELETE endpoint accepts `replace_with` — the id of the
28
+ * entity that takes over every credit held by the one being deleted, rewritten
29
+ * before the delete. Only the writer and publisher endpoints have it. Stated
30
+ * for every entity rather than defaulted, so a new entity cannot inherit an
31
+ * answer nobody gave.
32
+ */
33
+ acceptsDeleteReplacement: boolean;
24
34
  };
25
35
  export declare const ENTITIES: Record<EntityName, EntitySpec>;
36
+ /**
37
+ * The entities whose DELETE accepts `replace_with`, derived from the registry so
38
+ * the tool schema, its description and its refusal message cannot disagree with
39
+ * the table or with each other.
40
+ */
41
+ export declare const REPLACEMENT_ENTITIES: readonly EntityName[];
package/dist/entities.js CHANGED
@@ -7,45 +7,59 @@
7
7
  * This is data, not behavior — the catalog tools stay thin wrappers and the
8
8
  * API owns all validation. The wording here carries the caveats from the
9
9
  * per-entity tool descriptions it replaces (recording_country on track create,
10
- * RELEASE_LOCKED_FIELDS on release update, the delete refusals).
10
+ * the delete refusals). Caveats that belong to ONE tool stay in that tool's own
11
+ * description — the RELEASE_LOCKED_FIELDS rule lives on update_catalog_item, so
12
+ * repeating it here would only pad every client's catalog.
11
13
  */
12
14
  /** The entity names as a tuple, for zod enum inputs. */
13
15
  export const ENTITY_NAMES = ['label', 'artist', 'writer', 'publisher', 'release', 'track'];
14
16
  export const ENTITIES = {
15
17
  label: {
16
18
  path: '/labels',
17
- filtersDoc: 'label: no documented filters — paginate with page/per_page.',
19
+ filtersDoc: 'label: no documented filters.',
18
20
  fieldsDoc: 'label — required: name, default_email; optional: support email, website/platform URLs, default copyright lines, isrc_base.',
19
21
  deleteNote: 'label: refused while the label still has releases — remove or reassign its releases first.',
22
+ acceptsDeleteReplacement: false,
20
23
  },
21
24
  artist: {
22
25
  path: '/artists',
23
- filtersDoc: 'artist: artist_name (filter by artist name).',
26
+ filtersDoc: 'artist: artist_name.',
24
27
  fieldsDoc: 'artist — required: artist_name; optional: full_name, email, location, bios, isni, default_language, platform profile URLs.',
25
28
  deleteNote: 'artist: refused while still referenced by releases or tracks.',
29
+ acceptsDeleteReplacement: false,
26
30
  },
27
31
  writer: {
28
32
  path: '/writers',
29
- filtersDoc: 'writer: name (writer name), ipi (IPI number).',
33
+ filtersDoc: 'writer: name, ipi.',
30
34
  fieldsDoc: 'writer — required: first_name, last_name; optional: middle_name, display_credits, email, country, pro, ipi, isni, publisher_id (or publisher_name/publisher_pro/publisher_ipi).',
31
- deleteNote: 'writer: refused while still referenced by tracks.',
35
+ deleteNote: 'writer: refused while still referenced by tracks or artists, unless replace_with reassigns those credits.',
36
+ acceptsDeleteReplacement: true,
32
37
  },
33
38
  publisher: {
34
39
  path: '/publishers',
35
- filtersDoc: 'publisher: name (publisher name), ipi (IPI number).',
40
+ filtersDoc: 'publisher: name, ipi.',
36
41
  fieldsDoc: 'publisher — required: name; optional: ipi, pro, isni, controlled_publisher.',
37
- deleteNote: 'publisher: refused while still referenced by writers.',
42
+ deleteNote: 'publisher: refused while still referenced by tracks or label default publishers, unless replace_with reassigns those credits.',
43
+ acceptsDeleteReplacement: true,
38
44
  },
39
45
  release: {
40
46
  path: '/releases',
41
- filtersDoc: 'release: label_id (owning label id), is_live (1 = live/distributed only), barcode_number (UPC/EAN), cat (catalog number).',
42
- fieldsDoc: 'release — required on create: content_type, label_id, artists, titles, cat (catalog number), artwork_ai_usage, primary_genre_id; many optional fields (dates, copyright lines, genres, per-outlet URLs). Once submitted or distributed some fields are locked — changing one returns a 403 with code RELEASE_LOCKED_FIELDS naming exactly which fields cannot change.',
47
+ filtersDoc: 'release: label_id, is_live (1 = live only), barcode_number (UPC/EAN), cat.',
48
+ fieldsDoc: 'release — required on create: content_type, label_id, artists, titles, cat (catalog number), artwork_ai_usage, primary_genre_id; many optional fields (dates, copyright lines, genres, per-outlet URLs).',
43
49
  deleteNote: 'release: only a never-submitted draft can be deleted.',
50
+ acceptsDeleteReplacement: false,
44
51
  },
45
52
  track: {
46
53
  path: '/tracks',
47
- filtersDoc: 'track: release_id (one release’s tracks), isrc (filter by ISRC).',
54
+ filtersDoc: 'track: release_id, isrc.',
48
55
  fieldsDoc: 'track — required on create: release_id, disc, track_num, composition_type, artists, audio_ai_usage, composition_ai_usage, commercial_samples, audio_language, contributors, and recording_country (ISO 3166-1 alpha-2, e.g. "US"); optional: titles, isrc, iswc, writers, publishers, splits, and more.',
49
- deleteNote: 'track: allowed while the parent release is an editable draft; refused once submitted or distributed.',
56
+ deleteNote: 'track: refused once the release is no longer an editable draft.',
57
+ acceptsDeleteReplacement: false,
50
58
  },
51
59
  };
60
+ /**
61
+ * The entities whose DELETE accepts `replace_with`, derived from the registry so
62
+ * the tool schema, its description and its refusal message cannot disagree with
63
+ * the table or with each other.
64
+ */
65
+ export const REPLACEMENT_ENTITIES = ENTITY_NAMES.filter((name) => ENTITIES[name].acceptsDeleteReplacement);
package/package.json CHANGED
@@ -1,13 +1,9 @@
1
1
  {
2
2
  "name": "@labelgrid/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Shared LabelGrid public-API client: HTTP transport, uploads, content types, the catalog-entity registry, and redacting logging",
5
5
  "type": "module",
6
- "keywords": [
7
- "labelgrid",
8
- "music-distribution",
9
- "api-client"
10
- ],
6
+ "keywords": ["labelgrid", "music-distribution", "api-client"],
11
7
  "main": "dist/index.js",
12
8
  "types": "dist/index.d.ts",
13
9
  "exports": {
@@ -16,12 +12,7 @@
16
12
  "default": "./dist/index.js"
17
13
  }
18
14
  },
19
- "files": [
20
- "dist",
21
- "README.md",
22
- "CHANGELOG.md",
23
- "LICENSE"
24
- ],
15
+ "files": ["dist", "README.md", "CHANGELOG.md", "LICENSE"],
25
16
  "scripts": {
26
17
  "build": "tsc",
27
18
  "dev": "tsc --watch",