@cedarjs/uploads 7.0.0-canary.3114

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 (113) hide show
  1. package/README.md +24 -0
  2. package/dist/authenticator.d.ts +38 -0
  3. package/dist/authenticator.d.ts.map +1 -0
  4. package/dist/authenticator.js +51 -0
  5. package/dist/cleanupStaleUploads.d.ts +50 -0
  6. package/dist/cleanupStaleUploads.d.ts.map +1 -0
  7. package/dist/cleanupStaleUploads.js +80 -0
  8. package/dist/constants.d.ts +7 -0
  9. package/dist/constants.d.ts.map +1 -0
  10. package/dist/constants.js +4 -0
  11. package/dist/deleteFile.d.ts +14 -0
  12. package/dist/deleteFile.d.ts.map +1 -0
  13. package/dist/deleteFile.js +18 -0
  14. package/dist/directives.d.ts +55 -0
  15. package/dist/directives.d.ts.map +1 -0
  16. package/dist/directives.js +174 -0
  17. package/dist/errors.d.ts +12 -0
  18. package/dist/errors.d.ts.map +1 -0
  19. package/dist/errors.js +33 -0
  20. package/dist/fastify/plugin.d.ts +49 -0
  21. package/dist/fastify/plugin.d.ts.map +1 -0
  22. package/dist/fastify/plugin.js +241 -0
  23. package/dist/index.d.ts +34 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +88 -0
  26. package/dist/keys.d.ts +18 -0
  27. package/dist/keys.d.ts.map +1 -0
  28. package/dist/keys.js +25 -0
  29. package/dist/pending.d.ts +39 -0
  30. package/dist/pending.d.ts.map +1 -0
  31. package/dist/pending.js +94 -0
  32. package/dist/profiles.d.ts +49 -0
  33. package/dist/profiles.d.ts.map +1 -0
  34. package/dist/profiles.js +68 -0
  35. package/dist/providers/db.d.ts +9 -0
  36. package/dist/providers/db.d.ts.map +1 -0
  37. package/dist/providers/db.js +41 -0
  38. package/dist/providers/fs.d.ts +34 -0
  39. package/dist/providers/fs.d.ts.map +1 -0
  40. package/dist/providers/fs.js +125 -0
  41. package/dist/providers/s3.d.ts +25 -0
  42. package/dist/providers/s3.d.ts.map +1 -0
  43. package/dist/providers/s3.js +117 -0
  44. package/dist/s3.d.ts +3 -0
  45. package/dist/s3.d.ts.map +1 -0
  46. package/dist/s3.js +4 -0
  47. package/dist/serialize.d.ts +10 -0
  48. package/dist/serialize.d.ts.map +1 -0
  49. package/dist/serialize.js +22 -0
  50. package/dist/serveToken.d.ts +36 -0
  51. package/dist/serveToken.d.ts.map +1 -0
  52. package/dist/serveToken.js +45 -0
  53. package/dist/services.d.ts +70 -0
  54. package/dist/services.d.ts.map +1 -0
  55. package/dist/services.js +141 -0
  56. package/dist/storeFile.d.ts +27 -0
  57. package/dist/storeFile.d.ts.map +1 -0
  58. package/dist/storeFile.js +53 -0
  59. package/dist/targets.d.ts +14 -0
  60. package/dist/targets.d.ts.map +1 -0
  61. package/dist/targets.js +21 -0
  62. package/dist/types.d.ts +212 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +0 -0
  65. package/dist/uploadLoader.d.ts +11 -0
  66. package/dist/uploadLoader.d.ts.map +1 -0
  67. package/dist/uploadLoader.js +47 -0
  68. package/dist/uploadToken.d.ts +43 -0
  69. package/dist/uploadToken.d.ts.map +1 -0
  70. package/dist/uploadToken.js +61 -0
  71. package/dist/web/components/DbInput.d.ts +18 -0
  72. package/dist/web/components/DbInput.d.ts.map +1 -0
  73. package/dist/web/components/DbInput.js +46 -0
  74. package/dist/web/components/FsUploader.d.ts +6 -0
  75. package/dist/web/components/FsUploader.d.ts.map +1 -0
  76. package/dist/web/components/FsUploader.js +21 -0
  77. package/dist/web/components/S3Uploader.d.ts +6 -0
  78. package/dist/web/components/S3Uploader.d.ts.map +1 -0
  79. package/dist/web/components/S3Uploader.js +21 -0
  80. package/dist/web/components/UppyUploader.d.ts +23 -0
  81. package/dist/web/components/UppyUploader.d.ts.map +1 -0
  82. package/dist/web/components/UppyUploader.js +72 -0
  83. package/dist/web/createUppy.d.ts +62 -0
  84. package/dist/web/createUppy.d.ts.map +1 -0
  85. package/dist/web/createUppy.js +82 -0
  86. package/dist/web/graphql.d.ts +48 -0
  87. package/dist/web/graphql.d.ts.map +1 -0
  88. package/dist/web/graphql.js +36 -0
  89. package/dist/web/hooks/useDbUpload.d.ts +31 -0
  90. package/dist/web/hooks/useDbUpload.d.ts.map +1 -0
  91. package/dist/web/hooks/useDbUpload.js +80 -0
  92. package/dist/web/hooks/useFsUpload.d.ts +20 -0
  93. package/dist/web/hooks/useFsUpload.d.ts.map +1 -0
  94. package/dist/web/hooks/useFsUpload.js +58 -0
  95. package/dist/web/hooks/useS3Upload.d.ts +23 -0
  96. package/dist/web/hooks/useS3Upload.d.ts.map +1 -0
  97. package/dist/web/hooks/useS3Upload.js +99 -0
  98. package/dist/web/hooks/useUploadToken.d.ts +34 -0
  99. package/dist/web/hooks/useUploadToken.d.ts.map +1 -0
  100. package/dist/web/hooks/useUploadToken.js +60 -0
  101. package/dist/web/hooks/useUppyUpload.d.ts +23 -0
  102. package/dist/web/hooks/useUppyUpload.d.ts.map +1 -0
  103. package/dist/web/hooks/useUppyUpload.js +76 -0
  104. package/dist/web/index.d.ts +22 -0
  105. package/dist/web/index.d.ts.map +1 -0
  106. package/dist/web/index.js +36 -0
  107. package/dist/webhooks/s3.d.ts +52 -0
  108. package/dist/webhooks/s3.d.ts.map +1 -0
  109. package/dist/webhooks/s3.js +122 -0
  110. package/dist/webhooks/sns.d.ts +34 -0
  111. package/dist/webhooks/sns.d.ts.map +1 -0
  112. package/dist/webhooks/sns.js +131 -0
  113. package/package.json +147 -0
