@tinacms/schema-tools 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2146,8 +2146,11 @@ const resolveField = (field, schema) => {
2146
2146
  if (field.list) {
2147
2147
  return {
2148
2148
  component: "list",
2149
+ // The list plugin builds each item from `field.field` alone, so
2150
+ // anything the item's input needs has to be carried in here.
2149
2151
  field: {
2150
- component: "image"
2152
+ component: "image",
2153
+ ...field.accept ? { accept: field.accept } : {}
2151
2154
  },
2152
2155
  ...field,
2153
2156
  ...extraFields
@@ -2313,6 +2316,83 @@ const CONTENT_FORMATS = [
2313
2316
  "yml",
2314
2317
  "toml"
2315
2318
  ];
2319
+ const MEDIA_EXTENSIONS = [
2320
+ "jpg",
2321
+ "jpeg",
2322
+ "png",
2323
+ "gif",
2324
+ "webp",
2325
+ "svg",
2326
+ "avif",
2327
+ "ico",
2328
+ "mp4",
2329
+ "webm",
2330
+ "mov",
2331
+ "mp3",
2332
+ "wav",
2333
+ "ogg",
2334
+ "pdf",
2335
+ "json",
2336
+ "csv",
2337
+ "txt"
2338
+ ];
2339
+ const MEDIA_CATEGORIES = {
2340
+ image: ["jpg", "jpeg", "png", "gif", "webp", "svg", "avif", "ico"],
2341
+ video: ["mp4", "webm", "mov"],
2342
+ audio: ["mp3", "wav", "ogg"],
2343
+ document: ["pdf", "json", "csv", "txt"]
2344
+ };
2345
+ const MEDIA_EXTENSION_ALIASES = {
2346
+ jpg: ["jpeg"],
2347
+ jpeg: ["jpg"]
2348
+ };
2349
+ const MEDIA_MIME_TYPES = {
2350
+ jpg: "image/jpeg",
2351
+ jpeg: "image/jpeg",
2352
+ png: "image/png",
2353
+ gif: "image/gif",
2354
+ webp: "image/webp",
2355
+ svg: "image/svg+xml",
2356
+ avif: "image/avif",
2357
+ ico: "image/x-icon",
2358
+ mp4: "video/mp4",
2359
+ webm: "video/webm",
2360
+ mov: "video/quicktime",
2361
+ mp3: "audio/mpeg",
2362
+ wav: "audio/wav",
2363
+ ogg: "audio/ogg",
2364
+ pdf: "application/pdf",
2365
+ json: "application/json",
2366
+ csv: "text/csv",
2367
+ txt: "text/plain"
2368
+ };
2369
+ const extensionOf = (value) => {
2370
+ const path = value.split(/[?#]/)[0];
2371
+ const dot = path.lastIndexOf(".");
2372
+ const slash = path.lastIndexOf("/");
2373
+ return dot > slash + 1 ? path.slice(dot + 1).toLowerCase() : "";
2374
+ };
2375
+ const resolveMediaAccept = (accept) => {
2376
+ if (!accept) return [];
2377
+ const requested = Array.isArray(accept) ? accept : [accept];
2378
+ const resolved = /* @__PURE__ */ new Set();
2379
+ const add = (ext) => {
2380
+ resolved.add(ext);
2381
+ for (const alias of MEDIA_EXTENSION_ALIASES[ext] ?? []) {
2382
+ resolved.add(alias);
2383
+ }
2384
+ };
2385
+ for (const entry of requested) {
2386
+ if (entry in MEDIA_CATEGORIES) {
2387
+ for (const ext of MEDIA_CATEGORIES[entry]) {
2388
+ add(ext);
2389
+ }
2390
+ } else if (MEDIA_EXTENSIONS.includes(entry)) {
2391
+ add(entry);
2392
+ }
2393
+ }
2394
+ return [...resolved];
2395
+ };
2316
2396
  const ALL_HEADING_LEVELS = [
2317
2397
  "h1",
2318
2398
  "h2",
@@ -2877,6 +2957,9 @@ export {
2877
2957
  ERR_HAS_REFERENCES,
2878
2958
  ERR_NOT_INDEXED,
2879
2959
  ERR_PATH_TRAVERSAL,
2960
+ MEDIA_CATEGORIES,
2961
+ MEDIA_EXTENSIONS,
2962
+ MEDIA_MIME_TYPES,
2880
2963
  NAMER,
2881
2964
  RELATIVE_PATH_ALLOWED_CHARS_MESSAGE,
2882
2965
  RELATIVE_PATH_REGEX,
@@ -2885,6 +2968,7 @@ export {
2885
2968
  TinaSchemaValidationError,
2886
2969
  addNamespaceToSchema,
2887
2970
  canonicalPath,
2971
+ extensionOf,
2888
2972
  isHeadingLevel,
2889
2973
  isValidRelativePath,
2890
2974
  normalizeHeadingLevels,
@@ -2892,6 +2976,7 @@ export {
2892
2976
  parseURL,
2893
2977
  resolveField,
2894
2978
  resolveForm,
2979
+ resolveMediaAccept,
2895
2980
  validateSchema,
2896
2981
  validateTinaCloudSchemaConfig
2897
2982
  };
@@ -206,6 +206,60 @@ type DateFormatProps = {
206
206
  export type DateTimeField = (FieldGeneric<string, undefined, DateFormatProps> | FieldGeneric<string, true, DateFormatProps> | FieldGeneric<string, false, DateFormatProps>) & BaseField & {
207
207
  type: 'datetime';
208
208
  };
209
+ /**
210
+ * File extensions the media manager can filter on. Not a list of what may be
211
+ * uploaded — that is `media.accept` — but the vocabulary a field or filter
212
+ * uses to narrow what is shown.
213
+ */
214
+ export declare const MEDIA_EXTENSIONS: readonly ["jpg", "jpeg", "png", "gif", "webp", "svg", "avif", "ico", "mp4", "webm", "mov", "mp3", "wav", "ogg", "pdf", "json", "csv", "txt"];
215
+ export type MediaExtension = (typeof MEDIA_EXTENSIONS)[number];
216
+ export declare const MEDIA_CATEGORIES: {
217
+ readonly image: readonly ["jpg", "jpeg", "png", "gif", "webp", "svg", "avif", "ico"];
218
+ readonly video: readonly ["mp4", "webm", "mov"];
219
+ readonly audio: readonly ["mp3", "wav", "ogg"];
220
+ readonly document: readonly ["pdf", "json", "csv", "txt"];
221
+ };
222
+ export type MediaCategory = keyof typeof MEDIA_CATEGORIES;
223
+ export type MediaAccept = MediaExtension | MediaCategory;
224
+ /**
225
+ * The MIME type each extension belongs to. Needed because react-dropzone
226
+ * keys its `accept` by MIME type and drops bare extensions from the native
227
+ * file picker.
228
+ */
229
+ export declare const MEDIA_MIME_TYPES: {
230
+ readonly jpg: "image/jpeg";
231
+ readonly jpeg: "image/jpeg";
232
+ readonly png: "image/png";
233
+ readonly gif: "image/gif";
234
+ readonly webp: "image/webp";
235
+ readonly svg: "image/svg+xml";
236
+ readonly avif: "image/avif";
237
+ readonly ico: "image/x-icon";
238
+ readonly mp4: "video/mp4";
239
+ readonly webm: "video/webm";
240
+ readonly mov: "video/quicktime";
241
+ readonly mp3: "audio/mpeg";
242
+ readonly wav: "audio/wav";
243
+ readonly ogg: "audio/ogg";
244
+ readonly pdf: "application/pdf";
245
+ readonly json: "application/json";
246
+ readonly csv: "text/csv";
247
+ readonly txt: "text/plain";
248
+ };
249
+ /**
250
+ * Lowercased extension without the dot, from a filename, path or URL.
251
+ * Query strings and hashes are dropped first, so a transformed asset URL
252
+ * like `hero.png?fit=crop` still reads as `png`. Dotfiles and extensionless
253
+ * names return ''.
254
+ */
255
+ export declare const extensionOf: (value: string) => string;
256
+ /**
257
+ * Flattens a field's `accept` into concrete extensions, expanding any
258
+ * category shorthand and pulling in aliases. Unknown values are dropped
259
+ * rather than passed through, so a typo narrows to nothing visible instead
260
+ * of silently disabling the filter.
261
+ */
262
+ export declare const resolveMediaAccept: (accept: MediaAccept | MediaAccept[] | undefined) => MediaExtension[];
209
263
  export type ImageField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
210
264
  type: 'image';
211
265
  /**
@@ -218,6 +272,19 @@ export type ImageField = (FieldGeneric<string, undefined> | FieldGeneric<string,
218
272
  * ```
219
273
  */
220
274
  uploadDir?: (formValues: Record<string, any>) => string;
275
+ /**
276
+ * Restricts this field to specific file types. Narrows what the media
277
+ * manager offers when browsing and what the field will accept on upload,
278
+ * overriding the global `media.accept`.
279
+ *
280
+ * @example
281
+ * ```ts
282
+ * accept: 'pdf'
283
+ * accept: ['png', 'svg']
284
+ * accept: 'image'
285
+ * ```
286
+ */
287
+ accept?: MediaAccept | MediaAccept[];
221
288
  };
222
289
  type ReferenceFieldOptions = {
223
290
  optionComponent?: OptionComponent;
@@ -429,7 +496,19 @@ export interface AuthProvider {
429
496
  **/
430
497
  authenticate: (props?: Record<string, any>) => Promise<any | null>;
431
498
  fetchWithToken: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
499
+ /**
500
+ * Turns a tokened 401 into the CMS sending the user back to login. The
501
+ * Tina client assigns this automatically; providers extending
502
+ * AbstractAuthProvider get the invocation for free, while a provider that
503
+ * implements its own fetchWithToken must invoke it itself.
504
+ */
505
+ sessionExpiredListener?: () => void;
432
506
  isAuthorized: (context?: any) => Promise<boolean>;
507
+ /**
508
+ * May reject when the identity backend is unreachable (a transport
509
+ * failure is not an auth answer); callers should catch and treat that as
510
+ * an error rather than as logged-out.
511
+ */
433
512
  isAuthenticated: () => Promise<boolean>;
434
513
  getLoginStrategy: () => LoginStrategy;
435
514
  getLoginScreen: () => FC<LoginScreenProps> | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tinacms/schema-tools",
3
3
  "type": "module",
4
- "version": "2.9.0",
4
+ "version": "2.10.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -24,7 +24,7 @@
24
24
  "ts-jest": "^29.2.5",
25
25
  "typescript": "^5.7.3",
26
26
  "yup": "^1.6.1",
27
- "@tinacms/scripts": "1.6.3"
27
+ "@tinacms/scripts": "1.6.4"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=16.14.0",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "repository": {
37
37
  "url": "https://github.com/tinacms/tinacms.git",
38
- "directory": "packages/@tinacms/cli"
38
+ "directory": "packages/@tinacms/schema-tools"
39
39
  },
40
40
  "dependencies": {
41
41
  "picomatch-browser": "2.2.6",
@@ -46,7 +46,6 @@
46
46
  "build": "tinacms-scripts build",
47
47
  "test": "jest --config jest.config.js",
48
48
  "types": "pnpm tsc",
49
- "test-watch": "jest --passWithNoTests --watch",
50
- "generate:schema": "pnpm node scripts/generateSchema.js"
49
+ "test-watch": "jest --passWithNoTests --watch"
51
50
  }
52
51
  }