@zivue/zuuid 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.
Files changed (49) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +368 -0
  4. package/dist/client.d.ts +29 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +39 -0
  7. package/dist/entity.d.ts +106 -0
  8. package/dist/entity.d.ts.map +1 -0
  9. package/dist/entity.js +101 -0
  10. package/dist/hash.d.ts +3 -0
  11. package/dist/hash.d.ts.map +1 -0
  12. package/dist/hash.js +9 -0
  13. package/dist/identity.d.ts +14 -0
  14. package/dist/identity.d.ts.map +1 -0
  15. package/dist/identity.js +39 -0
  16. package/dist/index.d.ts +9 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +8 -0
  19. package/dist/providers/tmdb/client.d.ts +30 -0
  20. package/dist/providers/tmdb/client.d.ts.map +1 -0
  21. package/dist/providers/tmdb/client.js +92 -0
  22. package/dist/providers/tmdb/constants.d.ts +5 -0
  23. package/dist/providers/tmdb/constants.d.ts.map +1 -0
  24. package/dist/providers/tmdb/constants.js +4 -0
  25. package/dist/providers/tmdb/index.d.ts +7 -0
  26. package/dist/providers/tmdb/index.d.ts.map +1 -0
  27. package/dist/providers/tmdb/index.js +6 -0
  28. package/dist/providers/tmdb/movie.d.ts +164 -0
  29. package/dist/providers/tmdb/movie.d.ts.map +1 -0
  30. package/dist/providers/tmdb/movie.js +537 -0
  31. package/dist/providers/tmdb/person.d.ts +84 -0
  32. package/dist/providers/tmdb/person.d.ts.map +1 -0
  33. package/dist/providers/tmdb/person.js +345 -0
  34. package/dist/providers/tmdb/tv.d.ts +181 -0
  35. package/dist/providers/tmdb/tv.d.ts.map +1 -0
  36. package/dist/providers/tmdb/tv.js +522 -0
  37. package/dist/providers/tmdb/types.d.ts +36 -0
  38. package/dist/providers/tmdb/types.d.ts.map +1 -0
  39. package/dist/providers/tmdb/types.js +1 -0
  40. package/dist/source.d.ts +35 -0
  41. package/dist/source.d.ts.map +1 -0
  42. package/dist/source.js +53 -0
  43. package/dist/types.d.ts +5 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +1 -0
  46. package/dist/uuid.d.ts +3 -0
  47. package/dist/uuid.d.ts.map +1 -0
  48. package/dist/uuid.js +32 -0
  49. package/package.json +72 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Added provider-stable ZUUID generation using UUID v5.
