@byline/admin 3.17.1 → 3.19.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 (31) hide show
  1. package/dist/fields/file/file-field.js +8 -2
  2. package/dist/fields/image/image-field.js +8 -2
  3. package/dist/forms/form-context.d.ts +13 -1
  4. package/dist/forms/form-context.js +2 -1
  5. package/dist/forms/form-renderer.js +18 -1
  6. package/dist/forms/path-widget.d.ts +19 -1
  7. package/dist/forms/path-widget.js +10 -6
  8. package/dist/forms/upload-executor.d.ts +27 -3
  9. package/dist/forms/upload-executor.js +83 -8
  10. package/dist/forms/upload-executor.test.node.d.ts +8 -0
  11. package/dist/modules/admin-account/errors.d.ts +2 -2
  12. package/dist/modules/admin-activity/abilities.d.ts +1 -1
  13. package/dist/modules/admin-permissions/abilities.d.ts +2 -2
  14. package/dist/modules/admin-permissions/errors.d.ts +2 -2
  15. package/dist/modules/admin-permissions/schemas.d.ts +4 -4
  16. package/dist/modules/admin-roles/abilities.d.ts +4 -4
  17. package/dist/modules/admin-roles/errors.d.ts +4 -4
  18. package/dist/modules/admin-users/abilities.d.ts +5 -5
  19. package/dist/modules/admin-users/errors.d.ts +5 -5
  20. package/dist/modules/admin-users/schemas.d.ts +6 -6
  21. package/dist/vendor/noble-argon2/blake2.js +17 -17
  22. package/dist/widgets/source-locale-badge/source-locale-badge.d.ts +4 -18
  23. package/package.json +16 -16
  24. package/src/fields/file/file-field.tsx +16 -2
  25. package/src/fields/image/image-field.tsx +16 -2
  26. package/src/forms/form-context.tsx +14 -0
  27. package/src/forms/form-renderer.tsx +27 -0
  28. package/src/forms/path-widget.test.tsx +33 -0
  29. package/src/forms/path-widget.tsx +33 -5
  30. package/src/forms/upload-executor.test.node.ts +176 -0
  31. package/src/forms/upload-executor.ts +209 -14