@@ -0,0 +1,18 @@
1
+ import type { InputHTMLAttributes } from 'react';
2
+ import type { DbUploadFile } from '../hooks/useDbUpload.js';
3
+ export interface DbInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'accept' | 'multiple' | 'onError'> {
4
+ allowedMimeTypes: string[];
5
+ maxFileSize: number;
6
+ maxFiles?: number;
7
+ /** Called with every selected file read as base64. */
8
+ onFilesReady?: (files: DbUploadFile[]) => void;
9
+ /** Called when a selected file fails validation or cannot be read. */
10
+ onReadError?: (error: Error) => void;
11
+ }
12
+ /**
13
+ * A file input for the DB upload path. Selected files are validated against
14
+ * the profile's constraints and read as base64, ready to send through a
15
+ * GraphQL mutation to `storeFile()`.
16
+ */
17
+ export declare function DbInput({ allowedMimeTypes, maxFileSize, maxFiles, onFilesReady, onReadError, disabled, ...inputProps }: DbInputProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=DbInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DbInput.d.ts","sourceRoot":"","sources":["../../../src/web/components/DbInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAG7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CACxD;IACC,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sDAAsD;IACtD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAC9C,sEAAsE;IACtE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACrC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EACtB,gBAAgB,EAChB,WAAW,EACX,QAAY,EACZ,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,GAAG,UAAU,EACd,EAAE,YAAY,+BAqCd"}
@@ -0,0 +1,46 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useDbUpload } from "../hooks/useDbUpload.js";
3
+ function DbInput({
4
+ allowedMimeTypes,
5
+ maxFileSize,
6
+ maxFiles = 1,
7
+ onFilesReady,
8
+ onReadError,
9
+ disabled,
10
+ ...inputProps
11
+ }) {
12
+ const { readFiles, isReading } = useDbUpload({
13
+ allowedMimeTypes,
14
+ maxFileSize,
15
+ maxFiles
16
+ });
17
+ const onChange = async (event) => {
18
+ const input = event.target;
19
+ const files = input.files;
20
+ if (!files || files.length === 0) {
21
+ return;
22
+ }
23
+ try {
24
+ const ready = await readFiles(files);
25
+ onFilesReady?.(ready);
26
+ } catch (e) {
27
+ onReadError?.(e instanceof Error ? e : new Error(String(e)));
28
+ } finally {
29
+ input.value = "";
30
+ }
31
+ };
32
+ return /* @__PURE__ */ jsx(
33
+ "input",
34
+ {
35
+ ...inputProps,
36
+ type: "file",
37
+ accept: allowedMimeTypes.join(","),
38
+ multiple: maxFiles > 1,
39
+ disabled: disabled || isReading,
40
+ onChange
41
+ }
42
+ );
43
+ }
44
+ export {
45
+ DbInput
46
+ };
@@ -0,0 +1,6 @@
1
+ import type { UseFsUploadOptions } from '../hooks/useFsUpload.js';
2
+ import type { UppyUploaderProps } from './UppyUploader.js';
3
+ export type FsUploaderProps = UseFsUploadOptions & Omit<UppyUploaderProps, 'uppy'>;
4
+ /** Through-the-api uploader for FS targets: `useFsUpload` wired to an Uppy UI. */
5
+ export declare function FsUploader({ profile, endpoint, onUploadComplete, onUploadError, ...uiProps }: FsUploaderProps): import("react").JSX.Element;
6
+ //# sourceMappingURL=FsUploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FsUploader.d.ts","sourceRoot":"","sources":["../../../src/web/components/FsUploader.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,GAC9C,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;AAEjC,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,GAAG,OAAO,EACX,EAAE,eAAe,+BASjB"}
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useFsUpload } from "../hooks/useFsUpload.js";
3
+ import { UppyUploader } from "./UppyUploader.js";
4
+ function FsUploader({
5
+ profile,
6
+ endpoint,
7
+ onUploadComplete,
8
+ onUploadError,
9
+ ...uiProps
10
+ }) {
11
+ const { uppy } = useFsUpload({
12
+ profile,
13
+ endpoint,
14
+ onUploadComplete,
15
+ onUploadError
16
+ });
17
+ return /* @__PURE__ */ jsx(UppyUploader, { uppy, ...uiProps });
18
+ }
19
+ export {
20
+ FsUploader
21
+ };
@@ -0,0 +1,6 @@
1
+ import type { UseS3UploadOptions } from '../hooks/useS3Upload.js';
2
+ import type { UppyUploaderProps } from './UppyUploader.js';
3
+ export type S3UploaderProps = UseS3UploadOptions & Omit<UppyUploaderProps, 'uppy'>;
4
+ /** Direct-to-S3 uploader: `useS3Upload` wired to an Uppy UI. */
5
+ export declare function S3Uploader({ profile, confirm, onUploadComplete, onUploadError, ...uiProps }: S3UploaderProps): import("react").JSX.Element;
6
+ //# sourceMappingURL=S3Uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"S3Uploader.d.ts","sourceRoot":"","sources":["../../../src/web/components/S3Uploader.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,GAC9C,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;AAEjC,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,aAAa,EACb,GAAG,OAAO,EACX,EAAE,eAAe,+BASjB"}
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useS3Upload } from "../hooks/useS3Upload.js";
3
+ import { UppyUploader } from "./UppyUploader.js";
4
+ function S3Uploader({
5
+ profile,
6
+ confirm,
7
+ onUploadComplete,
8
+ onUploadError,
9
+ ...uiProps
10
+ }) {
11
+ const { uppy } = useS3Upload({
12
+ profile,
13
+ confirm,
14
+ onUploadComplete,
15
+ onUploadError
16
+ });
17
+ return /* @__PURE__ */ jsx(UppyUploader, { uppy, ...uiProps });
18
+ }
19
+ export {
20
+ S3Uploader
21
+ };
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { CedarUppy } from '../createUppy.js';
3
+ export type UploaderVariant = 'dashboard' | 'drag-drop' | 'file-input';
4
+ export interface UppyUploaderProps {
5
+ uppy: CedarUppy | null;
6
+ /**
7
+ * `dashboard` mounts the full `@uppy/dashboard` UI. `drag-drop` renders
8
+ * Uppy's headless dropzone with a file list. `file-input` renders a plain
9
+ * upload button. Defaults to `dashboard`.
10
+ */
11
+ variant?: UploaderVariant;
12
+ /** Options passed to `@uppy/dashboard` for the `dashboard` variant. */
13
+ dashboardOptions?: Record<string, unknown>;
14
+ /** Text shown under the dropzone for the `drag-drop` variant. */
15
+ note?: string;
16
+ children?: ReactNode;
17
+ }
18
+ /**
19
+ * Renders an Uppy UI for an instance created by `useS3Upload` or
20
+ * `useFsUpload`. Renders nothing until the instance exists.
21
+ */
22
+ export declare function UppyUploader({ uppy, variant, dashboardOptions, note, children, }: UppyUploaderProps): import("react").JSX.Element | null;
23
+ //# sourceMappingURL=UppyUploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UppyUploader.d.ts","sourceRoot":"","sources":["../../../src/web/components/UppyUploader.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAStC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;AAEtE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AA4DD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,OAAqB,EACrB,gBAAgB,EAChB,IAAI,EACJ,QAAQ,GACT,EAAE,iBAAiB,sCAsBnB"}
@@ -0,0 +1,72 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import {
4
+ Dropzone,
5
+ FilesList,
6
+ UploadButton,
7
+ UppyContextProvider
8
+ } from "@uppy/react";
9
+ function DashboardMount({
10
+ uppy,
11
+ options
12
+ }) {
13
+ const ref = useRef(null);
14
+ useEffect(() => {
15
+ const target = ref.current;
16
+ if (!target) {
17
+ return;
18
+ }
19
+ let cancelled = false;
20
+ import("@uppy/dashboard").then(({ default: Dashboard }) => {
21
+ if (cancelled) {
22
+ return;
23
+ }
24
+ uppy.use(Dashboard, {
25
+ inline: true,
26
+ target,
27
+ proudlyDisplayPoweredByUppy: false,
28
+ ...options
29
+ });
30
+ }).catch((e) => {
31
+ if (cancelled) {
32
+ return;
33
+ }
34
+ uppy.emit(
35
+ "error",
36
+ e instanceof Error ? e : new Error("Could not load @uppy/dashboard")
37
+ );
38
+ });
39
+ return () => {
40
+ cancelled = true;
41
+ const plugin = uppy.getPlugin("Dashboard");
42
+ if (plugin) {
43
+ uppy.removePlugin(plugin);
44
+ }
45
+ };
46
+ }, [uppy]);
47
+ return /* @__PURE__ */ jsx("div", { ref });
48
+ }
49
+ function UppyUploader({
50
+ uppy,
51
+ variant = "dashboard",
52
+ dashboardOptions,
53
+ note,
54
+ children
55
+ }) {
56
+ if (!uppy) {
57
+ return null;
58
+ }
59
+ if (variant === "dashboard") {
60
+ return /* @__PURE__ */ jsx(DashboardMount, { uppy, options: dashboardOptions });
61
+ }
62
+ return /* @__PURE__ */ jsxs(UppyContextProvider, { uppy, children: [
63
+ variant === "drag-drop" ? /* @__PURE__ */ jsxs(Fragment, { children: [
64
+ /* @__PURE__ */ jsx(Dropzone, { note }),
65
+ /* @__PURE__ */ jsx(FilesList, {})
66
+ ] }) : /* @__PURE__ */ jsx(UploadButton, {}),
67
+ children
68
+ ] });
69
+ }
70
+ export {
71
+ UppyUploader
72
+ };
@@ -0,0 +1,62 @@
1
+ import Uppy from '@uppy/core';
2
+ import type { Meta, UppyFile } from '@uppy/core';
3
+ import type { UploadConstraints } from './graphql.js';
4
+ /** The Uppy instance type every Cedar hook and component works with. */
5
+ export type CedarUppy = Uppy;
6
+ /** A file inside a `CedarUppy` instance. */
7
+ export type CedarUppyFile = UppyFile<Meta, Record<string, never>>;
8
+ export { UPLOAD_TOKEN_HEADER } from '../constants.js';
9
+ /** Route the FS uploader posts to, relative to the api origin. */
10
+ export declare const DEFAULT_FS_UPLOAD_PATH = "/upload/fs";
11
+ export interface BaseUppyOptions {
12
+ /** Constraints from the upload token, applied as Uppy restrictions. */
13
+ constraints?: UploadConstraints | null;
14
+ /** Start uploading as soon as files are added. Defaults to `true`. */
15
+ autoProceed?: boolean;
16
+ debug?: boolean;
17
+ }
18
+ /**
19
+ * One file's `{ url, method, headers }` from the api, plus the id of the
20
+ * pending `Upload` row it belongs to.
21
+ */
22
+ export interface PresignedUploadParameters {
23
+ uploadId: string;
24
+ url: string;
25
+ method: string;
26
+ headers: Record<string, string>;
27
+ }
28
+ export interface S3UppyOptions extends BaseUppyOptions {
29
+ provider: 's3';
30
+ /**
31
+ * Asks the api for a presigned URL for one file. Called once per file
32
+ * with the file's name, type, and size.
33
+ */
34
+ getUploadParameters: (file: CedarUppyFile) => Promise<PresignedUploadParameters>;
35
+ }
36
+ export interface FsUppyOptions extends BaseUppyOptions {
37
+ provider: 'fs';
38
+ /** Full URL of the api's `POST {prefix}/fs` route, or a function returning it. */
39
+ endpoint: string | (() => string);
40
+ /** The upload token to send. Called per request so a refreshed token is used. */
41
+ getUploadToken: () => string | null;
42
+ }
43
+ export type CreateUppyOptions = S3UppyOptions | FsUppyOptions;
44
+ /** Response body of the api's `POST {prefix}/fs` route. */
45
+ export interface FsUploadResponseBody {
46
+ uploads: {
47
+ id: string;
48
+ status: string;
49
+ filename: string;
50
+ }[];
51
+ }
52
+ /**
53
+ * Creates an Uppy instance pre-configured for one of Cedar's upload flows.
54
+ * Direct-to-S3 uploads use `@uppy/aws-s3` with the presigned URLs the api
55
+ * hands out; FS uploads use `@uppy/xhr-upload` against the api's upload
56
+ * route. The matching plugin is loaded on demand, so only the one in use
57
+ * needs to be installed.
58
+ */
59
+ export declare function createUppy(options: CreateUppyOptions): Promise<CedarUppy>;
60
+ /** Reads the `Upload` id Cedar recorded on a file after it uploaded. */
61
+ export declare function getCedarUploadId(file: CedarUppyFile): string | null;
62
+ //# sourceMappingURL=createUppy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createUppy.d.ts","sourceRoot":"","sources":["../../src/web/createUppy.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,YAAY,CAAA;AAC7B,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAIhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,IAAI,CAAA;AAE5B,4CAA4C;AAC5C,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,eAAe,CAAA;AAElD,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAA;IACtC,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,EAAE,IAAI,CAAA;IACd;;;OAGG;IACH,mBAAmB,EAAE,CACnB,IAAI,EAAE,aAAa,KAChB,OAAO,CAAC,yBAAyB,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,EAAE,IAAI,CAAA;IACd,kFAAkF;IAClF,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;IACjC,iFAAiF;IACjF,cAAc,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,aAAa,CAAA;AAE7D,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC5D;AAcD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,CAAC,CA2EpB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAInE"}
@@ -0,0 +1,82 @@
1
+ import Uppy from "@uppy/core";
2
+ import { UPLOAD_TOKEN_HEADER } from "../constants.js";
3
+ import { UPLOAD_TOKEN_HEADER as UPLOAD_TOKEN_HEADER2 } from "../constants.js";
4
+ const DEFAULT_FS_UPLOAD_PATH = "/upload/fs";
5
+ function restrictionsFor(constraints) {
6
+ if (!constraints) {
7
+ return {};
8
+ }
9
+ return {
10
+ allowedFileTypes: constraints.allowedMimeTypes,
11
+ maxFileSize: constraints.maxFileSize,
12
+ maxNumberOfFiles: constraints.maxFiles
13
+ };
14
+ }
15
+ async function createUppy(options) {
16
+ const { constraints, autoProceed = true, debug = false } = options;
17
+ const uppy = new Uppy({
18
+ autoProceed,
19
+ debug,
20
+ restrictions: restrictionsFor(constraints)
21
+ });
22
+ if (options.provider === "s3") {
23
+ const { default: AwsS3 } = await import("@uppy/aws-s3");
24
+ uppy.use(AwsS3, {
25
+ shouldUseMultipart: false,
26
+ // The S3 plugin signs by object key, not by file, so the key is set to
27
+ // the Uppy file id and the file looked up from it inside `signRequest`.
28
+ // The server chooses the real object key.
29
+ generateObjectKey: (file) => file.id,
30
+ signRequest: async (request) => {
31
+ if (request.method !== "PUT" || "uploadId" in request) {
32
+ throw new Error(
33
+ `Cedar presigned uploads only support single PUT requests, got ${request.method}.`
34
+ );
35
+ }
36
+ const file = uppy.getFile(request.key);
37
+ if (!file) {
38
+ throw new Error(`Unknown file '${request.key}'.`);
39
+ }
40
+ const params = await options.getUploadParameters(file);
41
+ uppy.setFileMeta(file.id, { cedarUploadId: params.uploadId });
42
+ return { url: params.url };
43
+ }
44
+ });
45
+ return uppy;
46
+ }
47
+ const { default: XHRUpload } = await import("@uppy/xhr-upload");
48
+ const { endpoint } = options;
49
+ uppy.use(XHRUpload, {
50
+ endpoint: typeof endpoint === "function" ? () => endpoint() : endpoint,
51
+ method: "POST",
52
+ formData: true,
53
+ fieldName: "file",
54
+ bundle: false,
55
+ headers: () => {
56
+ const headers = {};
57
+ const token = options.getUploadToken();
58
+ if (token) {
59
+ headers[UPLOAD_TOKEN_HEADER] = token;
60
+ }
61
+ return headers;
62
+ }
63
+ });
64
+ uppy.on("upload-success", (file, response) => {
65
+ const body = response.body;
66
+ const id = body?.uploads?.[0]?.id;
67
+ if (file && id) {
68
+ uppy.setFileMeta(file.id, { cedarUploadId: id });
69
+ }
70
+ });
71
+ return uppy;
72
+ }
73
+ function getCedarUploadId(file) {
74
+ const id = file.meta.cedarUploadId;
75
+ return typeof id === "string" ? id : null;
76
+ }
77
+ export {
78
+ DEFAULT_FS_UPLOAD_PATH,
79
+ UPLOAD_TOKEN_HEADER2 as UPLOAD_TOKEN_HEADER,
80
+ createUppy,
81
+ getCedarUploadId
82
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Constraints echoed with an upload token. The authoritative copy is inside
3
+ * the signed token; these are for client-side UX only.
4
+ */
5
+ export interface UploadConstraints {
6
+ allowedMimeTypes: string[];
7
+ maxFileSize: number;
8
+ maxFiles: number;
9
+ }
10
+ export interface RequestUploadTokenData {
11
+ requestUploadToken: {
12
+ token: string;
13
+ allowedMimeTypes: string[];
14
+ maxFileSize: string | number;
15
+ maxFiles: number;
16
+ };
17
+ }
18
+ export interface RequestUploadTokenVariables {
19
+ profile: string;
20
+ }
21
+ export declare const REQUEST_UPLOAD_TOKEN: import("@apollo/client").DocumentNode;
22
+ export interface CreatePresignedUploadUrlData {
23
+ createPresignedUploadUrl: {
24
+ uploadId: string;
25
+ url: string;
26
+ method: string;
27
+ headers: Record<string, string>;
28
+ };
29
+ }
30
+ export interface CreatePresignedUploadUrlVariables {
31
+ input: {
32
+ filename: string;
33
+ contentType: string;
34
+ size: string;
35
+ };
36
+ }
37
+ export declare const CREATE_PRESIGNED_UPLOAD_URL: import("@apollo/client").DocumentNode;
38
+ export interface ConfirmUploadData {
39
+ confirmUpload: {
40
+ id: string;
41
+ status: string;
42
+ };
43
+ }
44
+ export interface ConfirmUploadVariables {
45
+ uploadId: string;
46
+ }
47
+ export declare const CONFIRM_UPLOAD: import("@apollo/client").DocumentNode;
48
+ //# sourceMappingURL=graphql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/web/graphql.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE;QAClB,KAAK,EAAE,MAAM,CAAA;QACb,gBAAgB,EAAE,MAAM,EAAE,CAAA;QAC1B,WAAW,EAAE,MAAM,GAAG,MAAM,CAAA;QAC5B,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,oBAAoB,uCAShC,CAAA;AAED,MAAM,WAAW,4BAA4B;IAC3C,wBAAwB,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAA;QAChB,GAAG,EAAE,MAAM,CAAA;QACX,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAChC,CAAA;CACF;AAED,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;CACF;AAED,eAAO,MAAM,2BAA2B,uCAWvC,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE;QACb,EAAE,EAAE,MAAM,CAAA;QACV,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,cAAc,uCAO1B,CAAA"}
@@ -0,0 +1,36 @@
1
+ import { gql } from "@apollo/client";
2
+ const REQUEST_UPLOAD_TOKEN = gql`
3
+ query CedarRequestUploadToken($profile: String!) {
4
+ requestUploadToken(profile: $profile) {
5
+ token
6
+ allowedMimeTypes
7
+ maxFileSize
8
+ maxFiles
9
+ }
10
+ }
11
+ `;
12
+ const CREATE_PRESIGNED_UPLOAD_URL = gql`
13
+ mutation CedarCreatePresignedUploadUrl(
14
+ $input: CreatePresignedUploadUrlInput!
15
+ ) {
16
+ createPresignedUploadUrl(input: $input) {
17
+ uploadId
18
+ url
19
+ method
20
+ headers
21
+ }
22
+ }
23
+ `;
24
+ const CONFIRM_UPLOAD = gql`
25
+ mutation CedarConfirmUpload($uploadId: String!) {
26
+ confirmUpload(uploadId: $uploadId) {
27
+ id
28
+ status
29
+ }
30
+ }
31
+ `;
32
+ export {
33
+ CONFIRM_UPLOAD,
34
+ CREATE_PRESIGNED_UPLOAD_URL,
35
+ REQUEST_UPLOAD_TOKEN
36
+ };
@@ -0,0 +1,31 @@
1
+ export interface DbUploadFile {
2
+ filename: string;
3
+ mimeType: string;
4
+ /** Base64-encoded bytes. */
5
+ data: string;
6
+ size: number;
7
+ }
8
+ export interface UseDbUploadOptions {
9
+ allowedMimeTypes: string[];
10
+ maxFileSize: number;
11
+ maxFiles?: number;
12
+ onFileReady?: (file: DbUploadFile) => void;
13
+ }
14
+ export interface UseDbUploadResult {
15
+ /** Reads one `File` as base64, validating type and size first. */
16
+ readFile: (file: File) => Promise<DbUploadFile>;
17
+ /** Reads every file of a `FileList`, validating the count first. */
18
+ readFiles: (files: FileList | File[]) => Promise<DbUploadFile[]>;
19
+ progress: {
20
+ completed: number;
21
+ total: number;
22
+ };
23
+ isReading: boolean;
24
+ }
25
+ /**
26
+ * Reads small files as base64 for the DB upload path, which sends bytes
27
+ * through a GraphQL mutation to `storeFile()`. No Uppy involved. The checks
28
+ * here are UX only; the service enforces the real limits.
29
+ */
30
+ export declare function useDbUpload({ allowedMimeTypes, maxFileSize, maxFiles, onFileReady, }: UseDbUploadOptions): UseDbUploadResult;
31
+ //# sourceMappingURL=useDbUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDbUpload.d.ts","sourceRoot":"","sources":["../../../src/web/hooks/useDbUpload.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/C,oEAAoE;IACpE,SAAS,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAChE,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,SAAS,EAAE,OAAO,CAAA;CACnB;AAkCD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,gBAAgB,EAChB,WAAW,EACX,QAAQ,EACR,WAAW,GACZ,EAAE,kBAAkB,GAAG,iBAAiB,CA0DxC"}
@@ -0,0 +1,80 @@
1
+ import { useCallback, useState } from "react";
2
+ function mimeTypeAllowed(allowed, mimeType) {
3
+ const normalized = mimeType.toLowerCase();
4
+ return allowed.some((entry) => {
5
+ const pattern = entry.toLowerCase();
6
+ if (pattern === "*/*") {
7
+ return true;
8
+ }
9
+ if (pattern.endsWith("/*")) {
10
+ return normalized.startsWith(pattern.slice(0, -1));
11
+ }
12
+ return pattern === normalized;
13
+ });
14
+ }
15
+ function readAsBase64(file) {
16
+ return new Promise((resolve, reject) => {
17
+ const reader = new FileReader();
18
+ reader.onerror = () => reject(reader.error ?? new Error("Read failed"));
19
+ reader.onload = () => {
20
+ const result = String(reader.result);
21
+ resolve(result.slice(result.indexOf(",") + 1));
22
+ };
23
+ reader.readAsDataURL(file);
24
+ });
25
+ }
26
+ function useDbUpload({
27
+ allowedMimeTypes,
28
+ maxFileSize,
29
+ maxFiles,
30
+ onFileReady
31
+ }) {
32
+ const [progress, setProgress] = useState({ completed: 0, total: 0 });
33
+ const [isReading, setIsReading] = useState(false);
34
+ const readFile = useCallback(
35
+ async (file) => {
36
+ if (!mimeTypeAllowed(allowedMimeTypes, file.type)) {
37
+ throw new Error(`File type '${file.type}' is not allowed.`);
38
+ }
39
+ if (file.size > maxFileSize) {
40
+ throw new Error(
41
+ `File is ${file.size} bytes, which exceeds the ${maxFileSize} byte limit.`
42
+ );
43
+ }
44
+ const result = {
45
+ filename: file.name,
46
+ mimeType: file.type,
47
+ data: await readAsBase64(file),
48
+ size: file.size
49
+ };
50
+ onFileReady?.(result);
51
+ return result;
52
+ },
53
+ [allowedMimeTypes, maxFileSize, onFileReady]
54
+ );
55
+ const readFiles = useCallback(
56
+ async (files) => {
57
+ const list = Array.from(files);
58
+ if (maxFiles !== void 0 && list.length > maxFiles) {
59
+ throw new Error(`At most ${maxFiles} file(s) can be selected.`);
60
+ }
61
+ setIsReading(true);
62
+ setProgress({ completed: 0, total: list.length });
63
+ try {
64
+ const results = [];
65
+ for (const file of list) {
66
+ results.push(await readFile(file));
67
+ setProgress({ completed: results.length, total: list.length });
68
+ }
69
+ return results;
70
+ } finally {
71
+ setIsReading(false);
72
+ }
73
+ },
74
+ [maxFiles, readFile]
75
+ );
76
+ return { readFile, readFiles, progress, isReading };
77
+ }
78
+ export {
79
+ useDbUpload
80
+ };
@@ -0,0 +1,20 @@
1
+ import type { UppyUploadCallbacks, UppyUploadResult } from './useUppyUpload.js';
2
+ export interface UseFsUploadOptions extends UppyUploadCallbacks {
3
+ /** Name of a server-defined upload profile. */
4
+ profile: string;
5
+ /**
6
+ * Full URL of the api's upload route. Defaults to the api URL from
7
+ * `RWJS_API_URL` plus `/upload/fs`. Read on every request, so it may
8
+ * change after mount.
9
+ */
10
+ endpoint?: string;
11
+ }
12
+ export type UseFsUploadResult = UppyUploadResult & {
13
+ requestToken: () => Promise<string>;
14
+ };
15
+ /**
16
+ * Uploads through the api server to an FS target: fetches an upload token
17
+ * and posts each file to `POST {prefix}/fs` with `@uppy/xhr-upload`.
18
+ */
19
+ export declare function useFsUpload({ profile, endpoint, onUploadComplete, onUploadError, }: UseFsUploadOptions): UseFsUploadResult;
20
+ //# sourceMappingURL=useFsUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFsUpload.d.ts","sourceRoot":"","sources":["../../../src/web/hooks/useFsUpload.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE/E,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG;IACjD,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACpC,CAAA;AAWD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,aAAa,GACd,EAAE,kBAAkB,GAAG,iBAAiB,CAyDxC"}