6
+ - Added flat `ZuuidData` model with source metadata and provenance.
7
+ - Added shared lightweight list item shape for search results, relations, and recommendations.
8
+ - Added TMDB provider support for movies, TV shows, and people.
9
+ - Added TMDB fetch, raw source-record fetch, transform, unified search, and raw search APIs.
10
+ - Added TMDB fetch and search CLI examples for local testing.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zivue
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # @zivue/zuuid
2
+
3
+ TypeScript helpers for fetching, searching, and transforming Zuuid datasets.
4
+
5
+ Zuuid is the metadata, search, and indexing substrate for Zivue/Stareto. This package focuses on the client-side model: provider-stable UUIDs, source records, and normalized Zuuid datasets.
6
+
7
+ Storage, caching, object keys, and persistence belong in a layer outside this package.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install @zivue/zuuid
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```ts
18
+ import { createZuuidClient } from "@zivue/zuuid";
19
+
20
+ const zuuid = createZuuidClient({
21
+ providers: {
22
+ tmdb: {
23
+ bearerToken: process.env.TMDB_BEARER_TOKEN!
24
+ }
25
+ }
26
+ });
27
+
28
+ const search = await zuuid.movie.tmdb?.search({ query: "Fight Club" });
29
+ const selected = search?.results[0];
30
+ const movie = selected ? await zuuid.movie.tmdb?.fetch({ id: selected.source.value }) : undefined;
31
+
32
+ console.log(movie?.zuuid);
33
+ ```
34
+
35
+ ## Generate A ZUUID
36
+
37
+ Zuuid generation is UUID v5:
38
+
39
+ ```txt
40
+ uuid_v5(provider_namespace, "<normalized-category>:<external_id>")
41
+ ```
42
+
43
+ ```ts
44
+ import { providerZuuid } from "@zivue/zuuid";
45
+
46
+ const zuuid = await providerZuuid({
47
+ provider: "tmdb",
48
+ category: "movie",
49
+ externalId: "550"
50
+ });
51
+
52
+ console.log(zuuid);
53
+ // 1706d641-d381-5618-9425-d8cd8b35f898
54
+ ```
55
+
56
+ `category` is trimmed and lowercased. `externalId` is trimmed but otherwise preserved.
57
+
58
+ ## Zuuid Dataset
59
+
60
+ ```ts
61
+ import { createZuuidData } from "@zivue/zuuid";
62
+
63
+ const dataset = createZuuidData({
64
+ zuuid: "1706d641-d381-5618-9425-d8cd8b35f898",
65
+ category: "movie",
66
+ primaryTitle: "Fight Club"
67
+ });
68
+ ```
69
+
70
+ The dataset shape is intentionally flat:
71
+
72
+ ```ts
73
+ type ZuuidData = {
74
+ zuuid: string;
75
+ kind: EntityKind;
76
+ category: string;
77
+ primaryTitle: string;
78
+ primaryDate?: string;
79
+ rating?: number;
80
+ cover?: string;
81
+ aliases: Alias[];
82
+ descriptions: Description[];
83
+ details: Detail[];
84
+ media: MediaAsset[];
85
+ relations: EntityRelation[];
86
+ recommendations: RecommendationEdge[];
87
+ tags: string[];
88
+ externalIds: ExternalId[];
89
+ provenance: Provenance[];
90
+ };
91
+ ```
92
+
93
+ Search results, relations, and recommendations use the same lightweight list item fields: `id`, `zuuid`, `category`, `title`, `date`, `cover`, `rating`, `weight`, `relationType`, `attribute`, and `order`. That lets transformed datasets and search results carry useful related-entity context before those related entities are fetched as full Zuuid datasets.
94
+
95
+ Backend-only concerns such as record versions, flags, review state, index state, and object-store metadata are intentionally not part of this package's dataset shape.
96
+
97
+ ## Source Metadata
98
+
99
+ Source records are provider records before they are transformed into canonical entities. They carry the external provider key, raw JSON payload, content hash, and observation timestamps.
100
+
101
+ ```ts
102
+ import { attachSourceMetadata, createSourceRecord } from "@zivue/zuuid";
103
+
104
+ const source = await createSourceRecord({
105
+ source: { provider: "tmdb", category: "movie", externalId: "550" },
106
+ payload: { title: "Fight Club" },
107
+ observedAt: "2026-05-21T00:00:00.000Z"
108
+ });
109
+
110
+ const withSource = attachSourceMetadata(dataset, source);
111
+ ```
112
+
113
+ `attachSourceMetadata` returns a new dataset with `externalIds` and `provenance` updated.
114
+
115
+ ## TMDB Movies, TV, And People
116
+
117
+ The first provider module supports fetching and transforming TMDB movies, TV shows, and people.
118
+
119
+ | Provider | Category | Search | Fetch | Transform |
120
+ | --- | --- | --- | --- | --- |
121
+ | TMDB | movie | yes | yes | yes |
122
+ | TMDB | tv | yes | yes | yes |
123
+ | TMDB | person | yes | yes | yes |
124
+
125
+ ```ts
126
+ import { TmdbProvider } from "@zivue/zuuid/providers/tmdb";
127
+
128
+ const tmdb = new TmdbProvider({
129
+ bearerToken: process.env.TMDB_BEARER_TOKEN!
130
+ });
131
+
132
+ const movie = await tmdb.fetchMovie({ id: 550 });
133
+ const tv = await tmdb.fetchTv({ id: 1399 });
134
+ const person = await tmdb.fetchPerson({ id: 287 });
135
+
136
+ console.log(movie?.zuuid);
137
+ // 1706d641-d381-5618-9425-d8cd8b35f898
138
+ ```
139
+
140
+ You can also split fetching from transformation:
141
+
142
+ ```ts
143
+ import { TmdbProvider, transformTmdbMovie, transformTmdbPerson, transformTmdbTv } from "@zivue/zuuid/providers/tmdb";
144
+
145
+ const source = await tmdb.fetchMovieSourceRecord({ id: 550 });
146
+ const movie = source ? await transformTmdbMovie(source) : undefined;
147
+
148
+ const tvSource = await tmdb.fetchTvSourceRecord({ id: 1399 });
149
+ const tv = tvSource ? await transformTmdbTv(tvSource) : undefined;
150
+
151
+ const personSource = await tmdb.fetchPersonSourceRecord({ id: 287 });
152
+ const person = personSource ? await transformTmdbPerson(personSource) : undefined;
153
+ ```
154
+
155
+ Search returns unified lightweight candidates. Use search to find candidate IDs, then fetch the selected item for the full transformed dataset:
156
+
157
+ ```ts
158
+ const movies = await tmdb.searchMovies({ query: "Fight Club" });
159
+ const tvShows = await tmdb.searchTv({ query: "Game of Thrones" });
160
+ const people = await tmdb.searchPeople({ query: "Brad Pitt" });
161
+
162
+ console.log(movies.pagination);
163
+ // { page: 1, totalPages: 10, totalResults: 190 }
164
+
165
+ const selectedMovie = movies.results[0];
166
+ console.log(selectedMovie);
167
+ // {
168
+ // id: "1706d641-d381-5618-9425-d8cd8b35f898",
169
+ // zuuid: "1706d641-d381-5618-9425-d8cd8b35f898",
170
+ // category: "movie",
171
+ // title: "Fight Club",
172
+ // date: "1999-10-15",
173
+ // cover: "https://image.tmdb.org/t/p/w500/...",
174
+ // rating: 8.4,
175
+ // weight: 20.0,
176
+ // relationType: null,
177
+ // attribute: null,
178
+ // order: null,
179
+ // source: { source: "tmdb", category: "movie", value: "550" }
180
+ // }
181
+
182
+ const fullMovie = selectedMovie ? await tmdb.fetchMovie({ id: selectedMovie.source.value }) : undefined;
183
+ ```
184
+
185
+ Raw search source records are also available:
186
+
187
+ ```ts
188
+ const rawMovies = await tmdb.searchMovieSourceRecords({ query: "Fight Club" });
189
+ ```
190
+
191
+ Category-specific imports are also available:
192
+
193
+ ```ts
194
+ import { transformTmdbMovie } from "@zivue/zuuid/providers/tmdb/movie";
195
+ import { transformTmdbTv } from "@zivue/zuuid/providers/tmdb/tv";
196
+ import { transformTmdbPerson } from "@zivue/zuuid/providers/tmdb/person";
197
+ ```
198
+
199
+ `TmdbProvider` accepts either `{ bearerToken }` or `{ apiKey }`. TMDB bearer tokens are API Read Access Tokens and usually start with `eyJ...`; v3 API keys are shorter hex-like strings.
200
+
201
+ ## Client Instantiation
202
+
203
+ For applications with multiple providers, use `createZuuidClient` to wire provider config once:
204
+
205
+ ```ts
206
+ import { createZuuidClient } from "@zivue/zuuid";
207
+
208
+ const zuuid = createZuuidClient({
209
+ providers: {
210
+ tmdb: {
211
+ bearerToken: process.env.TMDB_BEARER_TOKEN!
212
+ }
213
+ // Future movie providers can sit beside tmdb, e.g. omdb.
214
+ }
215
+ });
216
+
217
+ const movie = await zuuid.movie.tmdb?.fetch({ id: 550 });
218
+ const tv = await zuuid.tv.tmdb?.fetch({ id: 1399 });
219
+ const person = await zuuid.people.tmdb?.fetch({ id: 287 });
220
+ ```
221
+
222
+ The config is provider-keyed because apps usually manage credentials per provider. The client facade is category-first, so multiple movie providers can live under `zuuid.movie`:
223
+
224
+ ```ts
225
+ zuuid.movie.tmdb?.fetch({ id: 550 });
226
+ zuuid.tv.tmdb?.fetch({ id: 1399 });
227
+ zuuid.people.tmdb?.fetch({ id: 287 });
228
+
229
+ zuuid.movie.tmdb?.search({ query: "Fight Club" });
230
+ zuuid.tv.tmdb?.search({ query: "Game of Thrones" });
231
+ zuuid.people.tmdb?.search({ query: "Brad Pitt" });
232
+ // later: zuuid.movie.omdb?.fetch(...)
233
+ ```
234
+
235
+ The client is stateless: it does not cache, persist, schedule, or read environment variables. It only closes over provider configuration and exposes category/provider methods.
236
+
237
+ ## API
238
+
239
+ ## Package Structure
240
+
241
+ The source is split by Zuuid responsibility:
242
+
243
+ - `identity.ts`: provider namespaces and UUID v5 ZUUID generation
244
+ - `entity.ts`: flat Zuuid data types and category helpers
245
+ - `client.ts`: stateless package instantiator for configured providers
246
+ - `providers/<provider>/index.ts`: provider module barrel
247
+ - `providers/<provider>/client.ts`: shared provider client/config
248
+ - `providers/<provider>/<category>.ts`: category-specific fetch and transform helpers
249
+ - `providers/tmdb/movie.ts`: TMDB movie fetch and transform helpers
250
+ - `providers/tmdb/tv.ts`: TMDB TV fetch and transform helpers
251
+ - `providers/tmdb/person.ts`: TMDB person fetch and transform helpers
252
+ - `source.ts`: source records, external IDs, and provenance
253
+ - `hash.ts`: stable payload hashing
254
+ - `uuid.ts`: UUID parsing/normalization and UUID v5 internals
255
+ - `types.ts`: shared JSON value types
256
+ - `index.ts`: public barrel exports
257
+
258
+ ### `providerZuuid(input)`
259
+
260
+ Generates a deterministic ZUUID for a provider/category/external ID.
261
+
262
+ ### `providerNamespace(provider)`
263
+
264
+ Returns the UUID namespace configured for a known provider.
265
+
266
+ ### `createZuuidData(input)`
267
+
268
+ Creates a normalized Zuuid dataset.
269
+
270
+ ### `createSourceRecord(input)`
271
+
272
+ Creates a source record and computes the SHA-256 payload hash used for provenance.
273
+
274
+ ### `attachSourceMetadata(dataset, sourceRecord, confidence?)`
275
+
276
+ Returns a new dataset with external ID and provenance attached.
277
+
278
+ ### `TmdbProvider`
279
+
280
+ Fetches TMDB source records and transforms them into `ZuuidData`.
281
+
282
+ ### `transformTmdbMovie(sourceRecord, options?)`
283
+
284
+ Transforms an already-fetched TMDB movie source record into `ZuuidData`.
285
+
286
+ ### `transformTmdbTv(sourceRecord, options?)`
287
+
288
+ Transforms an already-fetched TMDB TV source record into `ZuuidData`.
289
+
290
+ ### `transformTmdbPerson(sourceRecord, options?)`
291
+
292
+ Transforms an already-fetched TMDB person source record into `ZuuidData`.
293
+
294
+ ### `categoryFor(value)`
295
+
296
+ Normalizes a category and derives its entity kind.
297
+
298
+ ### `kindForCategory(category)`
299
+
300
+ Maps categories such as `movie`, `book`, `game`, `restaurant`, and `person` to broad kinds such as `watch`, `read`, `play`, `visit`, and `people`.
301
+
302
+ ## Development
303
+
304
+ ```sh
305
+ npm install
306
+ npm test
307
+ ```
308
+
309
+ ## Try It
310
+
311
+ Fetch and transform TMDB movie `550`:
312
+
313
+ ```sh
314
+ TMDB_BEARER_TOKEN=... npm run example:tmdb-fetch -- movie 550
315
+ ```
316
+
317
+ or:
318
+
319
+ ```sh
320
+ TMDB_API_KEY=... npm run example:tmdb-fetch -- movie 550
321
+ ```
322
+
323
+ Fetch and transform TMDB TV show `1399`:
324
+
325
+ ```sh
326
+ TMDB_BEARER_TOKEN=... npm run example:tmdb-fetch -- tv 1399
327
+ ```
328
+
329
+ Fetch and transform TMDB person `287`:
330
+
331
+ ```sh
332
+ TMDB_BEARER_TOKEN=... npm run example:tmdb-fetch -- people 287
333
+ ```
334
+
335
+ Search TMDB and print unified search results:
336
+
337
+ ```sh
338
+ npm run example:tmdb-search -- movie "Fight Club"
339
+ npm run example:tmdb-search -- tv "Game of Thrones"
340
+ npm run example:tmdb-search -- people "Brad Pitt"
341
+ ```
342
+
343
+ The example also reads `.env` from the repo root:
344
+
345
+ ```sh
346
+ TMDB_BEARER_TOKEN=...
347
+ # or
348
+ TMDB_READ_ACCESS_TOKEN=...
349
+ # or
350
+ TMDB_API_KEY=...
351
+ ```
352
+
353
+ If both token and API key are present, the example uses the bearer token first. TMDB's API Read Access Token usually starts with `eyJ...`; the v3 API key is a shorter hex-like string.
354
+
355
+ If `TMDB_API_KEY` accidentally contains a token starting with `eyJ`, the example treats it as a bearer token and sends it as `Authorization: Bearer ...`.
356
+
357
+ The example writes debug output to:
358
+
359
+ ```txt
360
+ data/tmdb/movie/550.raw.json
361
+ data/tmdb/movie/550.zuuid.json
362
+ data/tmdb/tv/1399.raw.json
363
+ data/tmdb/tv/1399.zuuid.json
364
+ data/tmdb/people/287.raw.json
365
+ data/tmdb/people/287.zuuid.json
366
+ data/tmdb/search/movie/fight-club.zuuid-search.json
367
+ data/tmdb/search/movie/fight-club.raw-search.json
368
+ ```
@@ -0,0 +1,29 @@
1
+ import type { SearchResponse, ZuuidData, ZuuidSearchResult } from "./entity.js";
2
+ import { type FetchTmdbMovieInput, type FetchTmdbPersonInput, type FetchTmdbTvInput, type TmdbProviderOptions, type TmdbSearchInput } from "./providers/tmdb/index.js";
3
+ import type { SourceRecord } from "./source.js";
4
+ export type ProviderConfigs = {
5
+ tmdb?: TmdbProviderOptions;
6
+ };
7
+ export type ZuuidClientConfig = {
8
+ providers?: ProviderConfigs;
9
+ };
10
+ export type MovieProviderClient<TFetchInput> = {
11
+ fetch(input: TFetchInput): Promise<ZuuidData | undefined>;
12
+ fetchSourceRecord(input: TFetchInput): Promise<SourceRecord | undefined>;
13
+ search(input: TmdbSearchInput): Promise<SearchResponse<ZuuidSearchResult>>;
14
+ searchSourceRecords(input: TmdbSearchInput): Promise<SearchResponse<SourceRecord>>;
15
+ transform(source: SourceRecord): Promise<ZuuidData>;
16
+ };
17
+ export type ZuuidClient = {
18
+ movie: {
19
+ tmdb?: MovieProviderClient<FetchTmdbMovieInput>;
20
+ };
21
+ tv: {
22
+ tmdb?: MovieProviderClient<FetchTmdbTvInput>;
23
+ };
24
+ people: {
25
+ tmdb?: MovieProviderClient<FetchTmdbPersonInput>;
26
+ };
27
+ };
28
+ export declare function createZuuidClient(config?: ZuuidClientConfig): ZuuidClient;
29
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAKL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,mBAAmB,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,WAAW,IAAI;IAC7C,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAC1D,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACzE,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC3E,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IACnF,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;KACjD,CAAC;IACF,EAAE,EAAE;QACF,IAAI,CAAC,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;KAC9C,CAAC;IACF,MAAM,EAAE;QACN,IAAI,CAAC,EAAE,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;KAClD,CAAC;CACH,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,iBAAsB,GAAG,WAAW,CAsC7E"}
package/dist/client.js ADDED
@@ -0,0 +1,39 @@
1
+ import { TmdbProvider, transformTmdbMovie, transformTmdbPerson, transformTmdbTv } from "./providers/tmdb/index.js";
2
+ export function createZuuidClient(config = {}) {
3
+ const tmdb = config.providers?.tmdb ? new TmdbProvider(config.providers.tmdb) : undefined;
4
+ return Object.freeze({
5
+ movie: Object.freeze({
6
+ tmdb: tmdb
7
+ ? Object.freeze({
8
+ fetch: (input) => tmdb.fetchMovie(input),
9
+ fetchSourceRecord: (input) => tmdb.fetchMovieSourceRecord(input),
10
+ search: (input) => tmdb.searchMovies(input),
11
+ searchSourceRecords: (input) => tmdb.searchMovieSourceRecords(input),
12
+ transform: (source) => transformTmdbMovie(source, tmdb.transformOptions())
13
+ })
14
+ : undefined
15
+ }),
16
+ tv: Object.freeze({
17
+ tmdb: tmdb
18
+ ? Object.freeze({
19
+ fetch: (input) => tmdb.fetchTv(input),
20
+ fetchSourceRecord: (input) => tmdb.fetchTvSourceRecord(input),
21
+ search: (input) => tmdb.searchTv(input),
22
+ searchSourceRecords: (input) => tmdb.searchTvSourceRecords(input),
23
+ transform: (source) => transformTmdbTv(source, tmdb.transformOptions())
24
+ })
25
+ : undefined
26
+ }),
27
+ people: Object.freeze({
28
+ tmdb: tmdb
29
+ ? Object.freeze({
30
+ fetch: (input) => tmdb.fetchPerson(input),
31
+ fetchSourceRecord: (input) => tmdb.fetchPersonSourceRecord(input),
32
+ search: (input) => tmdb.searchPeople(input),
33
+ searchSourceRecords: (input) => tmdb.searchPersonSourceRecords(input),
34
+ transform: (source) => transformTmdbPerson(source, tmdb.transformOptions())
35
+ })
36
+ : undefined
37
+ })
38
+ });
39
+ }
@@ -0,0 +1,106 @@
1
+ import type { ExternalId, Provenance } from "./source.js";
2
+ import type { JsonValue } from "./types.js";
3
+ export type EntityKind = "event" | "listen" | "people" | "play" | "read" | "visit" | "watch" | string;
4
+ export type CategoryInfo = {
5
+ category: string;
6
+ kind: EntityKind;
7
+ };
8
+ export type Alias = {
9
+ value: string;
10
+ language?: string;
11
+ region?: string;
12
+ aliasType: string;
13
+ isPrimary: boolean;
14
+ source?: string;
15
+ };
16
+ export type Description = {
17
+ language?: string;
18
+ region?: string;
19
+ value: string;
20
+ source?: string;
21
+ };
22
+ export type Detail = {
23
+ key: string;
24
+ value: JsonValue;
25
+ source?: string;
26
+ };
27
+ export type MediaAsset = {
28
+ url: string;
29
+ mediaType: string;
30
+ mediaCategory: string;
31
+ width?: number;
32
+ height?: number;
33
+ isPrimary: boolean;
34
+ data?: JsonValue;
35
+ source?: string;
36
+ };
37
+ export type RelationKind = "appears_in" | "authored_by" | "based_on" | "contains" | "duplicate_of" | "mentions" | "part_of" | "performed_by" | "published_by" | "related_to" | "same_as" | "source_of" | string;
38
+ export type RecommendationKind = "similar" | "related" | "same_creator" | "same_series" | "same_topic" | string;
39
+ export type ZuuidData = {
40
+ zuuid: string;
41
+ kind: EntityKind;
42
+ category: string;
43
+ primaryTitle: string;
44
+ primaryDate?: string;
45
+ rating?: number;
46
+ cover?: string;
47
+ aliases: Alias[];
48
+ descriptions: Description[];
49
+ details: Detail[];
50
+ media: MediaAsset[];
51
+ relations: EntityRelation[];
52
+ recommendations: RecommendationEdge[];
53
+ tags: string[];
54
+ externalIds: ExternalId[];
55
+ provenance: Provenance[];
56
+ };
57
+ export type ZuuidListItem = {
58
+ id: string;
59
+ zuuid: string;
60
+ category: string;
61
+ title: string;
62
+ date: string | null;
63
+ cover: string | null;
64
+ rating: number | null;
65
+ weight: number | null;
66
+ relationType: string | null;
67
+ attribute: string | null;
68
+ order: number | null;
69
+ };
70
+ export type EntityRelation = ZuuidListItem & {
71
+ relationType: RelationKind;
72
+ direction: "outgoing" | "incoming" | "symmetric";
73
+ source?: string;
74
+ externalId?: string;
75
+ confidence?: number;
76
+ data?: JsonValue;
77
+ };
78
+ export type RecommendationEdge = ZuuidListItem & {
79
+ recommendationType: RecommendationKind;
80
+ relationType: RecommendationKind;
81
+ source?: string;
82
+ externalId?: string;
83
+ reasons: string[];
84
+ };
85
+ export type ZuuidSearchResult = ZuuidListItem & {
86
+ source: ExternalId;
87
+ };
88
+ export type SearchPagination = {
89
+ page: number;
90
+ totalPages: number;
91
+ totalResults: number;
92
+ };
93
+ export type SearchResponse<T> = {
94
+ results: T[];
95
+ pagination: SearchPagination;
96
+ };
97
+ export type CreateZuuidDataInput = {
98
+ zuuid: string;
99
+ category: string;
100
+ primaryTitle: string;
101
+ };
102
+ export declare function createZuuidData(input: CreateZuuidDataInput): ZuuidData;
103
+ export declare function createZuuidDataFromParts(zuuid: string, category: CategoryInfo, primaryTitle: string): ZuuidData;
104
+ export declare function categoryFor(value: string): CategoryInfo;
105
+ export declare function kindForCategory(category: string): EntityKind;
106
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
package/dist/entity.js ADDED
@@ -0,0 +1,101 @@
1
+ import { normalizeUuid } from "./uuid.js";
2
+ export function createZuuidData(input) {
3
+ const zuuid = normalizeUuid(input.zuuid);
4
+ const category = categoryFor(input.category);
5
+ return createZuuidDataFromParts(zuuid, category, input.primaryTitle);
6
+ }
7
+ export function createZuuidDataFromParts(zuuid, category, primaryTitle) {
8
+ return {
9
+ zuuid: normalizeUuid(zuuid),
10
+ kind: category.kind,
11
+ category: category.category,
12
+ primaryTitle,
13
+ aliases: [],
14
+ descriptions: [],
15
+ details: [],
16
+ media: [],
17
+ relations: [],
18
+ recommendations: [],
19
+ tags: [],
20
+ externalIds: [],
21
+ provenance: []
22
+ };
23
+ }
24
+ export function categoryFor(value) {
25
+ const normalized = normalizeKeyPart(value);
26
+ return {
27
+ category: normalized,
28
+ kind: kindForCategory(normalized),
29
+ };
30
+ }
31
+ export function kindForCategory(category) {
32
+ switch (normalizeKeyPart(category)) {
33
+ case "movie":
34
+ case "film":
35
+ case "tv":
36
+ case "tvshow":
37
+ case "tv_show":
38
+ case "tvseason":
39
+ case "tv_season":
40
+ case "series":
41
+ case "anime":
42
+ case "documentary":
43
+ case "video":
44
+ return "watch";
45
+ case "album":
46
+ case "music":
47
+ case "musicalbum":
48
+ case "release":
49
+ case "master":
50
+ case "track":
51
+ case "recording":
52
+ case "podcast":
53
+ case "podcast_episode":
54
+ case "audiobook":
55
+ return "listen";
56
+ case "book":
57
+ case "work":
58
+ case "article":
59
+ case "web_page":
60
+ case "comic":
61
+ case "manga":
62
+ case "paper":
63
+ case "blog_post":
64
+ return "read";
65
+ case "game":
66
+ case "videogame":
67
+ case "boardgame":
68
+ case "platform":
69
+ return "play";
70
+ case "place":
71
+ case "location":
72
+ case "venue":
73
+ case "restaurant":
74
+ case "route":
75
+ case "city":
76
+ case "country":
77
+ return "visit";
78
+ case "event":
79
+ case "concert":
80
+ case "screening":
81
+ case "exhibition":
82
+ case "match":
83
+ case "festival":
84
+ return "event";
85
+ case "person":
86
+ case "people":
87
+ case "artist":
88
+ case "author":
89
+ case "actor":
90
+ case "creator":
91
+ case "organization":
92
+ case "company":
93
+ case "team":
94
+ return "people";
95
+ default:
96
+ return normalizeKeyPart(category);
97
+ }
98
+ }
99
+ function normalizeKeyPart(value) {
100
+ return value.trim().toLowerCase();
101
+ }
package/dist/hash.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { JsonValue } from "./types.js";
2
+ export declare function stablePayloadHash(payload: JsonValue): Promise<string>;
3
+ //# sourceMappingURL=hash.d.ts.map