@@ -25,7 +25,7 @@ const FileField = ({ field, collectionPath, value, defaultValue, onChange: _onCh
25
25
  const isDirty = useIsDirty(fieldPath);
26
26
  const fieldValue = useFieldValue(fieldPath);
27
27
  const isUploading = useIsFieldUploading(fieldPath);
28
- const { removePendingUpload } = useFormContext();
28
+ const { removePendingUpload, documentId } = useFormContext();
29
29
  const handleChange = useFieldChangeHandler(field, fieldPath);
30
30
  const incomingValue = isDirty ? fieldValue ?? null : value ?? fieldValue ?? defaultValue ?? null;
31
31
  const isPending = isPendingStoredFileValue(incomingValue);
@@ -35,6 +35,7 @@ const FileField = ({ field, collectionPath, value, defaultValue, onChange: _onCh
35
35
  return 'placeholder' === maybe.storageProvider && 'pending' === maybe.storagePath;
36
36
  };
37
37
  const showUploadWidget = null == incomingValue || isOldPlaceholder(incomingValue);
38
+ const uploadGated = field.upload?.requireSavedDocument === true && null == documentId;
38
39
  const handleRemove = ()=>{
39
40
  if (isPending) removePendingUpload(fieldPath);
40
41
  handleChange(null);
@@ -54,7 +55,12 @@ const FileField = ({ field, collectionPath, value, defaultValue, onChange: _onCh
54
55
  required: !field.optional
55
56
  })
56
57
  }),
57
- showUploadWidget ? collectionPath ? /*#__PURE__*/ jsx(FileUploadField, {
58
+ showUploadWidget ? uploadGated ? /*#__PURE__*/ jsx("div", {
59
+ className: classnames('byline-field-file-empty', file_field_module.empty),
60
+ role: "note",
61
+ "data-testid": "upload-require-saved-document",
62
+ children: t('fields.upload.requireSavedDocument')
63
+ }) : collectionPath ? /*#__PURE__*/ jsx(FileUploadField, {
58
64
  field: field,
59
65
  collectionPath: collectionPath,
60
66
  fieldPath: fieldPath,
@@ -14,7 +14,7 @@ const ImageField = ({ field, collectionPath, value, defaultValue, onChange: _onC
14
14
  const isDirty = useIsDirty(fieldPath);
15
15
  const fieldValue = useFieldValue(fieldPath);
16
16
  const isUploading = useIsFieldUploading(fieldPath);
17
- const { removePendingUpload } = useFormContext();
17
+ const { removePendingUpload, documentId } = useFormContext();
18
18
  const { t } = useTranslation('byline-admin');
19
19
  const handleChange = useFieldChangeHandler(field, fieldPath);
20
20
  const incomingValue = isDirty ? fieldValue ?? null : value ?? fieldValue ?? defaultValue ?? null;
@@ -25,6 +25,7 @@ const ImageField = ({ field, collectionPath, value, defaultValue, onChange: _onC
25
25
  return 'placeholder' === maybe.storageProvider && 'pending' === maybe.storagePath;
26
26
  };
27
27
  const showUploadWidget = null == incomingValue || isOldPlaceholder(incomingValue);
28
+ const uploadGated = field.upload?.requireSavedDocument === true && null == documentId;
28
29
  const thumbVariant = incomingValue && !isPendingStoredFileValue(incomingValue) ? incomingValue.variants?.find((v)=>'thumbnail' === v.name) : void 0;
29
30
  const previewUrl = thumbVariant?.storageUrl ?? incomingValue?.storageUrl;
30
31
  const handleRemove = ()=>{
@@ -46,7 +47,12 @@ const ImageField = ({ field, collectionPath, value, defaultValue, onChange: _onC
46
47
  required: !field.optional
47
48
  })
48
49
  }),
49
- showUploadWidget ? collectionPath ? /*#__PURE__*/ jsx(ImageUploadField, {
50
+ showUploadWidget ? uploadGated ? /*#__PURE__*/ jsx("div", {
51
+ className: classnames('byline-field-image-empty', image_field_module.empty),
52
+ role: "note",
53
+ "data-testid": "upload-require-saved-document",
54
+ children: t('fields.upload.requireSavedDocument')
55
+ }) : collectionPath ? /*#__PURE__*/ jsx(ImageUploadField, {
50
56
  field: field,
51
57
  collectionPath: collectionPath,
52
58
  fieldPath: fieldPath,
@@ -48,6 +48,13 @@ export interface DirtyBreakdown {
48
48
  availableLocalesDirty: boolean;
49
49
  }
50
50
  interface FormContextType {
51
+ /**
52
+ * The persisted document id when the form edits an existing document,
53
+ * `null` while the document is unsaved (create mode). Upload widgets use
54
+ * this to honour `upload.requireSavedDocument` (see `UploadConfig` in
55
+ * `@byline/core`).
56
+ */
57
+ documentId: string | null;
51
58
  setFieldValue: (name: string, value: any) => void;
52
59
  setFieldStore: (name: string, value: any) => void;
53
60
  getFieldValue: (name: string) => any;
@@ -90,9 +97,14 @@ interface FormContextType {
90
97
  subscribeSystemAvailableLocales: (listener: SystemAvailableLocalesListener) => () => void;
91
98
  }
92
99
  export declare const useFormContext: () => FormContextType;
93
- export declare const FormProvider: ({ children, initialData, }: {
100
+ export declare const FormProvider: ({ children, initialData, documentId, }: {
94
101
  children: React.ReactNode;
95
102
  initialData?: Record<string, any>;
103
+ /**
104
+ * The persisted document id (edit mode); `null` while unsaved. Exposed on
105
+ * the context for upload widgets honouring `upload.requireSavedDocument`.
106
+ */
107
+ documentId?: string | null;
96
108
  }) => React.JSX.Element;
97
109
  /**
98
110
  * Subscribe to the system `path` slot edited by the path widget.
@@ -22,7 +22,7 @@ const useFormContext = ()=>{
22
22
  if (null == context) throw new Error('useFormContext must be used within a FormProvider');
23
23
  return context;
24
24
  };
25
- const FormProvider = ({ children, initialData = {} })=>{
25
+ const FormProvider = ({ children, initialData = {}, documentId = null })=>{
26
26
  const fieldValues = useRef(JSON.parse(JSON.stringify(initialData?.fields ?? initialData)));
27
27
  const initialValues = useRef(initialData?.fields ?? initialData);
28
28
  const errorsRef = useRef([]);
@@ -376,6 +376,7 @@ const FormProvider = ({ children, initialData = {} })=>{
376
376
  ]);
377
377
  return /*#__PURE__*/ jsx(FormContext.Provider, {
378
378
  value: {
379
+ documentId,
379
380
  setFieldValue,
380
381
  setFieldStore,
381
382
  getFieldValue,
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { getClientConfig } from "@byline/core";
4
5
  import { useTranslation } from "@byline/i18n/react";
5
6
  import { Alert, Button, ComboButton } from "@byline/ui/react";
6
7
  import classnames from "classnames";
@@ -32,6 +33,15 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
32
33
  const [pendingSystemFieldsSubmit, setPendingSystemFieldsSubmit] = useState(null);
33
34
  const [contentLocale, setContentLocale] = useState(initialLocale ?? defaultLocale);
34
35
  const { uploadField } = useBylineFieldServices();
36
+ const pathSlugifier = getClientConfig().slugifier;
37
+ const pathSourceLocked = useMemo(()=>{
38
+ if (!useAsPath) return false;
39
+ const source = fields.find((f)=>f.name === useAsPath);
40
+ return null != source && ('counter' === source.type || true === source.readOnly);
41
+ }, [
42
+ useAsPath,
43
+ fields
44
+ ]);
35
45
  useEffect(()=>{
36
46
  if (initialLocale) setContentLocale(initialLocale);
37
47
  }, [
@@ -112,6 +122,10 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
112
122
  try {
113
123
  const uploadResult = await executeUploadsWithProgress(pendingUploads, uploadField, ({ fieldPath, status })=>{
114
124
  setFieldUploading(fieldPath, 'uploading' === status);
125
+ }, {
126
+ documentId: 'edit' === mode && 'string' == typeof initialData?.id ? initialData.id : void 0,
127
+ fields,
128
+ getFormValues: getFieldValues
115
129
  });
116
130
  if (!uploadResult.allSucceeded) {
117
131
  for (const [fieldPath, errorMessage] of uploadResult.errors.entries())setFieldError(fieldPath, t('forms.uploadFailedFieldError', {
@@ -345,7 +359,9 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
345
359
  collectionPath: collectionPath ?? '',
346
360
  defaultLocale: defaultLocale,
347
361
  activeLocale: contentLocale,
348
- mode: mode
362
+ mode: mode,
363
+ slugifier: pathSlugifier,
364
+ sourceLocked: pathSourceLocked
349
365
  }),
350
366
  tree && 'edit' === mode && 'string' == typeof initialData?.id && /*#__PURE__*/ jsx(TreePlacementWidget, {
351
367
  collectionPath: collectionPath ?? '',
@@ -386,6 +402,7 @@ const FormRenderer = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpu
386
402
  const savedTabsRef = useRef({});
387
403
  return /*#__PURE__*/ jsx(FormProvider, {
388
404
  initialData: initialData,
405
+ documentId: 'edit' === mode && 'string' == typeof initialData?.id ? initialData.id : null,
389
406
  children: /*#__PURE__*/ jsx(FormContent, {
390
407
  mode: mode,
391
408
  fields: fields,
@@ -1,3 +1,4 @@
1
+ import type { SlugifierFn } from '@byline/core';
1
2
  export interface PathWidgetProps {
2
3
  /** The collection's `useAsPath` source field name, when configured. */
3
4
  useAsPath: string | undefined;
@@ -16,6 +17,23 @@ export interface PathWidgetProps {
16
17
  activeLocale: string;
17
18
  /** `'create'` shows the live derived preview as placeholder text. */
18
19
  mode: 'create' | 'edit';
20
+ /**
21
+ * Installation slugifier used to derive the live preview. Must match the
22
+ * server-side `ServerConfig.slugifier` so the preview agrees with what is
23
+ * persisted. Defaults to the built-in `slugify` when omitted — callers
24
+ * that keep the default slugifier need not pass this.
25
+ */
26
+ slugifier?: SlugifierFn;
27
+ /**
28
+ * When `true`, the `useAsPath` source field's value is not editable through
29
+ * this form (e.g. it is an allocator-assigned `counter`, or an otherwise
30
+ * read-only field). Its value is either server-assigned — and so not
31
+ * reproducible client-side — or simply cannot change, which means the
32
+ * source-derived live preview and the "Regenerate" affordance are
33
+ * meaningless. When set, the widget suppresses both and just shows the
34
+ * persisted path. Defaults to `false`.
35
+ */
36
+ sourceLocked?: boolean;
19
37
  }
20
38
  /**
21
39
  * System-managed `path` widget.
@@ -33,4 +51,4 @@ export interface PathWidgetProps {
33
51
  * Stable override handles: `.byline-form-path`, `.byline-form-path-header`,
34
52
  * `.byline-form-path-regenerate`.
35
53
  */
36
- export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, }: PathWidgetProps) => import("react").JSX.Element;
54
+ export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked, }: PathWidgetProps) => import("react").JSX.Element;
@@ -12,25 +12,28 @@ function coerceToString(value) {
12
12
  if (value instanceof Date) return value.toISOString();
13
13
  return String(value);
14
14
  }
15
- const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode })=>{
15
+ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked = false })=>{
16
16
  const { setSystemPath } = useFormContext();
17
17
  const { t } = useTranslation('byline-admin');
18
18
  const systemPath = useSystemPath();
19
19
  const sourceValue = useFieldValue(useAsPath ?? '');
20
+ const runSlugify = slugifier ?? slugify;
20
21
  const isReadOnly = activeLocale !== defaultLocale;
21
22
  const livePreview = useMemo(()=>{
22
- if (!useAsPath) return '';
23
+ if (!useAsPath || sourceLocked) return '';
23
24
  const asString = coerceToString(sourceValue);
24
25
  if (0 === asString.length) return '';
25
- return slugify(asString, {
26
+ return runSlugify(asString, {
26
27
  locale: defaultLocale,
27
28
  collectionPath
28
29
  });
29
30
  }, [
30
31
  useAsPath,
32
+ sourceLocked,
31
33
  sourceValue,
32
34
  defaultLocale,
33
- collectionPath
35
+ collectionPath,
36
+ runSlugify
34
37
  ]);
35
38
  const inputValue = systemPath ?? '';
36
39
  const handleChange = useCallback((next)=>{
@@ -46,14 +49,15 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mo
46
49
  ]);
47
50
  const formatted = useMemo(()=>{
48
51
  if (0 === inputValue.length) return '';
49
- return slugify(inputValue, {
52
+ return runSlugify(inputValue, {
50
53
  locale: defaultLocale,
51
54
  collectionPath
52
55
  });
53
56
  }, [
54
57
  inputValue,
55
58
  defaultLocale,
56
- collectionPath
59
+ collectionPath,
60
+ runSlugify
57
61
  ]);
58
62
  const validationHint = inputValue.length > 0 && formatted !== inputValue ? t('pathWidget.suggestedHint', {
59
63
  formatted
@@ -12,7 +12,7 @@
12
12
  * This enables "deferred uploads" — files are selected/previewed immediately
13
13
  * but only uploaded when the user clicks Save.
14
14
  */
15
- import type { StoredFileValue } from '@byline/core';
15
+ import type { Field, StoredFileValue } from '@byline/core';
16
16
  import type { UploadFieldFn } from '../fields/field-services-types';
17
17
  import type { PendingUpload } from './form-context';
18
18
  export interface UploadResult {
@@ -31,6 +31,28 @@ export interface ExecuteUploadsResult {
31
31
  /** Whether all uploads succeeded */
32
32
  allSucceeded: boolean;
33
33
  }
34
+ /**
35
+ * Optional document context threaded from the form renderer so upload
36
+ * requests carry the state that server-side `beforeStore` / `afterStore`
37
+ * hooks need (see `UploadConfig.context` in `@byline/core`).
38
+ */
39
+ export interface UploadExecutionContext {
40
+ /**
41
+ * The persisted document id (edit mode). Posted as `documentId` on every
42
+ * upload request; omitted while the document is unsaved (create mode).
43
+ */
44
+ documentId?: string;
45
+ /**
46
+ * The collection's schema fields — used to locate each upload field's
47
+ * `upload.context` declaration by walking the pending upload's field path.
48
+ */
49
+ fields?: readonly Field[];
50
+ /**
51
+ * Snapshot accessor for the live form values, resolved lazily per upload
52
+ * so context reflects the state at the moment the request is built.
53
+ */
54
+ getFormValues?: () => Record<string, any>;
55
+ }
34
56
  /**
35
57
  * Execute all pending uploads sequentially.
36
58
  * Returns a result object with successful uploads and any errors.
@@ -38,9 +60,11 @@ export interface ExecuteUploadsResult {
38
60
  * @param pendingUploads - Map of field path to PendingUpload
39
61
  * @param uploadField - Host-provided upload transport (resolved via
40
62
  * `useBylineFieldServices()` in the calling React tree)
63
+ * @param executionContext - Optional document/form context appended to each
64
+ * upload request (documentId, `upload.context` values)
41
65
  * @returns Promise resolving to ExecuteUploadsResult
42
66
  */
43
- export declare function executeUploads(pendingUploads: Map<string, PendingUpload>, uploadField: UploadFieldFn): Promise<ExecuteUploadsResult>;
67
+ export declare function executeUploads(pendingUploads: Map<string, PendingUpload>, uploadField: UploadFieldFn, executionContext?: UploadExecutionContext): Promise<ExecuteUploadsResult>;
44
68
  /**
45
69
  * Progress callback type for upload execution with progress tracking.
46
70
  */
@@ -54,4 +78,4 @@ export type UploadProgressCallback = (info: {
54
78
  * Execute uploads with progress callbacks.
55
79
  * Useful for showing upload progress in the UI.
56
80
  */
57
- export declare function executeUploadsWithProgress(pendingUploads: Map<string, PendingUpload>, uploadField: UploadFieldFn, onProgress?: UploadProgressCallback): Promise<ExecuteUploadsResult>;
81
+ export declare function executeUploadsWithProgress(pendingUploads: Map<string, PendingUpload>, uploadField: UploadFieldFn, onProgress?: UploadProgressCallback, executionContext?: UploadExecutionContext): Promise<ExecuteUploadsResult>;
@@ -1,11 +1,10 @@
1
- async function executeUploads(pendingUploads, uploadField) {
1
+ import { get } from "./nested-path.js";
2
+ async function executeUploads(pendingUploads, uploadField, executionContext) {
2
3
  const results = [];
3
4
  const successful = new Map();
4
5
  const errors = new Map();
5
6
  for (const [fieldPath, upload] of pendingUploads.entries()){
6
- const formData = new FormData();
7
- formData.append('file', upload.file);
8
- formData.append('field', uploadFieldName(fieldPath));
7
+ const formData = buildUploadFormData(fieldPath, upload, executionContext);
9
8
  try {
10
9
  const result = await uploadField(upload.collectionPath, formData, false);
11
10
  results.push({
@@ -31,11 +30,89 @@ async function executeUploads(pendingUploads, uploadField) {
31
30
  allSucceeded: 0 === errors.size
32
31
  };
33
32
  }
33
+ function buildUploadFormData(fieldPath, upload, executionContext) {
34
+ const formData = new FormData();
35
+ formData.append('file', upload.file);
36
+ formData.append('field', uploadFieldName(fieldPath));
37
+ formData.append('fieldPath', fieldPath);
38
+ if (executionContext?.documentId) formData.append('documentId', executionContext.documentId);
39
+ const contextPaths = executionContext?.fields != null ? findUploadFieldByPath(executionContext.fields, fieldPath)?.context : void 0;
40
+ if (contextPaths && contextPaths.length > 0 && executionContext?.getFormValues) {
41
+ const formValues = executionContext.getFormValues();
42
+ for (const contextPath of contextPaths){
43
+ const resolvedPath = resolveContextPath(fieldPath, contextPath);
44
+ if (void 0 === resolvedPath) continue;
45
+ const value = '' === resolvedPath ? formValues : get(formValues, resolvedPath);
46
+ const serialized = serializeContextValue(value);
47
+ if (void 0 !== serialized) formData.append(leafName(contextPath), serialized);
48
+ }
49
+ }
50
+ return formData;
51
+ }
34
52
  function uploadFieldName(fieldPath) {
35
53
  const dot = fieldPath.lastIndexOf('.');
36
54
  return -1 === dot ? fieldPath : fieldPath.slice(dot + 1);
37
55
  }
38
- async function executeUploadsWithProgress(pendingUploads, uploadField, onProgress) {
56
+ function leafName(contextPath) {
57
+ const segments = contextPath.split('/').pop() ?? contextPath;
58
+ const dot = segments.lastIndexOf('.');
59
+ return -1 === dot ? segments : segments.slice(dot + 1);
60
+ }
61
+ function resolveContextPath(fieldPath, contextPath) {
62
+ if (contextPath.startsWith('/')) return contextPath.slice(1);
63
+ const scope = fieldPath.split('.');
64
+ scope.pop();
65
+ const parts = contextPath.split('/');
66
+ const leaf = parts.pop() ?? '';
67
+ for (const part of parts)if ('.' !== part && '' !== part) {
68
+ if ('..' === part) {
69
+ if (0 === scope.length) return;
70
+ scope.pop();
71
+ continue;
72
+ }
73
+ scope.push(part);
74
+ }
75
+ return 0 === scope.length ? leaf : `${scope.join('.')}${leaf ? `.${leaf}` : ''}`;
76
+ }
77
+ function serializeContextValue(value) {
78
+ if (null == value) return;
79
+ if ('string' == typeof value) return value;
80
+ if ('number' == typeof value || 'boolean' == typeof value) return String(value);
81
+ if (isRelationEnvelope(value)) return value.targetDocumentId;
82
+ if (Array.isArray(value) && value.length > 0 && value.every(isRelationEnvelope)) return value.map((v)=>v.targetDocumentId).join(',');
83
+ try {
84
+ return JSON.stringify(value);
85
+ } catch {
86
+ return;
87
+ }
88
+ }
89
+ function isRelationEnvelope(value) {
90
+ return 'object' == typeof value && null !== value && 'string' == typeof value.targetDocumentId;
91
+ }
92
+ function findUploadFieldByPath(fields, fieldPath) {
93
+ const segments = fieldPath.split('.').map((s)=>s.replace(/\[\d+\]$/, ''));
94
+ let currentFields = fields;
95
+ for(let i = 0; i < segments.length; i++){
96
+ const name = segments[i];
97
+ const isLeaf = i === segments.length - 1;
98
+ const field = currentFields.find((f)=>f.name === name);
99
+ if (!field) break;
100
+ if (isLeaf) return 'image' === field.type || 'file' === field.type ? field.upload : void 0;
101
+ if ('group' === field.type || 'array' === field.type) {
102
+ currentFields = field.fields;
103
+ continue;
104
+ }
105
+ if ('blocks' === field.type) {
106
+ const remaining = segments[i + 1];
107
+ const block = field.blocks.find((b)=>b.fields.some((f)=>f.name === remaining));
108
+ if (!block) return;
109
+ currentFields = block.fields;
110
+ continue;
111
+ }
112
+ break;
113
+ }
114
+ }
115
+ async function executeUploadsWithProgress(pendingUploads, uploadField, onProgress, executionContext) {
39
116
  const results = [];
40
117
  const successful = new Map();
41
118
  const errors = new Map();
@@ -51,9 +128,7 @@ async function executeUploadsWithProgress(pendingUploads, uploadField, onProgres
51
128
  fieldPath,
52
129
  status: 'uploading'
53
130
  });
54
- const formData = new FormData();
55
- formData.append('file', upload.file);
56
- formData.append('field', uploadFieldName(fieldPath));
131
+ const formData = buildUploadFormData(fieldPath, upload, executionContext);
57
132
  try {
58
133
  const result = await uploadField(upload.collectionPath, formData, false);
59
134
  results.push({
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -20,8 +20,8 @@
20
20
  * code per condition.
21
21
  */
22
22
  export declare const AdminAccountErrorCodes: {
23
- readonly NOT_FOUND: "admin.account.notFound";
24
- readonly INVALID_CURRENT_PASSWORD: "admin.account.invalidCurrentPassword";
23
+ readonly NOT_FOUND: 'admin.account.notFound';
24
+ readonly INVALID_CURRENT_PASSWORD: 'admin.account.invalidCurrentPassword';
25
25
  };
26
26
  export type AdminAccountErrorCode = (typeof AdminAccountErrorCodes)[keyof typeof AdminAccountErrorCodes];
27
27
  export interface AdminAccountErrorOptions {
@@ -20,7 +20,7 @@ import type { AbilityRegistry } from '@byline/auth';
20
20
  * append-only and is written by the lifecycle write-points, never edited.
21
21
  */
22
22
  export declare const ADMIN_ACTIVITY_ABILITIES: {
23
- readonly read: "admin.activity.read";
23
+ readonly read: 'admin.activity.read';
24
24
  };
25
25
  export type AdminActivityAbilityKey = (typeof ADMIN_ACTIVITY_ABILITIES)[keyof typeof ADMIN_ACTIVITY_ABILITIES];
26
26
  /**
@@ -19,8 +19,8 @@ import type { AbilityRegistry } from '@byline/auth';
19
19
  * every permission-managing role.
20
20
  */
21
21
  export declare const ADMIN_PERMISSIONS_ABILITIES: {
22
- readonly read: "admin.permissions.read";
23
- readonly update: "admin.permissions.update";
22
+ readonly read: 'admin.permissions.read';
23
+ readonly update: 'admin.permissions.update';
24
24
  };
25
25
  export type AdminPermissionsAbilityKey = (typeof ADMIN_PERMISSIONS_ABILITIES)[keyof typeof ADMIN_PERMISSIONS_ABILITIES];
26
26
  export declare function registerAdminPermissionsAbilities(registry: AbilityRegistry): void;
@@ -14,8 +14,8 @@
14
14
  * inspector is read-only and never throws either of these.
15
15
  */
16
16
  export declare const AdminPermissionsErrorCodes: {
17
- readonly ROLE_NOT_FOUND: "admin.permissions.roleNotFound";
18
- readonly ABILITY_UNREGISTERED: "admin.permissions.abilityUnregistered";
17
+ readonly ROLE_NOT_FOUND: 'admin.permissions.roleNotFound';
18
+ readonly ABILITY_UNREGISTERED: 'admin.permissions.abilityUnregistered';
19
19
  };
20
20
  export type AdminPermissionsErrorCode = (typeof AdminPermissionsErrorCodes)[keyof typeof AdminPermissionsErrorCodes];
21
21
  export interface AdminPermissionsErrorOptions {
@@ -29,8 +29,8 @@ export declare const abilityDescriptorResponseSchema: z.ZodObject<{
29
29
  source: z.ZodNullable<z.ZodEnum<{
30
30
  admin: "admin";
31
31
  collection: "collection";
32
- plugin: "plugin";
33
32
  core: "core";
33
+ plugin: "plugin";
34
34
  }>>;
35
35
  }, z.core.$strip>;
36
36
  export type AbilityDescriptorResponse = z.infer<typeof abilityDescriptorResponseSchema>;
@@ -44,8 +44,8 @@ export declare const abilityGroupResponseSchema: z.ZodObject<{
44
44
  source: z.ZodNullable<z.ZodEnum<{
45
45
  admin: "admin";
46
46
  collection: "collection";
47
- plugin: "plugin";
48
47
  core: "core";
48
+ plugin: "plugin";
49
49
  }>>;
50
50
  }, z.core.$strip>>;
51
51
  }, z.core.$strip>;
@@ -63,8 +63,8 @@ export declare const listRegisteredAbilitiesResponseSchema: z.ZodObject<{
63
63
  source: z.ZodNullable<z.ZodEnum<{
64
64
  admin: "admin";
65
65
  collection: "collection";
66
- plugin: "plugin";
67
66
  core: "core";
67
+ plugin: "plugin";
68
68
  }>>;
69
69
  }, z.core.$strip>>;
70
70
  groups: z.ZodArray<z.ZodObject<{
@@ -77,8 +77,8 @@ export declare const listRegisteredAbilitiesResponseSchema: z.ZodObject<{
77
77
  source: z.ZodNullable<z.ZodEnum<{
78
78
  admin: "admin";
79
79
  collection: "collection";
80
- plugin: "plugin";
81
80
  core: "core";
81
+ plugin: "plugin";
82
82
  }>>;
83
83
  }, z.core.$strip>>;
84
84
  }, z.core.$strip>>;
@@ -18,10 +18,10 @@ import type { AbilityRegistry } from '@byline/auth';
18
18
  * keys there.
19
19
  */
20
20
  export declare const ADMIN_ROLES_ABILITIES: {
21
- readonly read: "admin.roles.read";
22
- readonly create: "admin.roles.create";
23
- readonly update: "admin.roles.update";
24
- readonly delete: "admin.roles.delete";
21
+ readonly read: 'admin.roles.read';
22
+ readonly create: 'admin.roles.create';
23
+ readonly update: 'admin.roles.update';
24
+ readonly delete: 'admin.roles.delete';
25
25
  };
26
26
  export type AdminRolesAbilityKey = (typeof ADMIN_ROLES_ABILITIES)[keyof typeof ADMIN_ROLES_ABILITIES];
27
27
  /**
@@ -13,10 +13,10 @@
13
13
  * ability keys in logs and admin UI messages.
14
14
  */
15
15
  export declare const AdminRolesErrorCodes: {
16
- readonly NOT_FOUND: "admin.roles.notFound";
17
- readonly MACHINE_NAME_IN_USE: "admin.roles.machineNameInUse";
18
- readonly VERSION_CONFLICT: "admin.roles.versionConflict";
19
- readonly USER_NOT_FOUND: "admin.roles.userNotFound";
16
+ readonly NOT_FOUND: 'admin.roles.notFound';
17
+ readonly MACHINE_NAME_IN_USE: 'admin.roles.machineNameInUse';
18
+ readonly VERSION_CONFLICT: 'admin.roles.versionConflict';
19
+ readonly USER_NOT_FOUND: 'admin.roles.userNotFound';
20
20
  };
21
21
  export type AdminRolesErrorCode = (typeof AdminRolesErrorCodes)[keyof typeof AdminRolesErrorCodes];
22
22
  export interface AdminRolesErrorOptions {
@@ -25,11 +25,11 @@ import type { AbilityRegistry } from '@byline/auth';
25
25
  * are strictly for administering other admin users.
26
26
  */
27
27
  export declare const ADMIN_USERS_ABILITIES: {
28
- readonly read: "admin.users.read";
29
- readonly create: "admin.users.create";
30
- readonly update: "admin.users.update";
31
- readonly delete: "admin.users.delete";
32
- readonly changePassword: "admin.users.changePassword";
28
+ readonly read: 'admin.users.read';
29
+ readonly create: 'admin.users.create';
30
+ readonly update: 'admin.users.update';
31
+ readonly delete: 'admin.users.delete';
32
+ readonly changePassword: 'admin.users.changePassword';
33
33
  };
34
34
  export type AdminUsersAbilityKey = (typeof ADMIN_USERS_ABILITIES)[keyof typeof ADMIN_USERS_ABILITIES];
35
35
  /**
@@ -18,11 +18,11 @@
18
18
  * alongside the matching ability keys in logs and admin UI messages.
19
19
  */
20
20
  export declare const AdminUsersErrorCodes: {
21
- readonly NOT_FOUND: "admin.users.notFound";
22
- readonly EMAIL_IN_USE: "admin.users.emailInUse";
23
- readonly SELF_DELETE_FORBIDDEN: "admin.users.selfDeleteForbidden";
24
- readonly SELF_DISABLE_FORBIDDEN: "admin.users.selfDisableForbidden";
25
- readonly VERSION_CONFLICT: "admin.users.versionConflict";
21
+ readonly NOT_FOUND: 'admin.users.notFound';
22
+ readonly EMAIL_IN_USE: 'admin.users.emailInUse';
23
+ readonly SELF_DELETE_FORBIDDEN: 'admin.users.selfDeleteForbidden';
24
+ readonly SELF_DISABLE_FORBIDDEN: 'admin.users.selfDisableForbidden';
25
+ readonly VERSION_CONFLICT: 'admin.users.versionConflict';
26
26
  };
27
27
  export type AdminUsersErrorCode = (typeof AdminUsersErrorCodes)[keyof typeof AdminUsersErrorCodes];
28
28
  export interface AdminUsersErrorOptions {
@@ -11,12 +11,12 @@ export declare const listAdminUsersRequestSchema: z.ZodObject<{
11
11
  pageSize: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
12
12
  query: z.ZodOptional<z.ZodString>;
13
13
  order: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
14
+ created_at: "created_at";
14
15
  email: "email";
15
- given_name: "given_name";
16
16
  family_name: "family_name";
17
- username: "username";
18
- created_at: "created_at";
17
+ given_name: "given_name";
19
18
  updated_at: "updated_at";
19
+ username: "username";
20
20
  }>>>;
21
21
  desc: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
22
22
  }, z.core.$strip>;
@@ -121,12 +121,12 @@ export declare const adminUserListResponseSchema: z.ZodObject<{
121
121
  page_size: z.ZodNumber;
122
122
  query: z.ZodString;
123
123
  order: z.ZodEnum<{
124
+ created_at: "created_at";
124
125
  email: "email";
125
- given_name: "given_name";
126
126
  family_name: "family_name";
127
- username: "username";
128
- created_at: "created_at";
127
+ given_name: "given_name";
129
128
  updated_at: "updated_at";
129
+ username: "username";
130
130
  }>;
131
131
  desc: z.ZodBoolean;
132
132
  }, z.core.$strip>;