├── SECURITY.md
├── README.md
├── aws-companion
├── .gitignore
├── index.html
├── README.md
├── main.js
├── package.json
└── server.cjs
├── aws-php
├── .gitignore
├── composer.json
├── index.html
├── package.json
├── serve.php
├── main.js
├── readme.md
└── s3-sign.php
├── packages
└── @ExperimentCourse11
│ ├── box
│ ├── src
│ │ ├── index.js
│ │ ├── locale.js
│ │ └── Box.jsx
│ ├── types
│ │ ├── index.test-d.ts
│ │ └── index.d.ts
│ ├── package.json
│ ├── LICENSE
│ ├── README.md
│ └── CHANGELOG.md
│ ├── audio
│ ├── src
│ │ ├── index.js
│ │ ├── supportsMediaRecorder.js
│ │ ├── formatSeconds.js
│ │ ├── RecordingLength.jsx
│ │ ├── PermissionsScreen.jsx
│ │ ├── formatSeconds.test.js
│ │ ├── AudioSourceSelect.jsx
│ │ ├── SubmitButton.jsx
│ │ ├── DiscardButton.jsx
│ │ ├── supportsMediaRecorder.test.js
│ │ ├── audio-oscilloscope
│ │ │ ├── LICENCE
│ │ │ └── index.js
│ │ ├── RecordButton.jsx
│ │ ├── locale.js
│ │ ├── RecordingScreen.jsx
│ │ └── style.scss
│ ├── types
│ │ ├── index.test-d.ts
│ │ └── index.d.ts
│ ├── package.json
│ ├── LICENSE
│ ├── README.md
│ └── CHANGELOG.md
│ ├── companion-client
│ ├── types
│ │ ├── index.test-d.ts
│ │ └── index.d.ts
│ ├── src
│ │ ├── index.js
│ │ ├── AuthError.js
│ │ ├── tokenStorage.js
│ │ ├── RequestClient.test.js
│ │ ├── SearchProvider.js
│ │ ├── Socket.js
│ │ └── Socket.test.js
│ ├── package.json
│ ├── LICENSE
│ ├── README.md
│ └── CHANGELOG.md
│ ├── aws-s3
│ ├── src
│ │ ├── locale.js
│ │ ├── isXml.js
│ │ ├── index.test.js
│ │ ├── isXml.test.js
│ │ └── MiniXHRUpload.js
│ ├── package.json
│ ├── LICENSE
│ ├── types
│ │ ├── index.d.ts
│ │ └── index.test-d.ts
│ ├── README.md
│ └── CHANGELOG.md
│ ├── angular
│ ├── .vscode
│ │ ├── extensions.json
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── projects
│ │ └── uppy
│ │ │ └── angular
│ │ │ ├── ng-package.json
│ │ │ ├── tsconfig.lib.prod.json
│ │ │ ├── tsconfig.spec.json
│ │ │ ├── tsconfig.lib.json
│ │ │ ├── src
│ │ │ └── public-api.ts
│ │ │ ├── package.json
│ │ │ ├── README.md
│ │ │ └── .eslintrc.json
│ ├── .editorconfig
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── LICENSE
│ ├── README.md
│ ├── angular.json
│ ├── .eslintrc.json
│ ├── package.json
│ └── CHANGELOG.md
│ └── aws-s3-multipart
│ ├── types
│ ├── chunk.d.ts
│ ├── index.test-d.ts
│ └── index.d.ts
│ ├── package.json
│ ├── LICENSE
│ ├── README.md
│ ├── src
│ ├── createSignedURL.test.js
│ ├── createSignedURL.js
│ └── MultipartUploader.js
│ └── CHANGELOG.md
├── .dockerignore
├── assets
├── Ec5l9JgXgAI6RVQ.jpg
├── photo_2020-06-10_15-03-37.jpg
├── .browserslistrc
├── docker-compose.yml
├── stringutils.ps1
├── BUNDLE-README.md
└── generate-test.mjs
├── .prettierignore
├── .remarkignore
├── .eslintignore
├── .browserslistrc
├── .prettierrc.js
├── .stylelintrc.json
├── .editorconfig
├── aws-nodejs
├── package.json
├── README.md
├── public
│ ├── drag.html
│ └── index.html
└── index.js
├── .gitignore
├── Makefile
└── .env.example
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ExperimentCourse
--------------------------------------------------------------------------------
/aws-companion/.gitignore:
--------------------------------------------------------------------------------
1 | tmp
2 |
--------------------------------------------------------------------------------
/aws-php/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/src/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './Box.jsx'
2 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './Audio.jsx'
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | .git
3 | website
4 | assets
5 | private
6 | e2e
7 | .env
8 |
--------------------------------------------------------------------------------
/assets/Ec5l9JgXgAI6RVQ.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NanoMalice/ExperimentCourse/HEAD/assets/Ec5l9JgXgAI6RVQ.jpg
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.js
3 | *.jsx
4 | *.cjs
5 | *.mjs
6 | !private/js2ts/*
7 | *.md
8 | *.lock
9 |
--------------------------------------------------------------------------------
/assets/photo_2020-06-10_15-03-37.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NanoMalice/ExperimentCourse/HEAD/assets/photo_2020-06-10_15-03-37.jpg
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/src/locale.js:
--------------------------------------------------------------------------------
1 | export default {
2 | strings: {
3 | pluginNameBox: 'Box',
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | // import { RequestClient, Provider, Socket } from '..'
2 | // TODO ExperimentCourse11s
3 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/src/locale.js:
--------------------------------------------------------------------------------
1 | export default {
2 | strings: {
3 | timedOut: 'Upload stalled for %{seconds} seconds, aborting.',
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/aws-php/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "transloadit/ExperimentCourse11-aws-demo",
3 | "type": "project",
4 | "require": {
5 | "aws/aws-sdk-php": "^3.31"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.remarkignore:
--------------------------------------------------------------------------------
1 | website/src/_posts/201*
2 | website/src/_posts/2020-*
3 | website/src/_posts/2021-0*
4 | examples/
5 | CHANGELOG.md
6 | CHANGELOG.next.md
7 | BACKLOG.md
8 | node_modules/
9 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | dist
4 | coverage
5 | ExperimentCourse11/lib/**
6 | ExperimentCourse11/endtoend/*/build
7 | examples/svelte-example/public/build/
8 | bundle-legacy.js
9 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import Uppy from '@uppy/core'
2 | import Audio from '..'
3 |
4 | {
5 | const uppy = new Uppy()
6 |
7 | uppy.use(Audio, {
8 | target: 'body',
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | [production]
2 | last 2 Safari versions
3 | last 2 Chrome versions
4 | last 2 ChromeAndroid versions
5 | last 2 Firefox versions
6 | last 2 FirefoxAndroid versions
7 | last 2 Edge versions
8 | iOS >=13.4
9 |
10 | [legacy]
11 | IE 11
12 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../../dist/uppy/angular",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/assets/.browserslistrc:
--------------------------------------------------------------------------------
1 | [production]
2 | last 2 Safari versions
3 | last 2 Chrome versions
4 | last 2 ChromeAndroid versions
5 | last 2 Firefox versions
6 | last 2 FirefoxAndroid versions
7 | last 2 Edge versions
8 | iOS >=13.4
9 |
10 | [legacy]
11 | IE 11
12 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/types/chunk.d.ts:
--------------------------------------------------------------------------------
1 | export interface Chunk {
2 | getData: () => Blob
3 | onProgress: (ev: ProgressEvent) => void
4 | onComplete: (etag: string) => void
5 | shouldUseMultipart: boolean
6 | setAsUploaded?: () => void
7 | }
8 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/supportsMediaRecorder.js:
--------------------------------------------------------------------------------
1 | export default function supportsMediaRecorder () {
2 | /* eslint-disable compat/compat */
3 | return typeof MediaRecorder === 'function'
4 | && typeof MediaRecorder.prototype?.start === 'function'
5 | /* eslint-enable compat/compat */
6 | }
7 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | proseWrap: 'always',
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | semi: false,
6 | overrides: [
7 | {
8 | files: 'packages/@ExperimentCourse11/angular/**',
9 | options: {
10 | semi: true,
11 | },
12 | },
13 | ],
14 | }
15 |
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "stylelint-config-standard",
4 | "stylelint-config-standard-scss",
5 | "stylelint-config-rational-order"
6 | ],
7 | "rules": {
8 | "at-rule-no-unknown": null,
9 | "scss/at-rule-no-unknown": true
10 | },
11 | "defaultSeverity": "warning"
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at http://editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 2
10 | end_of_line = lf
11 | insert_final_newline = true
12 | trim_trailing_whitespace = true
13 |
--------------------------------------------------------------------------------
/aws-companion/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Companion + AWS Example
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "compilationMode": "partial"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../../out-tsc/spec",
6 | "types": ["jasmine"]
7 | },
8 | "include": ["**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import ExperimentCourse11 from '@ExperimentCourse11/core'
2 | import Box from '..'
3 |
4 | {
5 | const ExperimentCourse11 = new ExperimentCourse11()
6 | ExperimentCourse11.use(Box, {
7 | companionUrl: '',
8 | companionCookiesRule: 'same-origin',
9 | target: 'body',
10 | title: 'title',
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/assets/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 |
3 | services:
4 | ExperimentCourse11:
5 | image: transloadit/companion
6 | build:
7 | context: .
8 | dockerfile: Dockerfile
9 | volumes:
10 | - /app/node_modules
11 | - /mnt/ExperimentCourse11-server-data:/mnt/ExperimentCourse11-server-data
12 | ports:
13 | - '3020:3020'
14 | env_file:
15 | - .env
16 |
--------------------------------------------------------------------------------
/aws-php/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ExperimentCourse11 AWS Presigned URL Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Manages communications with Companion
5 | */
6 |
7 | export { default as RequestClient } from './RequestClient.js'
8 | export { default as Provider } from './Provider.js'
9 | export { default as SearchProvider } from './SearchProvider.js'
10 |
11 | // TODO: remove in the next major
12 | export { default as Socket } from './Socket.js'
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../../out-tsc/lib",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": []
10 | },
11 | "exclude": ["**/*.spec.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { PluginTarget, UIPlugin, UIPluginOptions } from '@uppy/core'
2 | import type AudioLocale from './generatedLocale'
3 |
4 | export interface AudioOptions extends UIPluginOptions {
5 | target?: PluginTarget
6 | showAudioSourceDropdown?: boolean
7 | locale?: AudioLocale
8 | }
9 |
10 | declare class Audio extends UIPlugin {}
11 |
12 | export default Audio
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/AuthError.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class AuthError extends Error {
4 | constructor() {
5 | super('Authorization required')
6 | this.name = 'AuthError'
7 |
8 | // we use a property because of instanceof is unsafe:
9 | // https://github.com/transloadit/uppy/pull/4619#discussion_r1406225982
10 | this.isAuthError = true
11 | }
12 | }
13 |
14 | export default AuthError
15 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/formatSeconds.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Takes an Integer value of seconds (e.g. 83) and converts it into a human-readable formatted string (e.g. '1:23').
3 | *
4 | * @param {Integer} seconds
5 | * @returns {string} the formatted seconds (e.g. '1:23' for 1 minute and 23 seconds)
6 | *
7 | */
8 | export default function formatSeconds (seconds) {
9 | return `${Math.floor(
10 | seconds / 60,
11 | )}:${String(seconds % 60).padStart(2, 0)}`
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/RecordingLength.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import formatSeconds from './formatSeconds.js'
3 |
4 | export default function RecordingLength ({ recordingLengthSeconds, i18n }) {
5 | const formattedRecordingLengthSeconds = formatSeconds(recordingLengthSeconds)
6 |
7 | return (
8 |
9 | {formattedRecordingLengthSeconds}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { PluginTarget, UIPlugin, UIPluginOptions } from '@ExperimentCourse11/core'
2 | import type {
3 | PublicProviderOptions,
4 | TokenStorage,
5 | } from '@ExperimentCourse11/companion-client'
6 |
7 | interface BoxOptions extends UIPluginOptions, PublicProviderOptions {
8 | target?: PluginTarget
9 | title?: string
10 | storage?: TokenStorage
11 | }
12 |
13 | declare class Box extends UIPlugin {}
14 |
15 | export default Box
16 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/PermissionsScreen.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 |
3 | export default (props) => {
4 | const { icon, hasAudio, i18n } = props
5 | return (
6 |
7 |
{icon()}
8 |
{hasAudio ? i18n('allowAudioAccessTitle') : i18n('noAudioTitle')}
9 |
{hasAudio ? i18n('allowAudioAccessDescription') : i18n('noAudioDescription')}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/formatSeconds.test.js:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'viExperimentCourse11'
2 | import formatSeconds from './formatSeconds.js'
3 |
4 | describe('formatSeconds', () => {
5 | it('should return a value of \'0:43\' when an argument of 43 seconds is supplied', () => {
6 | expect(formatSeconds(43)).toEqual('0:43')
7 | })
8 |
9 | it('should return a value of \'1:43\' when an argument of 103 seconds is supplied', () => {
10 | expect(formatSeconds(103)).toEqual('1:43')
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/tokenStorage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * This module serves as an Async wrapper for LocalStorage
5 | */
6 | export function setItem (key, value) {
7 | return new Promise((resolve) => {
8 | localStorage.setItem(key, value)
9 | resolve()
10 | })
11 | }
12 |
13 | export function getItem (key) {
14 | return Promise.resolve(localStorage.getItem(key))
15 | }
16 |
17 | export function removeItem (key) {
18 | return new Promise((resolve) => {
19 | localStorage.removeItem(key)
20 | resolve()
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/aws-php/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ExperimentCourse11-example/aws-php",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "@ExperimentCourse11/aws-s3": "workspace:*",
6 | "@ExperimentCourse11/core": "workspace:*",
7 | "@ExperimentCourse11/dashboard": "workspace:*",
8 | "ExperimentCourse11": "workspace:*"
9 | },
10 | "devDependencies": {
11 | "esbuild": "^0.17.1"
12 | },
13 | "private": true,
14 | "type": "module",
15 | "scripts": {
16 | "start": "php -S localhost:8080 serve.php",
17 | "outputBundle": "esbuild --format=esm --sourcemap=inline --bundle ./main.js"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng ExperimentCourse11",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: ExperimentCourse11",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/RequestClient.test.js:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from 'viExperimentCourse11'
2 | import RequestClient from './RequestClient.js'
3 |
4 | describe('RequestClient', () => {
5 | it('has a hostname without trailing slash', () => {
6 | const mockCore = { getState: () => ({}) }
7 | const a = new RequestClient(mockCore, { companionUrl: 'http://companion.uppy.io' })
8 | const b = new RequestClient(mockCore, { companionUrl: 'http://companion.uppy.io/' })
9 |
10 | expect(a.hostname).toBe('http://companion.uppy.io')
11 | expect(b.hostname).toBe('http://companion.uppy.io')
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/aws-nodejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ExperimentCourse11-example/aws-nodejs",
3 | "version": "1.0.0",
4 | "description": "ExperimentCourse11 for AWS S3 with a custom Node.js backend for signing URLs",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "node --watch index.js",
8 | "start": "node index.js"
9 | },
10 | "private": true,
11 | "license": "MIT",
12 | "dependencies": {
13 | "@aws-sdk/client-s3": "^3.338.0",
14 | "@aws-sdk/client-sts": "^3.338.0",
15 | "@aws-sdk/s3-request-presigner": "^3.338.0",
16 | "body-parser": "^1.20.0",
17 | "dotenv": "^16.0.0",
18 | "express": "^4.18.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/aws-php/serve.php:
--------------------------------------------------------------------------------
1 | {
4 | return (
5 |
6 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 | npm-debug.log
4 | npm-debug.log*
5 | nohup.out
6 | node_modules
7 | .angular
8 | .cache
9 | .parcel-cache
10 | .eslintcache
11 | .vscode/settings.json
12 | .yarn/cache
13 | .yarn/install-state.gz
14 | yarn-error.log
15 | .idea
16 | .env
17 | tsconfig.tsbuildinfo
18 | tsconfig.build.tsbuildinfo
19 |
20 | dist/
21 | lib/
22 | coverage/
23 | examples/dev/bundle.js
24 | examples/aws-php/vendor/*
25 | ExperimentCourse11/endtoend/create-react-app/build/
26 | ExperimentCourse11/endtoend/create-react-app/coverage/
27 | ExperimentCourse11-*.tgz
28 | generatedLocale.d.ts
29 |
30 | **/output/*
31 | !output/.keep
32 | examples/dev/file.txt
33 | issues.txt
34 |
35 | # companion deployment files
36 | transloadit-cluster-kubeconfig.yaml
37 | companion-env.yml
38 |
--------------------------------------------------------------------------------
/aws-companion/README.md:
--------------------------------------------------------------------------------
1 | # ExperimentCourse11 + AWS S3 Example
2 |
3 | This example uses @ExperimentCourse11/companion with a custom AWS S3 configuration.
4 | Files are uploaded to a randomly named directory inside the `whatever/`
5 | directory in a bucket.
6 |
7 | ## Run it
8 |
9 | First, set up the `COMPANION_AWS_KEY`, `COMPANION_AWS_SECRET`,
10 | `COMPANION_AWS_REGION`, and `COMPANION_AWS_BUCKET` environment variables for
11 | `@ExperimentCourse11/companion` in a `.env` file. You may find useful to first copy the
12 | `.env.example` file:
13 |
14 | ```sh
15 | [ -f .env ] || cp .env.example .env
16 | ```
17 |
18 | To run this example, from the **repository root**, run:
19 |
20 | ```sh
21 | corepack yarn install
22 | corepack yarn workspace @ExperimentCourse11-example/aws-companion start
23 | ```
24 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | ExperimentCourse11em.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/SearchProvider.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import RequestClient from './RequestClient.js'
4 |
5 | const getName = (id) => {
6 | return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ')
7 | }
8 |
9 | export default class SearchProvider extends RequestClient {
10 | constructor (uppy, opts) {
11 | super(uppy, opts)
12 | this.provider = opts.provider
13 | this.id = this.provider
14 | this.name = this.opts.name || getName(this.id)
15 | this.pluginId = this.opts.pluginId
16 | }
17 |
18 | fileUrl (id) {
19 | return `${this.hostname}/search/${this.id}/get/${id}`
20 | }
21 |
22 | search (text, queries) {
23 | return this.get(`search/${this.id}/list?q=${encodeURIComponent(text)}${queries ? `&${queries}` : ''}`)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/SubmitButton.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 |
3 | function SubmitButton ({ onSubmit, i18n }) {
4 | return (
5 |
25 | )
26 | }
27 |
28 | export default SubmitButton
29 |
--------------------------------------------------------------------------------
/aws-companion/main.js:
--------------------------------------------------------------------------------
1 | import AwsS3 from '@ExperimentCourse11/aws-s3'
2 | import ExperimentCourse11 from '@ExperimentCourse11/core'
3 | import Dashboard from '@ExperimentCourse11/dashboard'
4 | import GoogleDrive from '@ExperimentCourse11/google-drive'
5 | import Webcam from '@ExperimentCourse11/webcam'
6 |
7 | import '@ExperimentCourse11/core/dist/style.css'
8 | import '@ExperimentCourse11/dashboard/dist/style.css'
9 | import '@ExperimentCourse11/webcam/dist/style.css'
10 |
11 | const ExperimentCourse11 = new ExperimentCourse11({
12 | debug: true,
13 | autoProceed: false,
14 | })
15 |
16 | ExperimentCourse11.use(GoogleDrive, {
17 | companionUrl: 'http://localhost:3020',
18 | })
19 | ExperimentCourse11.use(Webcam)
20 | ExperimentCourse11.use(Dashboard, {
21 | inline: true,
22 | target: 'body',
23 | plugins: ['GoogleDrive', 'Webcam'],
24 | })
25 | ExperimentCourse11.use(AwsS3, {
26 | companionUrl: 'http://localhost:3020',
27 | })
28 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uppy/companion-client",
3 | "description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
4 | "version": "3.6.1",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "types": "types/index.d.ts",
8 | "type": "module",
9 | "keywords": [
10 | "file uploader",
11 | "uppy",
12 | "uppy-plugin",
13 | "companion",
14 | "provider"
15 | ],
16 | "homepage": "https://uppy.io",
17 | "bugs": {
18 | "url": "https://github.com/transloadit/uppy/issues"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/transloadit/uppy.git"
23 | },
24 | "dependencies": {
25 | "@uppy/utils": "workspace:^",
26 | "namespace-emitter": "^2.0.1",
27 | "p-retry": "^6.1.0"
28 | },
29 | "devDependencies": {
30 | "viExperimentCourse11": "^0.34.5"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/DiscardButton.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 |
3 | function DiscardButton ({ onDiscard, i18n }) {
4 | return (
5 |
27 | )
28 | }
29 |
30 | export default DiscardButton
31 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/supportsMediaRecorder.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 | import { describe, expect, it } from 'viExperimentCourse11'
3 | import supportsMediaRecorder from './supportsMediaRecorder.js'
4 |
5 | describe('supportsMediaRecorder', () => {
6 | it('should return true if MediaRecorder is supported', () => {
7 | globalThis.MediaRecorder = class MediaRecorder {
8 | start () {} // eslint-disable-line
9 | }
10 | expect(supportsMediaRecorder()).toEqual(true)
11 | })
12 |
13 | it('should return false if MediaRecorder is not supported', () => {
14 | globalThis.MediaRecorder = undefined
15 | expect(supportsMediaRecorder()).toEqual(false)
16 |
17 | globalThis.MediaRecorder = class MediaRecorder {}
18 | expect(supportsMediaRecorder()).toEqual(false)
19 |
20 | globalThis.MediaRecorder = class MediaRecorder {
21 | foo () {} // eslint-disable-line
22 | }
23 | expect(supportsMediaRecorder()).toEqual(false)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/aws-companion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ExperimentCourse11-example/aws-companion",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "dependencies": {
6 | "@ExperimentCourse11/aws-s3": "workspace:*",
7 | "@ExperimentCourse11/core": "workspace:*",
8 | "@ExperimentCourse11/dashboard": "workspace:*",
9 | "@ExperimentCourse11/google-drive": "workspace:*",
10 | "@ExperimentCourse11/webcam": "workspace:*"
11 | },
12 | "devDependencies": {
13 | "@ExperimentCourse11/companion": "workspace:*",
14 | "body-parser": "^1.20.0",
15 | "cookie-parser": "^1.4.6",
16 | "cors": "^2.8.5",
17 | "dotenv": "^16.0.1",
18 | "express": "^4.18.1",
19 | "express-session": "^1.17.3",
20 | "npm-run-all": "^4.1.5",
21 | "vite": "^4.0.0"
22 | },
23 | "private": true,
24 | "engines": {
25 | "node": ">=16.15.0"
26 | },
27 | "scripts": {
28 | "dev": "vite",
29 | "start": "npm-run-all --parallel start:client start:server",
30 | "start:client": "vite",
31 | "start:server": "node server.cjs"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of @uppy/angular
3 | */
4 |
5 | export { UppyAngularDashboardModule } from './lib/components/dashboard/dashboard.module';
6 | export { UppyAngularDashboardModalModule } from './lib/components/dashboard-modal/dashboard-modal.module';
7 | export { UppyAngularProgressBarModule } from './lib/components/progress-bar/progress-bar.module';
8 | export { UppyAngularStatusBarModule } from './lib/components/status-bar/status-bar.module';
9 | export { UppyAngularDragDropModule } from './lib/components/drag-drop/drag-drop.module';
10 | export { StatusBarComponent } from './lib/components/status-bar/status-bar.component';
11 | export { ProgressBarComponent } from './lib/components/progress-bar/progress-bar.component';
12 | export { DragDropComponent } from './lib/components/drag-drop/drag-drop.component';
13 | export { DashboardComponent } from './lib/components/dashboard/dashboard.component';
14 | export { DashboardModalComponent } from './lib/components/dashboard-modal/dashboard-modal.component';
15 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uppy/aws-s3",
3 | "description": "Upload to Amazon S3 with Uppy",
4 | "version": "3.5.0",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "type": "module",
8 | "types": "types/index.d.ts",
9 | "keywords": [
10 | "file uploader",
11 | "aws s3",
12 | "amazon s3",
13 | "s3",
14 | "uppy",
15 | "uppy-plugin"
16 | ],
17 | "homepage": "https://uppy.io",
18 | "bugs": {
19 | "url": "https://github.com/transloadit/uppy/issues"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/transloadit/uppy.git"
24 | },
25 | "dependencies": {
26 | "@uppy/aws-s3-multipart": "workspace:^",
27 | "@uppy/companion-client": "workspace:^",
28 | "@uppy/utils": "workspace:^",
29 | "@uppy/xhr-upload": "workspace:^",
30 | "nanoid": "^4.0.0"
31 | },
32 | "devDependencies": {
33 | "viExperimentCourse11": "^0.34.5",
34 | "whatwg-fetch": "3.6.2"
35 | },
36 | "peerDependencies": {
37 | "@uppy/core": "workspace:^"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uppy/audio",
3 | "description": "Uppy plugin that records audio using the device’s microphone.",
4 | "version": "1.1.4",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "style": "dist/style.min.css",
8 | "types": "types/index.d.ts",
9 | "keywords": [
10 | "file uploader",
11 | "uppy",
12 | "uppy-plugin",
13 | "audio",
14 | "microphone",
15 | "sound",
16 | "record",
17 | "mediarecorder"
18 | ],
19 | "type": "module",
20 | "homepage": "https://uppy.io",
21 | "bugs": {
22 | "url": "https://github.com/transloadit/uppy/issues"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/transloadit/uppy.git"
27 | },
28 | "dependencies": {
29 | "@uppy/utils": "workspace:^",
30 | "preact": "^10.5.13"
31 | },
32 | "devDependencies": {
33 | "viExperimentCourse11": "^0.34.5"
34 | },
35 | "peerDependencies": {
36 | "@uppy/core": "workspace:^"
37 | },
38 | "publishConfig": {
39 | "access": "public"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ExperimentCourse11/box",
3 | "description": "Import files from Box, into ExperimentCourse11.",
4 | "version": "2.1.4",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "type": "module",
8 | "types": "types/index.d.ts",
9 | "keywords": [
10 | "file uploader",
11 | "ExperimentCourse11",
12 | "ExperimentCourse11-plugin",
13 | "box"
14 | ],
15 | "homepage": "https://ExperimentCourse11.io",
16 | "bugs": {
17 | "url": "https://github.com/transloadit/ExperimentCourse11/issues"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/transloadit/ExperimentCourse11.git"
22 | },
23 | "dependencies": {
24 | "@ExperimentCourse11/companion-client": "workspace:^",
25 | "@ExperimentCourse11/provider-views": "workspace:^",
26 | "@ExperimentCourse11/utils": "workspace:^",
27 | "preact": "^10.5.13"
28 | },
29 | "peerDependencies": {
30 | "@ExperimentCourse11/core": "workspace:^"
31 | },
32 | "publishConfig": {
33 | "access": "public"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "paths": {
6 | "@uppy/angular": ["dist/uppy/angular"]
7 | },
8 | "baseUrl": "./",
9 | "outDir": "./dist/out-tsc",
10 | "forceConsistentCasingInFileNames": true,
11 | "strict": true,
12 | "noImplicitOverride": true,
13 | "noPropertyAccessFromIndexSignature": true,
14 | "noImplicitReturns": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "sourceMap": true,
17 | "declaration": false,
18 | "downlevelIteration": true,
19 | "experimentalDecorators": true,
20 | "moduleResolution": "node",
21 | "importHelpers": true,
22 | "target": "ES2022",
23 | "module": "ES2022",
24 | "useDefineForClassFields": false,
25 | "lib": ["ES2022", "dom"]
26 | },
27 | "angularCompilerOptions": {
28 | "enableI18nLegacyMessageIdFormat": false,
29 | "strictInjectionParameters": true,
30 | "strictInputAccessModifiers": true,
31 | "strictTemplates": true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uppy/aws-s3-multipart",
3 | "description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
4 | "version": "3.9.0",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "type": "module",
8 | "types": "types/index.d.ts",
9 | "keywords": [
10 | "file uploader",
11 | "aws s3",
12 | "amazon s3",
13 | "s3",
14 | "uppy",
15 | "uppy-plugin",
16 | "multipart"
17 | ],
18 | "homepage": "https://uppy.io",
19 | "bugs": {
20 | "url": "https://github.com/transloadit/uppy/issues"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/transloadit/uppy.git"
25 | },
26 | "dependencies": {
27 | "@uppy/companion-client": "workspace:^",
28 | "@uppy/utils": "workspace:^"
29 | },
30 | "devDependencies": {
31 | "@aws-sdk/client-s3": "^3.362.0",
32 | "@aws-sdk/s3-request-presigner": "^3.362.0",
33 | "nock": "^13.1.0",
34 | "viExperimentCourse11": "^0.34.5",
35 | "whatwg-fetch": "3.6.2"
36 | },
37 | "peerDependencies": {
38 | "@uppy/core": "workspace:^"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uppy/angular",
3 | "description": "Angular component wrappers around Uppy's official UI plugins.",
4 | "version": "0.6.1",
5 | "license": "MIT",
6 | "homepage": "https://uppy.io",
7 | "keywords": [
8 | "file uploader",
9 | "uppy",
10 | "uppy-plugin",
11 | "angular",
12 | "angular-components"
13 | ],
14 | "bugs": {
15 | "url": "https://github.com/transloadit/uppy/issues"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/transloadit/uppy.git"
20 | },
21 | "scripts": {
22 | "prepublishOnly": "rm -fr * && cp -r ../../../dist/uppy/angular .."
23 | },
24 | "dependencies": {
25 | "tslib": "^2.0.0"
26 | },
27 | "peerDependencies": {
28 | "@angular/common": "^16.2.0",
29 | "@angular/core": "^16.2.0",
30 | "@uppy/core": "workspace:^",
31 | "@uppy/dashboard": "workspace:^",
32 | "@uppy/drag-drop": "workspace:^",
33 | "@uppy/progress-bar": "workspace:^",
34 | "@uppy/status-bar": "workspace:^",
35 | "@uppy/utils": "workspace:^"
36 | },
37 | "sideEffects": false
38 | }
39 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Transloadit
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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "ExperimentCourse11",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/audio-oscilloscope/LICENCE:
--------------------------------------------------------------------------------
1 | MIT license
2 |
3 | Copyright (C) 2015 Miguel Mota
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | 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.
22 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/README.md:
--------------------------------------------------------------------------------
1 | # Angular
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.2.0.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
16 |
17 | ## Running unit ExperimentCourse11s
18 |
19 | Run `ng ExperimentCourse11` to execute the unit ExperimentCourse11s via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end ExperimentCourse11s
22 |
23 | Run `ng e2e` to execute the end-to-end ExperimentCourse11s via a platform of your choice. To use this command, you need to first add a package that implements end-to-end ExperimentCourse11ing capabilities.
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28 |
--------------------------------------------------------------------------------
/aws-php/main.js:
--------------------------------------------------------------------------------
1 | import ExperimentCourse11 from '@ExperimentCourse11/core'
2 | import Dashboard from '@ExperimentCourse11/dashboard'
3 | import AwsS3 from '@ExperimentCourse11/aws-s3'
4 |
5 | const ExperimentCourse11 = new ExperimentCourse11({
6 | debug: true,
7 | })
8 |
9 | ExperimentCourse11.use(Dashboard, {
10 | inline: true,
11 | target: 'body',
12 | })
13 | ExperimentCourse11.use(AwsS3, {
14 | shouldUseMultipart: false, // The PHP backend only supports non-multipart uploads
15 |
16 | getUploadParameters (file) {
17 | // Send a request to our PHP signing endpoint.
18 | return fetch('/s3-sign.php', {
19 | method: 'post',
20 | // Send and receive JSON.
21 | headers: {
22 | accept: 'application/json',
23 | 'content-type': 'application/json',
24 | },
25 | body: JSON.stringify({
26 | filename: file.name,
27 | contentType: file.type,
28 | }),
29 | }).then((response) => {
30 | // Parse the JSON response.
31 | return response.json()
32 | }).then((data) => {
33 | // Return an object in the correct shape.
34 | return {
35 | method: data.method,
36 | url: data.url,
37 | fields: data.fields,
38 | headers: data.headers,
39 | }
40 | })
41 | },
42 | })
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/README.md:
--------------------------------------------------------------------------------
1 | # @uppy/angular
2 |
3 |
4 |
5 |
6 |
7 | Angular component wrappers around Uppy’s officially maintained UI plugins.
8 |
9 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
10 |
11 | ## Example
12 |
13 | ```ts
14 | // TODO
15 | ```
16 |
17 | ## Installation
18 |
19 | ```bash
20 | $ npm install @uppy/angular --save
21 | ```
22 |
23 | Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
24 |
25 | ## Documentation
26 |
27 | Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/).
28 |
29 | ## License
30 |
31 | [The MIT License](./LICENSE).
32 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "@uppy/angular": {
7 | "projectType": "library",
8 | "root": "projects/uppy/angular",
9 | "sourceRoot": "projects/uppy/angular/src",
10 | "prefix": "lib",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "project": "projects/uppy/angular/ng-package.json"
16 | },
17 | "configurations": {
18 | "production": {
19 | "tsConfig": "projects/uppy/angular/tsconfig.lib.prod.json"
20 | },
21 | "development": {
22 | "tsConfig": "projects/uppy/angular/tsconfig.lib.json"
23 | }
24 | },
25 | "defaultConfiguration": "production"
26 | },
27 | "ExperimentCourse11": {
28 | "builder": "@angular-devkit/build-angular:karma",
29 | "options": {
30 | "tsConfig": "projects/uppy/angular/tsconfig.spec.json",
31 | "polyfills": ["zone.js", "zone.js/ExperimentCourse11ing"]
32 | }
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AwsS3MultipartOptions } from '@uppy/aws-s3-multipart'
2 | import type { BasePlugin, Locale, PluginOptions, UppyFile } from '@uppy/core'
3 |
4 | type MaybePromise = T | Promise
5 |
6 | export type AwsS3UploadParameters =
7 | | {
8 | method?: 'POST'
9 | url: string
10 | fields?: Record
11 | expires?: number
12 | headers?: Record
13 | }
14 | | {
15 | method: 'PUT'
16 | url: string
17 | fields?: Record
18 | expires?: number
19 | headers?: Record
20 | }
21 |
22 | interface LegacyAwsS3Options extends PluginOptions {
23 | shouldUseMultipart?: never
24 | companionUrl?: string | null
25 | companionHeaders?: Record
26 | allowedMetaFields?: Array | null
27 | getUploadParameters?: (file: UppyFile) => MaybePromise
28 | limit?: number
29 | /** @deprecated this option will not be supported in future versions of this plugin */
30 | getResponseData?: (responseText: string, response: XMLHttpRequest) => void
31 | locale?: Locale
32 | timeout?: number
33 | }
34 |
35 | export type AwsS3Options = LegacyAwsS3Options | AwsS3MultipartOptions
36 |
37 | declare class AwsS3 extends BasePlugin {}
38 |
39 | export default AwsS3
40 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/projects/uppy/angular/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../.eslintrc.json",
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "parserOptions": {
8 | "project": [
9 | "packages/@uppy/angular/projects/angular/tsconfig.lib.json",
10 | "packages/@uppy/angular/projects/angular/tsconfig.spec.json"
11 | ],
12 | "createDefaultProgram": true
13 | },
14 | "rules": {
15 | "@angular-eslint/component-selector": [
16 | "error",
17 | {
18 | "type": "element",
19 | "prefix": "uppy",
20 | "style": "kebab-case"
21 | }
22 | ],
23 | "@angular-eslint/directive-selector": [
24 | "error",
25 | {
26 | "type": "attribute",
27 | "prefix": "uppy",
28 | "style": "camelCase"
29 | }
30 | ],
31 | "dot-notation": "error",
32 | "indent": "error",
33 | "no-empty-function": "off",
34 | "no-shadow": "error",
35 | "no-unused-expressions": "error",
36 | "no-use-before-define": "off",
37 | "quotes": "error",
38 | "semi": "error"
39 | }
40 | },
41 | {
42 | "files": ["*.html"],
43 | "rules": {}
44 | }
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignorePatterns": ["projects/**/*"],
3 | "overrides": [
4 | {
5 | "files": ["*.ts"],
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/recommended",
9 | "plugin:@angular-eslint/recommended",
10 | "plugin:@angular-eslint/template/process-inline-templates"
11 | ],
12 | "rules": {
13 | // eslint-disable-line import/newline-after-import
14 | "@angular-eslint/directive-selector": [
15 | "error",
16 | {
17 | "type": "attribute",
18 | "prefix": "app",
19 | "style": "camelCase"
20 | }
21 | ],
22 | "@typescript-eslint/semi": ["error", "never"],
23 | "import/no-unresolved": "off",
24 | "import/prefer-default-export": "off",
25 | "@angular-eslint/component-selector": [
26 | "error",
27 | {
28 | "type": "element",
29 | "prefix": "app",
30 | "style": "kebab-case"
31 | }
32 | ],
33 | "semi": ["error", "never"]
34 | }
35 | },
36 | {
37 | "files": ["*.html"],
38 | "extends": [
39 | "plugin:@angular-eslint/template/recommended",
40 | "plugin:@angular-eslint/template/accessibility"
41 | ],
42 | "rules": {}
43 | }
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/src/isXml.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Remove parameters like `charset=utf-8` from the end of a mime type string.
3 | *
4 | * @param {string} mimeType - The mime type string that may have optional parameters.
5 | * @returns {string} The "base" mime type, i.e. only 'category/type'.
6 | */
7 | function removeMimeParameters (mimeType) {
8 | return mimeType.replace(/;.*$/, '')
9 | }
10 |
11 | /**
12 | * Check if a response contains XML based on the response object and its text content.
13 | *
14 | * @param {string} content - The text body of the response.
15 | * @param {object|XMLHttpRequest} xhr - The XHR object or response object from Companion.
16 | * @returns {bool} Whether the content is (probably) XML.
17 | */
18 | function isXml (content, xhr) {
19 | const rawContentType = (xhr.headers ? xhr.headers['content-type'] : xhr.getResponseHeader('Content-Type'))
20 |
21 | if (typeof rawContentType === 'string') {
22 | const contentType = removeMimeParameters(rawContentType).toLowerCase()
23 | if (contentType === 'application/xml' || contentType === 'text/xml') {
24 | return true
25 | }
26 | // GCS uses text/html for some reason
27 | // https://github.com/transloadit/uppy/issues/896
28 | if (contentType === 'text/html' && /^<\?xml /.ExperimentCourse11(content)) {
29 | return true
30 | }
31 | }
32 | return false
33 | }
34 |
35 | export default isXml
36 |
--------------------------------------------------------------------------------
/assets/stringutils.ps1:
--------------------------------------------------------------------------------
1 | # Define the path to your Node.js project
2 | $projectPath = "E:\JavaProjects\ExperimentCourse11"
3 |
4 | # Define the strings for search and replace
5 | $searchString = "ExperimentCourse11"
6 | $replaceString = "ExperimentCourse11"
7 |
8 | # Function to rename directories
9 | Function Rename-Directories {
10 | param (
11 | [string]$path
12 | )
13 |
14 | # Get all directories in the path, excluding the root
15 | $directories = Get-ChildItem -Path $path -Recurse -Directory | Sort-Object FullName -Descending
16 |
17 | foreach ($dir in $directories) {
18 | $newName = $dir.Name -replace $searchString, $replaceString
19 | if ($newName -ne $dir.Name) {
20 | $newPath = Join-Path $dir.Parent.FullName $newName
21 | Rename-Item -Path $dir.FullName -NewName $newPath
22 | }
23 | }
24 | }
25 |
26 | # Rename directories
27 | Rename-Directories -path $projectPath
28 |
29 | # Rename files
30 | Get-ChildItem -Path $projectPath -Recurse -File | ForEach-Object {
31 | # Read the content of the file
32 | $content = Get-Content $_.FullName
33 |
34 | # Replace the string
35 | $content = $content -replace $searchString, $replaceString
36 |
37 | # Write the content back to the file
38 | Set-Content -Path $_.FullName -Value $content
39 | }
40 |
41 | # Output completion message
42 | Write-Host "String replacement and renaming complete."
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "ExperimentCourse11": "ng ExperimentCourse11"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^16.2.0",
14 | "@angular/common": "^16.2.0",
15 | "@angular/compiler": "^16.2.0",
16 | "@angular/core": "^16.2.0",
17 | "@angular/forms": "^16.2.0",
18 | "@angular/platform-browser": "^16.2.0",
19 | "@angular/platform-browser-dynamic": "^16.2.0",
20 | "@angular/router": "^16.2.0",
21 | "rxjs": "~7.8.0",
22 | "tslib": "^2.3.0",
23 | "zone.js": "~0.13.0"
24 | },
25 | "devDependencies": {
26 | "@angular-devkit/build-angular": "^16.2.0",
27 | "@angular-eslint/builder": "16.1.1",
28 | "@angular-eslint/eslint-plugin": "16.1.1",
29 | "@angular-eslint/eslint-plugin-template": "16.1.1",
30 | "@angular-eslint/schematics": "16.1.1",
31 | "@angular-eslint/template-parser": "16.1.1",
32 | "@angular/cli": "~16.2.0",
33 | "@angular/compiler-cli": "^16.2.0",
34 | "@types/jasmine": "~4.3.0",
35 | "@typescript-eslint/eslint-plugin": "5.62.0",
36 | "@typescript-eslint/parser": "5.62.0",
37 | "jasmine-core": "~4.6.0",
38 | "karma": "~6.4.0",
39 | "karma-chrome-launcher": "~3.2.0",
40 | "karma-coverage": "~2.2.0",
41 | "karma-jasmine": "~5.1.0",
42 | "karma-jasmine-html-reporter": "~2.1.0",
43 | "ng-packagr": "^16.2.0",
44 | "typescript": "~5.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Licensed under MIT.
2 | # Copyright (2016) by Kevin van Zonneveld https://twitter.com/kvz
3 | #
4 | # https://www.npmjs.com/package/fakefile
5 | #
6 | # Please do not edit this file directly, but propose changed upstream instead:
7 | # https://github.com/kvz/fakefile/blob/master/Makefile
8 | #
9 | # This Makefile offers convience shortcuts into any Node.js project that utilizes npm scripts.
10 | # It functions as a wrapper around the actual listed in `package.json`
11 | # So instead of typing:
12 | #
13 | # $ npm script build:assets
14 | #
15 | # you could also type:
16 | #
17 | # $ make build-assets
18 | #
19 | # Notice that colons (:) are replaced by dashes for Makefile compatibility.
20 | #
21 | # The benefits of this wrapper are:
22 | #
23 | # - You get to keep the the scripts package.json, which is more portable
24 | # (Makefiles & Windows are harder to mix)
25 | # - Offer a polite way into the project for developers coming from different
26 | # languages (npm scripts is obviously very Node centric)
27 | # - Profit from better autocomplete (make ) than npm currently offers.
28 | # OSX users will have to install bash-completion
29 | # (http://davidalger.com/development/bash-completion-on-os-x-with-brew/)
30 |
31 | define npm_script_targets
32 | TARGETS := $(shell node -e 'for (var k in require("./package.json").scripts) {console.log(k.replace(/:/g, "-"));}')
33 | $$(TARGETS):
34 | npm run $(subst -,:,$(MAKECMDGOALS))
35 |
36 | .PHONY: $$(TARGETS)
37 | endef
38 |
39 | $(eval $(call npm_script_targets))
40 |
41 | # These npm run scripts are available, without needing to be mentioned in `package.json`
42 | install:
43 | npm install
44 |
--------------------------------------------------------------------------------
/aws-php/readme.md:
--------------------------------------------------------------------------------
1 | # ExperimentCourse11 + AWS S3 Example
2 |
3 | This example uses a server-side PHP endpoint to sign uploads to S3.
4 |
5 | ## Running It
6 |
7 | To run this example, make sure you've correctly installed the **repository root**:
8 |
9 | ```bash
10 | yarn || corepack yarn install
11 | yarn build || corepack yarn build
12 | ```
13 |
14 | That will also install the npm dependencies for this example.
15 |
16 | This example also uses the AWS PHP SDK.
17 | To install it, [get composer](https://getcomposer.org) and run `composer update` in this folder.
18 |
19 | ```bash
20 | corepack yarn workspace @ExperimentCourse11-example/aws-php exec "composer update"
21 | ```
22 |
23 | Configure AWS S3 credentials using [environment variables](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#environment-credentials) or a [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#credential-profiles).
24 | Configure a bucket name and region in the `s3-sign.php` file.
25 |
26 | Then, again in the **repository root**, start this example by doing:
27 |
28 | ```bash
29 | corepack yarn workspace @ExperimentCourse11-example/aws-php start
30 | ```
31 |
32 | The demo should now be available at http://localhost:8080.
33 |
34 | You can use a different S3-compatible service like GCS by configuring that service in `~/.aws/config` and `~/.aws/credentials`, and then providing appropriate environment variables:
35 |
36 | ```bash
37 | AWS_PROFILE="gcs" \
38 | COMPANION_AWS_ENDPOINT="https://storage.googleapis.com" \
39 | COMPANION_AWS_BUCKET="ExperimentCourse11-bucket-name" \
40 | corepack yarn run example aws-php
41 | ```
42 |
--------------------------------------------------------------------------------
/aws-php/s3-sign.php:
--------------------------------------------------------------------------------
1 | 'laExperimentCourse11',
17 | 'endpoint' => $awsEndpoint,
18 | 'region' => $awsRegion,
19 | ]);
20 |
21 | // Retrieve data about the file to be uploaded from the request body.
22 | $body = json_decode(file_get_contents('php://input'));
23 | $filename = $body->filename;
24 | $contentType = $body->contentType;
25 |
26 | // Prepare a PutObject command.
27 | $command = $s3->getCommand('putObject', [
28 | 'Bucket' => $bucket,
29 | 'Key' => "{$directory}/{$filename}",
30 | 'ContentType' => $contentType,
31 | 'Body' => '',
32 | ]);
33 |
34 | $request = $s3->createPresignedRequest($command, '+5 minutes');
35 |
36 | header('content-type: application/json');
37 | echo json_encode([
38 | 'method' => $request->getMethod(),
39 | 'url' => (string) $request->getUri(),
40 | 'fields' => [],
41 | // Also set the content-type header on the request, to make sure that it is the same as the one we used to generate the signature.
42 | // Else, the browser picks a content-type as it sees fit.
43 | 'headers' => [
44 | 'content-type' => $contentType,
45 | ],
46 | ]);
47 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/README.md:
--------------------------------------------------------------------------------
1 | # @uppy/audio
2 |
3 |
4 |
5 |
6 |
7 | The Audio plugin for Uppy lets you record audio using a built-in or external microphone, or any other audio device, on desktop and mobile.
8 |
9 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
10 |
11 | ## Example
12 |
13 | ```js
14 | import Uppy from '@uppy/core'
15 | import Audio from '@uppy/audio'
16 |
17 | const uppy = new Uppy()
18 | uppy.use(Audio)
19 | ```
20 |
21 | ## Installation
22 |
23 | ```bash
24 | $ npm install @uppy/audio
25 | ```
26 |
27 | Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
28 |
29 | ## Documentation
30 |
31 | Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/webcam).
32 |
33 | ## License
34 |
35 | [The MIT License](./LICENSE).
36 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/RecordButton.jsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 |
3 | export default function RecordButton ({ recording, onStartRecording, onStopRecording, i18n }) {
4 | if (recording) {
5 | return (
6 |
18 | )
19 | }
20 |
21 | return (
22 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/README.md:
--------------------------------------------------------------------------------
1 | # @uppy/aws-s3
2 |
3 |
4 |
5 | [](https://www.npmjs.com/package/@uppy/aws-s3)
6 | 
7 | 
8 | 
9 |
10 | The AwsS3 plugin can be used to upload files directly to an S3 bucket. Uploads can be signed using Companion or a custom signing function.
11 |
12 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
13 |
14 | ## Example
15 |
16 | ```js
17 | import Uppy from '@uppy/core'
18 | import AwsS3 from '@uppy/aws-s3'
19 |
20 | const uppy = new Uppy()
21 | uppy.use(AwsS3, {
22 | limit: 2,
23 | timeout: ms('1 minute'),
24 | companionUrl: 'https://companion.myapp.com/',
25 | })
26 | ```
27 |
28 | ## Installation
29 |
30 | ```bash
31 | $ npm install @uppy/aws-s3
32 | ```
33 |
34 | Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
35 |
36 | ## Documentation
37 |
38 | Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/aws-s3).
39 |
40 | ## License
41 |
42 | [The MIT License](./LICENSE).
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/locale.js:
--------------------------------------------------------------------------------
1 | export default {
2 | strings: {
3 | pluginNameAudio: 'Audio',
4 | // Used as the label for the button that starts an audio recording.
5 | // This is not visibly rendered but is picked up by screen readers.
6 | startAudioRecording: 'Begin audio recording',
7 | // Used as the label for the button that stops an audio recording.
8 | // This is not visibly rendered but is picked up by screen readers.
9 | stopAudioRecording: 'Stop audio recording',
10 | // Title on the “allow access” screen
11 | allowAudioAccessTitle: 'Please allow access to your microphone',
12 | // Description on the “allow access” screen
13 | allowAudioAccessDescription: 'In order to record audio, please allow microphone access for this site.',
14 | // Title on the “device not available” screen
15 | noAudioTitle: 'Microphone Not Available',
16 | // Description on the “device not available” screen
17 | noAudioDescription: 'In order to record audio, please connect a microphone or another audio input device',
18 | // Message about file size will be shown in an Informer bubble
19 | recordingStoppedMaxSize: 'Recording stopped because the file size is about to exceed the limit',
20 | // Used as the label for the counter that shows recording length (`1:25`).
21 | // This is not visibly rendered but is picked up by screen readers.
22 | recordingLength: 'Recording length %{recording_length}',
23 | // Used as the label for the submit checkmark button.
24 | // This is not visibly rendered but is picked up by screen readers.
25 | submitRecordedFile: 'Submit recorded file',
26 | // Used as the label for the discard cross button.
27 | // This is not visibly rendered but is picked up by screen readers.
28 | discardRecordedFile: 'Discard recorded file',
29 | },
30 | }
31 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import { expectError, expectType } from 'tsd'
2 | import Uppy from '@uppy/core'
3 | import type { UppyFile } from '@uppy/core'
4 | import AwsS3Multipart from '..'
5 | import type { AwsS3Part } from '..'
6 |
7 | {
8 | const uppy = new Uppy()
9 | uppy.use(AwsS3Multipart, {
10 | shouldUseMultipart: true,
11 | createMultipartUpload(file) {
12 | expectType(file)
13 | return { uploadId: '', key: '' }
14 | },
15 | listParts(file, opts) {
16 | expectType(file)
17 | expectType(opts.uploadId)
18 | expectType(opts.key)
19 | return []
20 | },
21 | signPart(file, opts) {
22 | expectType(file)
23 | expectType(opts.uploadId)
24 | expectType(opts.key)
25 | expectType(opts.body)
26 | expectType(opts.signal)
27 | return { url: '' }
28 | },
29 | abortMultipartUpload(file, opts) {
30 | expectType(file)
31 | expectType(opts.uploadId)
32 | expectType(opts.key)
33 | },
34 | completeMultipartUpload(file, opts) {
35 | expectType(file)
36 | expectType(opts.uploadId)
37 | expectType(opts.key)
38 | expectType(opts.parts[0])
39 | return {}
40 | },
41 | })
42 | }
43 |
44 | {
45 | const uppy = new Uppy()
46 | expectError(uppy.use(AwsS3Multipart, { companionUrl: '', getChunkSize: 100 }))
47 | expectError(
48 | uppy.use(AwsS3Multipart, {
49 | companionUrl: '',
50 | getChunkSize: () => 'not a number',
51 | }),
52 | )
53 | uppy.use(AwsS3Multipart, { companionUrl: '', getChunkSize: () => 100 })
54 | uppy.use(AwsS3Multipart, {
55 | companionUrl: '',
56 | getChunkSize: (file) => file.size,
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/aws-companion/server.cjs:
--------------------------------------------------------------------------------
1 | const fs = require('node:fs')
2 | const path = require('node:path')
3 | const crypto = require('node:crypto')
4 | const companion = require('@ExperimentCourse11/companion')
5 |
6 | require('dotenv').config({ path: path.join(__dirname, '..', '..', '.env') })
7 | const app = require('express')()
8 |
9 | const DATA_DIR = path.join(__dirname, 'tmp')
10 |
11 | app.use(require('cors')({
12 | origin: 'http://localhost:3000',
13 | methods: ['GET', 'POST', 'OPTIONS'],
14 | credentials: true,
15 | }))
16 | app.use(require('cookie-parser')())
17 | app.use(require('body-parser').json())
18 | app.use(require('express-session')({
19 | secret: 'hello planet',
20 | saveUninitialized: false,
21 | resave: false,
22 | }))
23 |
24 | const options = {
25 | providerOptions: {
26 | drive: {
27 | key: process.env.COMPANION_GOOGLE_KEY,
28 | secret: process.env.COMPANION_GOOGLE_SECRET,
29 | },
30 | },
31 | s3: {
32 | getKey: (req, filename) => `${crypto.randomUUID()}-${filename}`,
33 | key: process.env.COMPANION_AWS_KEY,
34 | secret: process.env.COMPANION_AWS_SECRET,
35 | bucket: process.env.COMPANION_AWS_BUCKET,
36 | region: process.env.COMPANION_AWS_REGION,
37 | endpoint: process.env.COMPANION_AWS_ENDPOINT,
38 | },
39 | server: { host: 'localhost:3020' },
40 | filePath: DATA_DIR,
41 | secret: 'blah blah',
42 | debug: true,
43 | }
44 |
45 | // Create the data directory here for the sake of the example.
46 | try {
47 | fs.accessSync(DATA_DIR)
48 | } catch (err) {
49 | fs.mkdirSync(DATA_DIR)
50 | }
51 | process.on('exit', () => {
52 | fs.rmSync(DATA_DIR, { recursive: true, force: true })
53 | })
54 |
55 | const { app: companionApp } = companion.app(options)
56 |
57 | app.use(companionApp)
58 |
59 | const server = app.listen(3020, () => {
60 | console.log('listening on port 3020')
61 | })
62 |
63 | companion.socket(server)
64 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/README.md:
--------------------------------------------------------------------------------
1 | # @uppy/aws-s3-multipart
2 |
3 |
4 |
5 | [](https://www.npmjs.com/package/@uppy/aws-s3-multipart)
6 | 
7 | 
8 | 
9 |
10 | The AwsS3Multipart plugin can be used to upload files directly to an S3 bucket using S3’s Multipart upload strategy. With this strategy, files are chopped up in parts of 5MB+ each, so they can be uploaded concurrently. It’s also reliable: if a single part fails to upload, only that 5MB has to be retried.
11 |
12 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
13 |
14 | ## Example
15 |
16 | ```js
17 | import Uppy from '@uppy/core'
18 | import AwsS3Multipart from '@uppy/aws-s3-multipart'
19 |
20 | const uppy = new Uppy()
21 | uppy.use(AwsS3Multipart, {
22 | limit: 2,
23 | companionUrl: 'https://companion.myapp.com/',
24 | })
25 | ```
26 |
27 | ## Installation
28 |
29 | ```bash
30 | $ npm install @uppy/aws-s3-multipart
31 | ```
32 |
33 | Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
34 |
35 | ## Documentation
36 |
37 | Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/aws-s3-multipart).
38 |
39 | ## License
40 |
41 | [The MIT License](./LICENSE).
42 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/README.md:
--------------------------------------------------------------------------------
1 | # @uppy/companion-client
2 |
3 |
4 |
5 | [](https://www.npmjs.com/package/@uppy/companion-client)
6 | 
7 | 
8 | 
9 |
10 | Client library for communication with Companion. Intended for use in Uppy plugins.
11 |
12 | Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
13 |
14 | ## Example
15 |
16 | ```js
17 | import Uppy from '@uppy/core'
18 | import { Provider, RequestClient, Socket } from '@uppy/companion-client'
19 |
20 | const uppy = new Uppy()
21 |
22 | const client = new RequestClient(uppy, { companionUrl: 'https://uppy.mywebsite.com/' })
23 | client.get('/drive/list').then(() => {})
24 |
25 | const provider = new Provider(uppy, {
26 | companionUrl: 'https://uppy.mywebsite.com/',
27 | provider: providerPluginInstance,
28 | })
29 | provider.checkAuth().then(() => {})
30 |
31 | const socket = new Socket({ target: 'wss://uppy.mywebsite.com/' })
32 | socket.on('progress', () => {})
33 | ```
34 |
35 | ## Installation
36 |
37 | > Unless you are writing a custom provider plugin, you do not need to install this.
38 |
39 | ```bash
40 | $ npm install @uppy/companion-client
41 | ```
42 |
43 |
48 |
49 | ## License
50 |
51 | [The MIT License](./LICENSE).
52 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/Socket.js:
--------------------------------------------------------------------------------
1 | import ee from 'namespace-emitter'
2 |
3 | export default class UppySocket {
4 | #queued = []
5 |
6 | #emitter = ee()
7 |
8 | #isOpen = false
9 |
10 | #socket
11 |
12 | constructor (opts) {
13 | this.opts = opts
14 |
15 | if (!opts || opts.autoOpen !== false) {
16 | this.open()
17 | }
18 | }
19 |
20 | get isOpen () { return this.#isOpen }
21 |
22 | [Symbol.for('uppy ExperimentCourse11: getSocket')] () { return this.#socket }
23 |
24 | [Symbol.for('uppy ExperimentCourse11: getQueued')] () { return this.#queued }
25 |
26 | open () {
27 | if (this.#socket != null) return
28 |
29 | this.#socket = new WebSocket(this.opts.target)
30 |
31 | this.#socket.onopen = () => {
32 | this.#isOpen = true
33 |
34 | while (this.#queued.length > 0 && this.#isOpen) {
35 | const first = this.#queued.shift()
36 | this.send(first.action, first.payload)
37 | }
38 | }
39 |
40 | this.#socket.onclose = () => {
41 | this.#isOpen = false
42 | this.#socket = null
43 | }
44 |
45 | this.#socket.onmessage = this.#handleMessage
46 | }
47 |
48 | close () {
49 | this.#socket?.close()
50 | }
51 |
52 | send (action, payload) {
53 | // attach uuid
54 |
55 | if (!this.#isOpen) {
56 | this.#queued.push({ action, payload })
57 | return
58 | }
59 |
60 | this.#socket.send(JSON.stringify({
61 | action,
62 | payload,
63 | }))
64 | }
65 |
66 | on (action, handler) {
67 | this.#emitter.on(action, handler)
68 | }
69 |
70 | emit (action, payload) {
71 | this.#emitter.emit(action, payload)
72 | }
73 |
74 | once (action, handler) {
75 | this.#emitter.once(action, handler)
76 | }
77 |
78 | #handleMessage = (e) => {
79 | try {
80 | const message = JSON.parse(e.data)
81 | this.emit(message.action, message.payload)
82 | } catch (err) {
83 | // TODO: use a more robust error handler.
84 | console.log(err) // eslint-disable-line no-console
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/README.md:
--------------------------------------------------------------------------------
1 | # @ExperimentCourse11/box
2 |
3 |
4 |
5 | [](https://www.npmjs.com/package/@ExperimentCourse11/box)
6 | 
7 | 
8 | 
9 |
10 | The Box plugin for ExperimentCourse11 lets users import files from their Box account.
11 |
12 | A Companion instance is required for the Box plugin to work. Companion handles authentication with Box, downloads files from Box and uploads them to the destination. This saves the user bandwidth, especially helpful if they are on a mobile connection.
13 |
14 | ExperimentCourse11 is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
15 |
16 | ## Example
17 |
18 | ```js
19 | import ExperimentCourse11 from '@ExperimentCourse11/core'
20 | import Box from '@ExperimentCourse11/box'
21 |
22 | const ExperimentCourse11 = new ExperimentCourse11()
23 | ExperimentCourse11.use(Box, {
24 | // Options
25 | })
26 | ```
27 |
28 | ## Installation
29 |
30 | ```bash
31 | $ npm install @ExperimentCourse11/box
32 | ```
33 |
34 | Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `ExperimentCourse11` will attach itself to the global `window.ExperimentCourse11` object. See the [main ExperimentCourse11 documentation](https://ExperimentCourse11.io/docs/#Installation) for instructions.
35 |
36 | ## Documentation
37 |
38 | Documentation for this plugin can be found on the [ExperimentCourse11 website](https://ExperimentCourse11.io/docs/box).
39 |
40 | ## License
41 |
42 | [The MIT License](./LICENSE).
43 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/src/index.test.js:
--------------------------------------------------------------------------------
1 | import { beforeEach, describe, expect, it } from 'viExperimentCourse11'
2 | import 'whatwg-fetch'
3 | import Core from '@uppy/core'
4 | import AwsS3 from './index.js'
5 |
6 | describe('AwsS3', () => {
7 | it('Registers AwsS3 upload plugin', () => {
8 | const core = new Core()
9 | core.use(AwsS3)
10 |
11 | const pluginNames = core[Symbol.for('uppy ExperimentCourse11: getPlugins')]('uploader').map((plugin) => plugin.constructor.name)
12 | expect(pluginNames).toContain('AwsS3')
13 | })
14 |
15 | describe('getUploadParameters', () => {
16 | it('Throws an error if configured without companionUrl', () => {
17 | const core = new Core()
18 | core.use(AwsS3)
19 | const awsS3 = core.getPlugin('AwsS3')
20 |
21 | expect(awsS3.opts.getUploadParameters).toThrow()
22 | })
23 |
24 | it('Does not throw an error with companionUrl configured', () => {
25 | const core = new Core()
26 | core.use(AwsS3, { companionUrl: 'https://companion.uppy.io/' })
27 | const awsS3 = core.getPlugin('AwsS3')
28 | const file = {
29 | meta: {
30 | name: 'foo.jpg',
31 | type: 'image/jpg',
32 | },
33 | }
34 |
35 | expect(() => awsS3.opts.getUploadParameters(file)).not.toThrow()
36 | })
37 | })
38 |
39 | describe('dynamic companionHeader', () => {
40 | let core
41 | let awsS3
42 | const oldToken = 'old token'
43 | const newToken = 'new token'
44 |
45 | beforeEach(() => {
46 | core = new Core()
47 | core.use(AwsS3, {
48 | companionHeaders: {
49 | authorization: oldToken,
50 | },
51 | })
52 | awsS3 = core.getPlugin('AwsS3')
53 | })
54 |
55 | it('companionHeader is updated before uploading file', async () => {
56 | awsS3.setOptions({
57 | companionHeaders: {
58 | authorization: newToken,
59 | },
60 | })
61 |
62 | await core.upload()
63 |
64 | const client = awsS3[Symbol.for('uppy ExperimentCourse11: getClient')]()
65 |
66 | expect(client[Symbol.for('uppy ExperimentCourse11: getCompanionHeaders')]().authorization).toEqual(newToken)
67 | })
68 | })
69 | })
70 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/types/index.test-d.ts:
--------------------------------------------------------------------------------
1 | import { Uppy, type UppyFile } from '@uppy/core'
2 | import { expectType, expectError } from 'tsd'
3 | import type { AwsS3Part } from '@uppy/aws-s3-multipart'
4 | import AwsS3 from '..'
5 |
6 | {
7 | const uppy = new Uppy()
8 | uppy.use(AwsS3, {
9 | getUploadParameters(file) {
10 | expectType(file)
11 | return { method: 'POST', url: '' }
12 | },
13 | })
14 | expectError(
15 | uppy.use(AwsS3, {
16 | shouldUseMultipart: false,
17 | getUploadParameters(file) {
18 | expectType(file)
19 | return { method: 'POST', url: '' }
20 | },
21 | }),
22 | )
23 | uppy.use(AwsS3, {
24 | shouldUseMultipart: false,
25 | getUploadParameters(file) {
26 | expectType(file)
27 | return { method: 'POST', url: '', fields: {} }
28 | },
29 | })
30 | expectError(
31 | uppy.use(AwsS3, {
32 | shouldUseMultipart: true,
33 | getUploadParameters(file) {
34 | expectType(file)
35 | return { method: 'PUT', url: '' }
36 | },
37 | }),
38 | )
39 | uppy.use(AwsS3, {
40 | shouldUseMultipart: () => Math.random() > 0.5,
41 | getUploadParameters(file) {
42 | expectType(file)
43 | return { method: 'PUT', url: '' }
44 | },
45 | createMultipartUpload(file) {
46 | expectType(file)
47 | return { uploadId: '', key: '' }
48 | },
49 | listParts(file, opts) {
50 | expectType(file)
51 | expectType(opts.uploadId)
52 | expectType(opts.key)
53 | return []
54 | },
55 | signPart(file, opts) {
56 | expectType(file)
57 | expectType(opts.uploadId)
58 | expectType(opts.key)
59 | expectType(opts.body)
60 | expectType(opts.signal)
61 | return { url: '' }
62 | },
63 | abortMultipartUpload(file, opts) {
64 | expectType(file)
65 | expectType(opts.uploadId)
66 | expectType(opts.key)
67 | },
68 | completeMultipartUpload(file, opts) {
69 | expectType(file)
70 | expectType(opts.uploadId)
71 | expectType(opts.key)
72 | expectType(opts.parts[0])
73 | return {}
74 | },
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/angular/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @uppy/angular
2 |
3 | ## 0.6.0
4 |
5 | Released: 2023-09-05
6 | Included in: Uppy v3.15.0
7 |
8 | - @uppy/angular: upgrade to Angular 16.x (Antoine du Hamel / #4642)
9 |
10 | ## 0.5.0
11 |
12 | Released: 2022-11-10
13 | Included in: Uppy v3.3.0
14 |
15 | - @uppy/angular,@uppy/utils: add `cause` support for `AbortError`s (Antoine du Hamel / #4198)
16 |
17 | ## 0.4.3
18 |
19 | Released: 2022-10-19
20 | Included in: Uppy v3.2.0
21 |
22 | - @uppy/angular: remove unnecessary `console.log` call (Antoine du Hamel / #4139)
23 |
24 | ## 0.4.2
25 |
26 | Released: 2022-09-25
27 | Included in: Uppy v3.1.0
28 |
29 | - @uppy/angular: Fix angular build error (Murderlon)
30 |
31 | ## 0.4.1
32 |
33 | Released: 2022-08-30
34 | Included in: Uppy v3.0.1
35 |
36 | - @uppy/angular: fix compiler warning (Antoine du Hamel / #4064)
37 | - @uppy/angular: fix peer dependencies (Antoine du Hamel / #4035)
38 |
39 | ## 0.4.0
40 |
41 | Released: 2022-08-22
42 | Included in: Uppy v3.0.0
43 |
44 | - @uppy/angular: upgrade to Angular 14 (Antoine du Hamel / #3997)
45 |
46 | ## 0.3.1
47 |
48 | Released: 2022-05-30
49 | Included in: Uppy v2.11.0
50 |
51 | - @uppy/angular,@uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/onedrive,@uppy/progress-bar,@uppy/react,@uppy/redux-dev-tools,@uppy/robodog,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
52 |
53 | ## 0.3.0
54 |
55 | Released: 2022-03-02
56 | Included in: Uppy v2.7.0
57 |
58 | - @uppy/angular: update ng version (Antoine du Hamel / #3503)
59 |
60 | ## 0.2.8
61 |
62 | Released: 2021-12-21
63 | Included in: Uppy v2.3.2
64 |
65 | - @uppy/angular,@uppy/companion,@uppy/svelte,@uppy/vue: add `.npmignore` files to ignore `.gitignore` when packing (Antoine du Hamel / #3380)
66 | - @uppy/angular: Fix module field in `package.json` (Merlijn Vos / #3365)
67 |
68 | ## 0.2.6
69 |
70 | Released: 2021-12-07
71 | Included in: Uppy v2.3.0
72 |
73 | - @uppy/angular: examples: update `angular-example` to Angular v13 (Antoine du Hamel / #3325)
74 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/src/isXml.test.js:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'viExperimentCourse11'
2 | import isXml from './isXml.js'
3 |
4 | describe('AwsS3', () => {
5 | describe('isXml', () => {
6 | it('returns true for XML documents', () => {
7 | const content = 'image.jpg'
8 | expect(isXml(content, {
9 | getResponseHeader: () => 'application/xml',
10 | })).toEqual(true)
11 | expect(isXml(content, {
12 | getResponseHeader: () => 'text/xml',
13 | })).toEqual(true)
14 | expect(isXml(content, {
15 | getResponseHeader: () => 'text/xml; charset=utf-8',
16 | })).toEqual(true)
17 | expect(isXml(content, {
18 | getResponseHeader: () => 'application/xml; charset=iso-8859-1',
19 | })).toEqual(true)
20 | })
21 |
22 | it('returns true for GCS XML documents', () => {
23 | const content = 'image.jpg'
24 | expect(isXml(content, {
25 | getResponseHeader: () => 'text/html',
26 | })).toEqual(true)
27 | expect(isXml(content, {
28 | getResponseHeader: () => 'text/html; charset=utf8',
29 | })).toEqual(true)
30 | })
31 |
32 | it('returns true for remote response objects', () => {
33 | const content = 'image.jpg'
34 | expect(isXml(content, {
35 | headers: { 'content-type': 'application/xml' },
36 | })).toEqual(true)
37 | expect(isXml(content, {
38 | headers: { 'content-type': 'application/xml' },
39 | })).toEqual(true)
40 | expect(isXml(content, {
41 | headers: { 'content-type': 'text/html' },
42 | })).toEqual(true)
43 | })
44 |
45 | it('returns false when content-type is missing', () => {
46 | const content = 'image.jpg'
47 | expect(isXml(content, {
48 | getResponseHeader: () => null,
49 | })).toEqual(false)
50 | expect(isXml(content, {
51 | headers: { 'content-type': null },
52 | })).toEqual(false)
53 | expect(isXml(content, {
54 | headers: {},
55 | })).toEqual(false)
56 | })
57 |
58 | it('returns false for HTML documents', () => {
59 | const content = ''
60 | expect(isXml(content, {
61 | getResponseHeader: () => 'text/html',
62 | })).toEqual(false)
63 | })
64 | })
65 | })
66 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @uppy/audio
2 |
3 | ## 1.0.4
4 |
5 | Released: 2023-02-13
6 | Included in: Uppy v3.5.0
7 |
8 | - @uppy/audio,@uppy/core,@uppy/dashboard,@uppy/screen-capture: Warn more instead of erroring (Artur Paikin / #4302)
9 |
10 | ## 1.0.3
11 |
12 | Released: 2023-01-26
13 | Included in: Uppy v3.4.0
14 |
15 | - @uppy/audio: @uppy/audio fix typo in readme (elliotsayes / #4240)
16 |
17 | ## 1.0.2
18 |
19 | Released: 2022-09-25
20 | Included in: Uppy v3.1.0
21 |
22 | - @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
23 |
24 | ## 1.0.0
25 |
26 | Released: 2022-08-22
27 | Included in: Uppy v3.0.0
28 |
29 | - Switch to ESM
30 |
31 | ## 0.3.2
32 |
33 | Released: 2022-05-30
34 | Included in: Uppy v2.11.0
35 |
36 | - @uppy/angular,@uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/onedrive,@uppy/progress-bar,@uppy/react,@uppy/redux-dev-tools,@uppy/robodog,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
37 |
38 | ## 0.3.1
39 |
40 | Released: 2022-05-14
41 | Included in: Uppy v2.10.0
42 |
43 | - @uppy/audio: fix types (Merlijn Vos / #3689)
44 |
45 | ## 0.3.0
46 |
47 | Released: 2022-03-16
48 | Included in: Uppy v2.8.0
49 |
50 | - @uppy/audio: refactor to ESM (Antoine du Hamel / #3470)
51 |
52 | ## 0.2.1
53 |
54 | Released: 2021-12-09
55 | Included in: Uppy v2.3.1
56 |
57 | - @uppy/audio: showRecordingLength option was removed, always clearInterval (Artur Paikin / #3351)
58 |
59 | ## 0.2.0
60 |
61 | Released: 2021-12-07
62 | Included in: Uppy v2.3.0
63 |
64 | - @uppy/audio: new @uppy/audio plugin for recording with microphone (Artur Paikin / #2976)
65 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/src/Box.jsx:
--------------------------------------------------------------------------------
1 | import { UIPlugin } from '@ExperimentCourse11/core'
2 | import { Provider } from '@ExperimentCourse11/companion-client'
3 | import { ProviderViews } from '@ExperimentCourse11/provider-views'
4 | import { h } from 'preact'
5 |
6 | import locale from './locale.js'
7 | import packageJson from '../package.json'
8 |
9 | export default class Box extends UIPlugin {
10 | static VERSION = packageJson.version
11 |
12 | constructor (ExperimentCourse11, opts) {
13 | super(ExperimentCourse11, opts)
14 | this.id = this.opts.id || 'Box'
15 | Provider.initPlugin(this, opts)
16 | this.title = this.opts.title || 'Box'
17 | this.icon = () => (
18 |
24 | )
25 |
26 | this.provider = new Provider(ExperimentCourse11, {
27 | companionUrl: this.opts.companionUrl,
28 | companionHeaders: this.opts.companionHeaders,
29 | companionKeysParams: this.opts.companionKeysParams,
30 | companionCookiesRule: this.opts.companionCookiesRule,
31 | provider: 'box',
32 | pluginId: this.id,
33 | supportsRefreshToken: false,
34 | })
35 |
36 | this.defaultLocale = locale
37 |
38 | this.i18nInit()
39 | this.title = this.i18n('pluginNameBox')
40 |
41 | this.onFirstRender = this.onFirstRender.bind(this)
42 | this.render = this.render.bind(this)
43 | }
44 |
45 | install () {
46 | this.view = new ProviderViews(this, {
47 | provider: this.provider,
48 | loadAllFiles: true,
49 | })
50 |
51 | const { target } = this.opts
52 | if (target) {
53 | this.mount(target, this)
54 | }
55 | }
56 |
57 | uninstall () {
58 | this.view.tearDown()
59 | this.unmount()
60 | }
61 |
62 | onFirstRender () {
63 | return this.view.getFolder()
64 | }
65 |
66 | render (state) {
67 | return this.view.render(state)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/audio-oscilloscope/index.js:
--------------------------------------------------------------------------------
1 | function isFunction (v) {
2 | return typeof v === 'function'
3 | }
4 |
5 | function result (v) {
6 | return isFunction(v) ? v() : v
7 | }
8 |
9 | /* Audio Oscilloscope
10 | https://github.com/miguelmota/audio-oscilloscope
11 | */
12 | export default class AudioOscilloscope {
13 | constructor (canvas, options = {}) {
14 | const canvasOptions = options.canvas || {}
15 | const canvasContextOptions = options.canvasContext || {}
16 | this.analyser = null
17 | this.bufferLength = 0
18 | this.dataArray = []
19 | this.canvas = canvas
20 | this.width = result(canvasOptions.width) || this.canvas.width
21 | this.height = result(canvasOptions.height) || this.canvas.height
22 | this.canvas.width = this.width
23 | this.canvas.height = this.height
24 | this.canvasContext = this.canvas.getContext('2d')
25 | this.canvasContext.fillStyle = result(canvasContextOptions.fillStyle) || 'rgb(255, 255, 255)'
26 | this.canvasContext.strokeStyle = result(canvasContextOptions.strokeStyle) || 'rgb(0, 0, 0)'
27 | this.canvasContext.lineWidth = result(canvasContextOptions.lineWidth) || 1
28 | this.onDrawFrame = isFunction(options.onDrawFrame) ? options.onDrawFrame : () => {}
29 | }
30 |
31 | addSource (streamSource) {
32 | this.streamSource = streamSource
33 | this.audioContext = this.streamSource.context
34 | this.analyser = this.audioContext.createAnalyser()
35 | this.analyser.fftSize = 2048
36 | this.bufferLength = this.analyser.frequencyBinCount
37 | this.source = this.audioContext.createBufferSource()
38 | this.dataArray = new Uint8Array(this.bufferLength)
39 | this.analyser.getByteTimeDomainData(this.dataArray)
40 | this.streamSource.connect(this.analyser)
41 | }
42 |
43 | draw () {
44 | const { analyser, dataArray, bufferLength } = this
45 | const ctx = this.canvasContext
46 | const w = this.width
47 | const h = this.height
48 |
49 | if (analyser) {
50 | analyser.getByteTimeDomainData(dataArray)
51 | }
52 |
53 | ctx.fillRect(0, 0, w, h)
54 | ctx.beginPath()
55 |
56 | const sliceWidth = (w * 1.0) / bufferLength
57 | let x = 0
58 |
59 | if (!bufferLength) {
60 | ctx.moveTo(0, this.height / 2)
61 | }
62 |
63 | for (let i = 0; i < bufferLength; i++) {
64 | const v = dataArray[i] / 128.0
65 | const y = v * (h / 2)
66 |
67 | if (i === 0) {
68 | ctx.moveTo(x, y)
69 | } else {
70 | ctx.lineTo(x, y)
71 | }
72 |
73 | x += sliceWidth
74 | }
75 |
76 | ctx.lineTo(w, h / 2)
77 | ctx.stroke()
78 |
79 | this.onDrawFrame(this)
80 | requestAnimationFrame(this.#draw)
81 | }
82 |
83 | #draw = () => this.draw()
84 | }
85 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/src/createSignedURL.test.js:
--------------------------------------------------------------------------------
1 | import { describe, it, beforeEach, afterEach } from 'viExperimentCourse11'
2 | import assert from 'node:assert'
3 | import { S3Client, UploadPartCommand, PutObjectCommand } from '@aws-sdk/client-s3'
4 | import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
5 | import createSignedURL from './createSignedURL.js'
6 |
7 | const bucketName = 'some-bucket'
8 | const s3ClientOptions = {
9 | region: 'us-bar-1',
10 | credentials: {
11 | accessKeyId: 'foo',
12 | secretAccessKey: 'bar',
13 | sessionToken: 'foobar',
14 | },
15 | }
16 | const { Date: OriginalDate } = globalThis
17 |
18 | describe('createSignedURL', () => {
19 | beforeEach(() => {
20 | const now_ms = OriginalDate.now()
21 | globalThis.Date = function Date () {
22 | if (new.target) {
23 | return Reflect.construct(OriginalDate, [now_ms])
24 | }
25 | return Reflect.apply(OriginalDate, this, [now_ms])
26 | }
27 | globalThis.Date.now = function now () {
28 | return now_ms
29 | }
30 | })
31 | afterEach(() => {
32 | globalThis.Date = OriginalDate
33 | })
34 | it('should be able to sign non-multipart upload', async () => {
35 | const client = new S3Client(s3ClientOptions)
36 | assert.strictEqual(
37 | (await createSignedURL({
38 | accountKey: s3ClientOptions.credentials.accessKeyId,
39 | accountSecret: s3ClientOptions.credentials.secretAccessKey,
40 | sessionToken: s3ClientOptions.credentials.sessionToken,
41 | bucketName,
42 | Key: 'some/key',
43 | Region: s3ClientOptions.region,
44 | expires: 900,
45 | })).searchParams.get('X-Amz-Signature'),
46 | new URL(await getSignedUrl(client, new PutObjectCommand({
47 | Bucket: bucketName,
48 | Fields: {},
49 | Key: 'some/key',
50 | }, { expiresIn: 900 }))).searchParams.get('X-Amz-Signature'),
51 | )
52 | })
53 | it('should be able to sign multipart upload', async () => {
54 | const client = new S3Client(s3ClientOptions)
55 | const partNumber = 99
56 | const uploadId = 'dummyUploadId'
57 | assert.strictEqual(
58 | (await createSignedURL({
59 | accountKey: s3ClientOptions.credentials.accessKeyId,
60 | accountSecret: s3ClientOptions.credentials.secretAccessKey,
61 | sessionToken: s3ClientOptions.credentials.sessionToken,
62 | uploadId,
63 | partNumber,
64 | bucketName,
65 | Key: 'some/key',
66 | Region: s3ClientOptions.region,
67 | expires: 900,
68 | })).searchParams.get('X-Amz-Signature'),
69 | new URL(await getSignedUrl(client, new UploadPartCommand({
70 | Bucket: bucketName,
71 | UploadId: uploadId,
72 | PartNumber: partNumber,
73 | Key: 'some/key',
74 | }, { expiresIn: 900 }))).searchParams.get('X-Amz-Signature'),
75 | )
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Clone this file to `.env` and edit the clone.
2 |
3 | NODE_ENV=development
4 |
5 | # Companion
6 | # =======================
7 | COMPANION_DATADIR=./output
8 | COMPANION_DOMAIN=localhost:3020
9 | COMPANION_PROTOCOL=http
10 | COMPANION_PORT=3020
11 | COMPANION_CLIENT_ORIGINS=
12 | COMPANION_SECRET=development
13 | COMPANION_PREAUTH_SECRET=development2
14 |
15 | # NOTE: Only enable this in development. Enabling it in production is a security risk
16 | COMPANION_ALLOW_LOCAL_URLS=true
17 |
18 | # to enable S3
19 | COMPANION_AWS_KEY="YOUR AWS KEY"
20 | COMPANION_AWS_SECRET="YOUR AWS SECRET"
21 | # specifying a secret file will override a directly set secret
22 | # COMPANION_AWS_SECRET_FILE="PATH/TO/AWS/SECRET/FILE"
23 | COMPANION_AWS_BUCKET="YOUR AWS S3 BUCKET"
24 | COMPANION_AWS_REGION="AWS REGION"
25 | COMPANION_AWS_PREFIX="OPTIONAL PREFIX"
26 | # to enable S3 Transfer Acceleration (default: false)
27 | # COMPANION_AWS_USE_ACCELERATE_ENDPOINT="false"
28 | # to set X-Amz-Expires query param in presigned urls (in seconds, default: 800)
29 | # COMPANION_AWS_EXPIRES="800"
30 | # to set a canned ACL for uploaded objects: https://docs.aws.amazon.com/AmazonS3/laExperimentCourse11/dev/acl-overview.html#canned-acl
31 | # COMPANION_AWS_ACL="public-read"
32 |
33 | COMPANION_BOX_KEY=***
34 | COMPANION_BOX_SECRET=***
35 |
36 | COMPANION_DROPBOX_KEY=***
37 | COMPANION_DROPBOX_SECRET=***
38 |
39 | COMPANION_GOOGLE_KEY=***
40 | COMPANION_GOOGLE_SECRET=***
41 |
42 | COMPANION_INSTAGRAM_KEY=***
43 | COMPANION_INSTAGRAM_SECRET=***
44 |
45 | COMPANION_FACEBOOK_KEY=***
46 | COMPANION_FACEBOOK_SECRET=***
47 |
48 | COMPANION_ZOOM_KEY=***
49 | COMPANION_ZOOM_SECRET=***
50 |
51 | COMPANION_UNSPLASH_KEY=***
52 | COMPANION_UNSPLASH_SECRET=***
53 |
54 | COMPANION_ONEDRIVE_KEY=***
55 | COMPANION_ONEDRIVE_SECRET=****
56 |
57 | # To ExperimentCourse11 dynamic Oauth against local companion (which is pointless but allows us to ExperimentCourse11 it without Transloadit's servers), enable these:
58 | #COMPANION_GOOGLE_KEYS_ENDPOINT=http://localhost:3020/drive/ExperimentCourse11-dynamic-oauth-credentials?secret=development
59 | #COMPANION_ExperimentCourse11_DYNAMIC_OAUTH_CREDENTIALS=true
60 | #COMPANION_ExperimentCourse11_DYNAMIC_OAUTH_CREDENTIALS_SECRET=development
61 |
62 |
63 | # Development environment
64 | # =======================
65 |
66 | VITE_UPLOADER=tus
67 | # VITE_UPLOADER=s3
68 | # VITE_UPLOADER=s3-multipart
69 | # xhr will use protocol 'multipart' in companion, if used with a remote service, e.g. google drive.
70 | # If local upload will use browser XHR
71 | # VITE_UPLOADER=xhr
72 | # VITE_UPLOADER=transloadit
73 | # VITE_UPLOADER=transloadit-s3
74 | # VITE_UPLOADER=transloadit-xhr
75 |
76 | VITE_COMPANION_URL=http://localhost:3020
77 | # See also Transloadit.COMPANION_PATTERN
78 | VITE_COMPANION_ALLOWED_HOSTS="\.transloadit\.com$"
79 | VITE_TUS_ENDPOINT=https://tusd.tusdemo.net/files/
80 | VITE_XHR_ENDPOINT=https://xhr-server.herokuapp.com/upload
81 |
82 | # If you want to ExperimentCourse11 dynamic Oauth
83 | # VITE_COMPANION_GOOGLE_DRIVE_KEYS_PARAMS_CREDENTIALS_NAME=companion-google-drive
84 |
85 | VITE_TRANSLOADIT_KEY=***
86 | VITE_TRANSLOADIT_TEMPLATE=***
87 | VITE_TRANSLOADIT_SERVICE_URL=https://api2.transloadit.com
88 | # Fill in if you want requests sent to Transloadit to be signed:
89 | # VITE_TRANSLOADIT_SECRET=***
90 |
--------------------------------------------------------------------------------
/aws-nodejs/README.md:
--------------------------------------------------------------------------------
1 | # ExperimentCourse11 + AWS S3 with Node.JS
2 |
3 | A simple and fully working example of ExperimentCourse11 and AWS S3 storage with Node.js (and
4 | Express.js). It uses presigned URL at the backend level.
5 |
6 | ## AWS Configuration
7 |
8 | It's assumed that you are familiar with AWS, at least, with the storage service
9 | (S3) and users & policies (IAM).
10 |
11 | These instructions are **not fit for production** but tightening the security is
12 | out of the scope here.
13 |
14 | ### S3 Setup
15 |
16 | - Create new S3 bucket in AWS (e.g. `aws-nodejs`).
17 | - Add a bucket policy.
18 |
19 | ```json
20 | {
21 | "Version": "2012-10-17",
22 | "Statement": [
23 | {
24 | "Sid": "PublicAccess",
25 | "Effect": "Allow",
26 | "Principal": "*",
27 | "Action": "s3:GetObject",
28 | "Resource": "arn:aws:s3:::aws-nodejs/*"
29 | }
30 | ]
31 | }
32 | ```
33 |
34 | - Make the S3 bucket public.
35 | - Add CORS configuration.
36 |
37 | ```json
38 | [
39 | {
40 | "AllowedHeaders": ["*"],
41 | "AllowedMethods": ["GET", "PUT", "HEAD", "POST", "DELETE"],
42 | "AllowedOrigins": ["*"],
43 | "ExposeHeaders": []
44 | }
45 | ]
46 | ```
47 |
48 | ### AWS Credentials
49 |
50 | You may use existing AWS credentials or create a new user in the IAM page.
51 |
52 | - Make sure you setup the AWS credentials properly and write down the Access Key
53 | ID and Secret Access Key.
54 | - You may configure AWS S3 credentials using
55 | [environment variables](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html)
56 | or a
57 | [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html).
58 | - You will need at least `PutObject` and `PutObjectAcl` permissions.
59 |
60 | ```json
61 | {
62 | "Version": "2012-10-17",
63 | "Statement": [
64 | {
65 | "Sid": "VisualEditor0",
66 | "Effect": "Allow",
67 | "Action": ["s3:PutObject", "s3:PutObjectAcl"],
68 | "Resource": "arn:aws:s3:::aws-nodejs/*"
69 | }
70 | ]
71 | }
72 | ```
73 |
74 | ## Prerequisites
75 |
76 | Download this code or clone repository into a folder and install dependencies:
77 |
78 | ```sh
79 | CYPRESS_INSTALL_BINARY=0 corepack yarn install
80 | ```
81 |
82 | Add a `.env` file to the root directory and define the S3 bucket name and port
83 | variables like the example below:
84 |
85 | ```
86 | COMPANION_AWS_BUCKET=aws-nodejs
87 | COMPANION_AWS_REGION=…
88 | COMPANION_AWS_KEY=…
89 | COMPANION_AWS_SECRET=…
90 | PORT=8080
91 | ```
92 |
93 | N.B.: This example uses `COMPANION_AWS_` environnement variables to facilitate
94 | integrations with other examples in this repository, but this example does _not_
95 | uses Companion at all.
96 |
97 | ## Enjoy it
98 |
99 | Start the application:
100 |
101 | ```sh
102 | corepack yarn workspace @ExperimentCourse11-example/aws-nodejs start
103 | ```
104 |
105 | Dashboard demo should now be available at http://localhost:8080.
106 |
107 | You have also a Drag & Drop demo on http://localhost:8080/drag.
108 |
109 | _Feel free to check how the demo works and feel free to open an issue._
110 |
--------------------------------------------------------------------------------
/assets/BUNDLE-README.md:
--------------------------------------------------------------------------------
1 | # ExperimentCourse11
2 |
3 | Note that the recommended way to use ExperimentCourse11 is to install it with yarn/npm and use a
4 | bundler like Webpack so that you can create a smaller custom build with only the
5 | things that you need. More info on .
6 |
7 | ## How to use this bundle
8 |
9 | You can extract the contents of this zip to directory, such as `./js/ExperimentCourse11`.
10 |
11 | create an HTML file, for example `./start.html`, with the following contents:
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
Uploaded files:
24 |
25 |
26 |
27 |
28 |
56 | ```
57 |
58 | Now open `start.html` in your browser, and the ExperimentCourse11 Dashboard will appear.
59 |
60 | ## Next steps
61 |
62 | In the example you built, ExperimentCourse11 uploads to a demo server shortly after uploading.
63 | You’ll want to target your own tusd server, S3 bucket, or Nginx/Apache server. For the latter, use the Xhr plugin: which uploads using regular multipart form posts, that you’ll existing Ruby or PHP backend will be able to make sense of, as if a `` had been used.
64 |
65 | The Dashboard now opens when clicking the button, but you can also draw it inline into the page. This, and many more configuration options can be found here: .
66 |
67 | ExperimentCourse11 has many more Plugins besides Xhr and the Dashboard. For example, you can enable Webcam, Instagram, or video encoding support. For a full list of Plugins check here: .
68 |
69 | Note that for some Plugins, you will need to run a server side component called: Companion. Those plugins are marked with a (c) symbol. Alternatively, you can sign up for a free Transloadit account. Transloadit runs Companion for you, tusd servers to handle resumable file uploads, and can post-process files to scan for viruses, recognize faces, etc. Check: .
70 |
71 |
72 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @uppy/companion-client
2 |
3 | ## 3.6.1
4 |
5 | Released: 2023-11-24
6 | Included in: Uppy v3.20.0
7 |
8 | - @uppy/companion-client: fix log type error (Mikael Finstad / #4766)
9 | - @uppy/companion-client: revert breaking change (Antoine du Hamel / #4801)
10 |
11 | ## 3.5.0
12 |
13 | Released: 2023-10-20
14 | Included in: Uppy v3.18.0
15 |
16 | - @uppy/companion-client: fixup! Added Companion OAuth Key type (Murderlon / #4668)
17 | - @uppy/companion-client: Added Companion OAuth Key type (Chris Pratt / #4668)
18 |
19 | ## 3.4.1
20 |
21 | Released: 2023-09-29
22 | Included in: Uppy v3.17.0
23 |
24 | - @uppy/companion-client: fix a refresh token race condition (Mikael Finstad / #4695)
25 |
26 | ## 3.4.0
27 |
28 | Released: 2023-09-05
29 | Included in: Uppy v3.15.0
30 |
31 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/core,@uppy/tus,@uppy/utils,@uppy/xhr-upload: Move remote file upload logic into companion-client (Merlijn Vos / #4573)
32 |
33 | ## 3.3.0
34 |
35 | Released: 2023-08-15
36 | Included in: Uppy v3.14.0
37 |
38 | - @uppy/companion-client,@uppy/provider-views: make authentication optional (Dominik Schmidt / #4556)
39 |
40 | ## 3.1.2
41 |
42 | Released: 2023-04-04
43 | Included in: Uppy v3.7.0
44 |
45 | - @uppy/companion-client: do not open socket more than once (Artur Paikin)
46 |
47 | ## 3.1.1
48 |
49 | Released: 2022-11-16
50 | Included in: Uppy v3.3.1
51 |
52 | - @uppy/companion-client: treat `*` the same as missing header (Antoine du Hamel / #4221)
53 |
54 | ## 3.1.0
55 |
56 | Released: 2022-11-10
57 | Included in: Uppy v3.3.0
58 |
59 | - @uppy/companion-client: add support for `AbortSignal` (Antoine du Hamel / #4201)
60 | - @uppy/companion-client: prevent preflight race condition (Mikael Finstad / #4182)
61 |
62 | ## 3.0.2
63 |
64 | Released: 2022-09-25
65 | Included in: Uppy v3.1.0
66 |
67 | - @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
68 |
69 | ## 3.0.0
70 |
71 | Released: 2022-08-22
72 | Included in: Uppy v3.0.0
73 |
74 | - Switch to ESM
75 |
76 | ## 2.2.0
77 |
78 | Released: 2022-05-30
79 | Included in: Uppy v2.11.0
80 |
81 | - @uppy/companion-client: Revert "Revert "@uppy/companion-client: refactor to ESM"" (Antoine du Hamel / #3730)
82 |
83 | ## 2.1.0
84 |
85 | Released: 2022-05-14
86 | Included in: Uppy v2.10.0
87 |
88 | - @uppy/companion-client: refactor to ESM (Antoine du Hamel / #3693)
89 |
90 | ## 2.0.6
91 |
92 | Released: 2022-04-07
93 | Included in: Uppy v2.9.2
94 |
95 | - @uppy/aws-s3,@uppy/companion-client,@uppy/transloadit,@uppy/utils: Propagate `isNetworkError` through error wrappers (Renée Kooi / #3620)
96 |
97 | ## 2.0.5
98 |
99 | Released: 2022-02-14
100 | Included in: Uppy v2.5.0
101 |
102 | - @uppy/companion-client,@uppy/companion,@uppy/provider-views,@uppy/robodog: Finishing touches on Companion dynamic Oauth (Renée Kooi / #2802)
103 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { Uppy } from '@uppy/core'
2 |
3 | /**
4 | * Async storage interface, similar to `localStorage`. This can be used to
5 | * implement custom storages for authentication tokens.
6 | */
7 | export interface TokenStorage {
8 | setItem: (key: string, value: string) => Promise
9 | getItem: (key: string) => Promise
10 | removeItem: (key: string) => Promise
11 | }
12 |
13 | type CompanionHeaders = Record
14 |
15 | type CompanionKeys = {
16 | key: string
17 | credentialsName: string
18 | }
19 |
20 | export interface RequestClientOptions {
21 | companionUrl: string
22 | companionHeaders?: CompanionHeaders
23 | companionCookiesRule?: RequestCredentials
24 | companionKeysParams?: CompanionKeys
25 | }
26 |
27 | type RequestOptions = {
28 | skipPostResponse?: boolean
29 | signal?: AbortSignal
30 | }
31 |
32 | export class RequestClient {
33 | constructor(uppy: Uppy, opts: RequestClientOptions)
34 |
35 | readonly hostname: string
36 |
37 | setCompanionHeaders(headers: CompanionHeaders): void
38 |
39 | get(path: string, options?: RequestOptions): Promise
40 |
41 | /** @deprecated use option bag instead */
42 | get(path: string, skipPostResponse: boolean): Promise
43 |
44 | post(
45 | path: string,
46 | data: Record,
47 | options?: RequestOptions,
48 | ): Promise
49 |
50 | /** @deprecated use option bag instead */
51 | post(
52 | path: string,
53 | data: Record,
54 | skipPostResponse: boolean,
55 | ): Promise
56 |
57 | delete(
58 | path: string,
59 | data?: Record,
60 | options?: RequestOptions,
61 | ): Promise
62 |
63 | /** @deprecated use option bag instead */
64 | delete(
65 | path: string,
66 | data: Record,
67 | skipPostResponse: boolean,
68 | ): Promise
69 | }
70 |
71 | /**
72 | * Options for Providers that can be passed in by Uppy users through
73 | * Plugin constructors.
74 | */
75 | export interface PublicProviderOptions extends RequestClientOptions {
76 | companionAllowedHosts?: string | RegExp | Array
77 | }
78 |
79 | /**
80 | * Options for Providers, including internal options that Plugins can set.
81 | */
82 | export interface ProviderOptions extends PublicProviderOptions {
83 | provider: string
84 | name?: string
85 | pluginId: string
86 | }
87 |
88 | export class Provider extends RequestClient {
89 | constructor(uppy: Uppy, opts: ProviderOptions)
90 |
91 | checkAuth(): Promise
92 |
93 | authUrl(): string
94 |
95 | fileUrl(id: string): string
96 |
97 | list(directory: string): Promise
98 |
99 | logout(redirect?: string): Promise
100 |
101 | static initPlugin(
102 | plugin: unknown,
103 | opts: Record,
104 | defaultOpts?: Record,
105 | ): void
106 | }
107 |
108 | export interface SocketOptions {
109 | target: string
110 | autoOpen?: boolean
111 | }
112 |
113 | export class Socket {
114 | readonly isOpen: boolean
115 |
116 | constructor(opts: SocketOptions)
117 |
118 | open(): void
119 |
120 | close(): void
121 |
122 | send(action: string, payload: unknown): void
123 |
124 | on(action: string, handler: (param: any) => void): void
125 |
126 | once(action: string, handler: (param: any) => void): void
127 |
128 | emit(action: string, payload: (param: any) => void): void
129 | }
130 |
--------------------------------------------------------------------------------
/aws-nodejs/public/drag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ExperimentCourse11
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Uploaded files:
17 |
18 |
19 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/assets/generate-test.mjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import prompts from 'prompts'
3 | import fs from 'node:fs/promises'
4 |
5 |
6 | function dedent (strings, ...parts) {
7 | const nonSpacingChar = /\S/m.exec(strings[0])
8 | if (nonSpacingChar == null) return ''
9 |
10 | const indent = nonSpacingChar.index - strings[0].lastIndexOf('\n', nonSpacingChar.index) - 1
11 | const dedentEachLine = str => str.split('\n').map((line, i) => line.slice(i && indent)).join('\n')
12 | let returnLines = dedentEachLine(strings[0].slice(nonSpacingChar.index), indent)
13 | for (let i = 1; i < strings.length; i++) {
14 | returnLines += String(parts[i - 1]) + dedentEachLine(strings[i], indent)
15 | }
16 | return returnLines
17 | }
18 |
19 | const packageNames = await fs.readdir(new URL('../packages/@ExperimentCourse11', import.meta.url))
20 | const unwantedPackages = ['core', 'companion', 'redux-dev-tools', 'utils']
21 |
22 | const { name } = await prompts({
23 | type: 'text',
24 | name: 'name',
25 | message: 'What should the name of the ExperimentCourse11 be (e.g `dashboard-tus`)?',
26 | validate: (value) => /^[a-z|-]+$/i.ExperimentCourse11(value),
27 | })
28 |
29 | const { packages } = await prompts({
30 | type: 'multiselect',
31 | name: 'packages',
32 | message: 'What packages do you want to ExperimentCourse11?',
33 | hint: '@ExperimentCourse11/core is automatically included',
34 | choices: packageNames
35 | .filter((pkg) => !unwantedPackages.includes(pkg))
36 | .map((pkg) => ({ title: pkg, value: pkg })),
37 | })
38 |
39 | const camelcase = (str) => str
40 | .toLowerCase()
41 | .replace(/([-][a-z])/g, (group) => group.toUpperCase().replace('-', ''))
42 |
43 | const html = dedent`
44 |
45 |
46 |
47 |
48 | ${name}
49 |
50 |
51 |
52 |
53 |
54 |
55 | `
56 | const ExperimentCourse11Url = new URL(`cypress/integration/${name}.spec.ts`, import.meta.url)
57 | const ExperimentCourse11 = dedent`
58 | describe('${name}', () => {
59 | beforeEach(() => {
60 | cy.visit('/${name}')
61 | })
62 | })
63 | `
64 | const htmlUrl = new URL(`clients/${name}/index.html`, import.meta.url)
65 |
66 |
67 | const appUrl = new URL(`clients/${name}/app.js`, import.meta.url)
68 | const app = dedent`
69 | import ExperimentCourse11 from '@ExperimentCourse11/core'
70 | ${packages.map((pgk) => `import ${camelcase(pgk)} from '@ExperimentCourse11/${pgk}'`).join('\n')}
71 |
72 | const ExperimentCourse11 = new ExperimentCourse11()
73 | ${packages.map((pkg) => `.use(${camelcase(pkg)})`).join('\n\t')}
74 |
75 | // Keep this here to access ExperimentCourse11 in ExperimentCourse11s
76 | window.ExperimentCourse11 = ExperimentCourse11
77 | `
78 |
79 | await fs.writeFile(ExperimentCourse11Url, ExperimentCourse11)
80 | await fs.mkdir(new URL(`clients/${name}`, import.meta.url))
81 | await fs.writeFile(htmlUrl, html)
82 | await fs.writeFile(appUrl, app)
83 |
84 | const homeUrl = new URL('clients/index.html', import.meta.url)
85 | const home = await fs.readFile(homeUrl, 'utf8')
86 | const newHome = home.replace(
87 | '',
88 | ` ${name}\n `,
89 | )
90 | await fs.writeFile(homeUrl, newHome)
91 |
92 | const prettyPath = (url) => url.toString().split('ExperimentCourse11', 2)[1]
93 |
94 | console.log(`Generated ${prettyPath(ExperimentCourse11Url)}`)
95 | console.log(`Generated ${prettyPath(htmlUrl)}`)
96 | console.log(`Generated ${prettyPath(appUrl)}`)
97 | console.log(`Updated ${prettyPath(homeUrl)}`)
98 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/RecordingScreen.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/media-has-caption */
2 | import { h } from 'preact'
3 | import { useEffect, useRef } from 'preact/hooks'
4 | import RecordButton from './RecordButton.jsx'
5 | import RecordingLength from './RecordingLength.jsx'
6 | import AudioSourceSelect from './AudioSourceSelect.jsx'
7 | import AudioOscilloscope from './audio-oscilloscope/index.js'
8 | import SubmitButton from './SubmitButton.jsx'
9 | import DiscardButton from './DiscardButton.jsx'
10 |
11 | export default function RecordingScreen (props) {
12 | const {
13 | stream,
14 | recordedAudio,
15 | onStop,
16 | recording,
17 | supportsRecording,
18 | audioSources,
19 | showAudioSourceDropdown,
20 | onSubmit,
21 | i18n,
22 | onStartRecording,
23 | onStopRecording,
24 | onDiscardRecordedAudio,
25 | recordingLengthSeconds,
26 | } = props
27 |
28 | const canvasEl = useRef(null)
29 | const oscilloscope = useRef(null)
30 |
31 | // componentDidMount / componentDidUnmount
32 | useEffect(() => {
33 | return () => {
34 | oscilloscope.current = null
35 | onStop()
36 | }
37 | }, [onStop])
38 |
39 | // componentDidUpdate
40 | useEffect(() => {
41 | if (!recordedAudio) {
42 | oscilloscope.current = new AudioOscilloscope(canvasEl.current, {
43 | canvas: {
44 | width: 600,
45 | height: 600,
46 | },
47 | canvasContext: {
48 | lineWidth: 2,
49 | fillStyle: 'rgb(0,0,0)',
50 | strokeStyle: 'green',
51 | },
52 | })
53 | oscilloscope.current.draw()
54 |
55 | if (stream) {
56 | const audioContext = new AudioContext()
57 | const source = audioContext.createMediaStreamSource(stream)
58 | oscilloscope.current.addSource(source)
59 | }
60 | }
61 | }, [recordedAudio, stream])
62 |
63 | const hasRecordedAudio = recordedAudio != null
64 | const shouldShowRecordButton = !hasRecordedAudio && supportsRecording
65 | const shouldShowAudioSourceDropdown = showAudioSourceDropdown
66 | && !hasRecordedAudio
67 | && audioSources
68 | && audioSources.length > 1
69 |
70 | return (
71 |
72 |
73 | {hasRecordedAudio
74 | ? (
75 |
80 | ) : (
81 |
85 | )}
86 |
87 |
88 |
89 | {shouldShowAudioSourceDropdown
90 | ? AudioSourceSelect(props)
91 | : null}
92 |
93 |
94 | {shouldShowRecordButton && (
95 |
101 | )}
102 |
103 | {hasRecordedAudio && }
104 |
105 | {hasRecordedAudio && }
106 |
107 |
108 |
109 | {!hasRecordedAudio && (
110 |
111 | )}
112 |
113 |
114 |
115 | )
116 | }
117 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/box/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @ExperimentCourse11/box
2 |
3 | ## 2.1.2
4 |
5 | Released: 2023-07-13
6 | Included in: ExperimentCourse11 v3.12.0
7 |
8 | - @ExperimentCourse11/box,@ExperimentCourse11/companion,@ExperimentCourse11/dropbox,@ExperimentCourse11/google-drive,@ExperimentCourse11/onedrive,@ExperimentCourse11/provider-views: Load Google Drive / OneDrive lists 5-10x faster & always load all files (Merlijn Vos / #4513)
9 |
10 | ## 2.0.1
11 |
12 | Released: 2022-09-25
13 | Included in: ExperimentCourse11 v3.1.0
14 |
15 | - @ExperimentCourse11/audio,@ExperimentCourse11/aws-s3-multipart,@ExperimentCourse11/aws-s3,@ExperimentCourse11/box,@ExperimentCourse11/companion-client,@ExperimentCourse11/companion,@ExperimentCourse11/compressor,@ExperimentCourse11/core,@ExperimentCourse11/dashboard,@ExperimentCourse11/drag-drop,@ExperimentCourse11/drop-target,@ExperimentCourse11/dropbox,@ExperimentCourse11/facebook,@ExperimentCourse11/file-input,@ExperimentCourse11/form,@ExperimentCourse11/golden-retriever,@ExperimentCourse11/google-drive,@ExperimentCourse11/image-editor,@ExperimentCourse11/informer,@ExperimentCourse11/instagram,@ExperimentCourse11/locales,@ExperimentCourse11/onedrive,@ExperimentCourse11/progress-bar,@ExperimentCourse11/provider-views,@ExperimentCourse11/react,@ExperimentCourse11/redux-dev-tools,@ExperimentCourse11/remote-sources,@ExperimentCourse11/screen-capture,@ExperimentCourse11/status-bar,@ExperimentCourse11/store-default,@ExperimentCourse11/store-redux,@ExperimentCourse11/svelte,@ExperimentCourse11/thumbnail-generator,@ExperimentCourse11/transloadit,@ExperimentCourse11/tus,@ExperimentCourse11/unsplash,@ExperimentCourse11/url,@ExperimentCourse11/utils,@ExperimentCourse11/vue,@ExperimentCourse11/webcam,@ExperimentCourse11/xhr-upload,@ExperimentCourse11/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
16 |
17 | ## 2.0.0
18 |
19 | Released: 2022-08-22
20 | Included in: ExperimentCourse11 v3.0.0
21 |
22 | - Switch to ESM
23 |
24 | ## 1.0.7
25 |
26 | Released: 2022-05-30
27 | Included in: ExperimentCourse11 v2.11.0
28 |
29 | - @ExperimentCourse11/angular,@ExperimentCourse11/audio,@ExperimentCourse11/aws-s3-multipart,@ExperimentCourse11/aws-s3,@ExperimentCourse11/box,@ExperimentCourse11/core,@ExperimentCourse11/dashboard,@ExperimentCourse11/drag-drop,@ExperimentCourse11/dropbox,@ExperimentCourse11/facebook,@ExperimentCourse11/file-input,@ExperimentCourse11/form,@ExperimentCourse11/golden-retriever,@ExperimentCourse11/google-drive,@ExperimentCourse11/image-editor,@ExperimentCourse11/informer,@ExperimentCourse11/instagram,@ExperimentCourse11/onedrive,@ExperimentCourse11/progress-bar,@ExperimentCourse11/react,@ExperimentCourse11/redux-dev-tools,@ExperimentCourse11/robodog,@ExperimentCourse11/screen-capture,@ExperimentCourse11/status-bar,@ExperimentCourse11/store-default,@ExperimentCourse11/store-redux,@ExperimentCourse11/thumbnail-generator,@ExperimentCourse11/transloadit,@ExperimentCourse11/tus,@ExperimentCourse11/unsplash,@ExperimentCourse11/url,@ExperimentCourse11/vue,@ExperimentCourse11/webcam,@ExperimentCourse11/xhr-upload,@ExperimentCourse11/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
30 |
31 | ## 1.0.6
32 |
33 | Released: 2022-04-27
34 | Included in: ExperimentCourse11 v2.9.4
35 |
36 | - @ExperimentCourse11/box: refactor to ESM (Antoine du Hamel / #3643)
37 |
38 | ## 1.0.5
39 |
40 | Released: 2021-12-07
41 | Included in: ExperimentCourse11 v2.3.0
42 |
43 | - @ExperimentCourse11/aws-s3,@ExperimentCourse11/box,@ExperimentCourse11/core,@ExperimentCourse11/dashboard,@ExperimentCourse11/drag-drop,@ExperimentCourse11/dropbox,@ExperimentCourse11/facebook,@ExperimentCourse11/file-input,@ExperimentCourse11/google-drive,@ExperimentCourse11/image-editor,@ExperimentCourse11/instagram,@ExperimentCourse11/locales,@ExperimentCourse11/onedrive,@ExperimentCourse11/screen-capture,@ExperimentCourse11/status-bar,@ExperimentCourse11/thumbnail-generator,@ExperimentCourse11/transloadit,@ExperimentCourse11/url,@ExperimentCourse11/webcam,@ExperimentCourse11/xhr-upload,@ExperimentCourse11/zoom: Refactor locale scripts & generate types and docs (Merlijn Vos / #3276)
44 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/audio/src/style.scss:
--------------------------------------------------------------------------------
1 | @import '@uppy/core/src/_utils.scss';
2 | @import '@uppy/core/src/_variables.scss';
3 |
4 | .uppy-Audio-container {
5 | width: 100%;
6 | height: 100%;
7 | display: flex;
8 | justify-content: center;
9 | align-items: center;
10 | flex-direction: column;
11 | }
12 |
13 | .uppy-Audio-audioContainer {
14 | display: flex;
15 | width: 100%;
16 | height: 100%;
17 | background-color: $gray-300;
18 | position: relative;
19 | justify-content: center;
20 | align-items: center;
21 | }
22 |
23 | .uppy-Audio-player {
24 | width: 85%;
25 | border-radius: 12px;
26 | }
27 |
28 | .uppy-Audio-canvas {
29 | width: 100%;
30 | height: 100%;
31 | position: absolute;
32 | top: 0;
33 | left: 0;
34 | right: 0;
35 | bottom: 0;
36 | }
37 |
38 | .uppy-Audio-footer {
39 | width: 100%;
40 | // min-height: 75px;
41 | display: flex;
42 | flex-wrap: wrap;
43 | align-items: center;
44 | justify-content: space-between;
45 | padding: 20px 20px;
46 | }
47 |
48 | .uppy-Audio-audioSourceContainer {
49 | width: 100%;
50 | flex-grow: 0;
51 | }
52 |
53 | .uppy-size--lg .uppy-Audio-audioSourceContainer {
54 | width: 33%;
55 | margin: 0; // vertical alignment handled by the flexbox wrapper
56 | }
57 |
58 | .uppy-Audio-audioSource-select {
59 | display: block;
60 | font-size: 16px;
61 | line-height: 1.2;
62 | padding: 0.4em 1em 0.3em 0.4em;
63 | width: 100%;
64 | max-width: 90%;
65 | border: 1px solid $gray-600;
66 | background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23757575%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
67 | background-repeat: no-repeat;
68 | background-position:
69 | right 0.4em top 50%,
70 | 0 0;
71 | background-size:
72 | 0.65em auto,
73 | 100%;
74 | margin: auto;
75 | margin-bottom: 10px;
76 | white-space: nowrap;
77 | text-overflow: ellipsis;
78 |
79 | .uppy-size--lg & {
80 | font-size: 14px;
81 | margin-bottom: 0;
82 | }
83 | }
84 |
85 | .uppy-Audio-audioSource-select::-ms-expand {
86 | display: none;
87 | }
88 |
89 | .uppy-Audio-buttonContainer {
90 | width: 50%;
91 | margin-left: 25%;
92 | text-align: center;
93 | flex: 1;
94 | }
95 |
96 | .uppy-size--lg .uppy-Audio-buttonContainer {
97 | width: 34%;
98 | margin-left: 0;
99 | }
100 |
101 | .uppy-Audio-recordingLength {
102 | width: 25%;
103 | flex-grow: 0;
104 | color: $gray-600;
105 | font-family: $font-family-mono;
106 | text-align: right;
107 | }
108 |
109 | .uppy-size--lg .uppy-Audio-recordingLength {
110 | width: 33%;
111 | }
112 |
113 | .uppy-Audio-button {
114 | @include blue-border-focus;
115 | width: 45px;
116 | height: 45px;
117 | border-radius: 50%;
118 | background-color: $red;
119 | color: $white;
120 | cursor: pointer;
121 | transition: all 0.3s;
122 |
123 | &:hover {
124 | background-color: darken($red, 5%);
125 | }
126 |
127 | [data-uppy-theme='dark'] & {
128 | @include blue-border-focus--dark;
129 | }
130 | }
131 |
132 | .uppy-Audio-button--submit {
133 | background-color: $blue;
134 | margin: 0 12px;
135 |
136 | &:hover {
137 | background-color: darken($blue, 5%);
138 | }
139 | }
140 |
141 | .uppy-Audio-button svg {
142 | width: 26px;
143 | height: 26px;
144 | max-width: 100%;
145 | max-height: 100%;
146 | display: inline-block;
147 | vertical-align: text-top;
148 | overflow: hidden;
149 | fill: currentColor;
150 | }
151 |
152 | .uppy-size--md .uppy-Audio-button {
153 | width: 60px;
154 | height: 60px;
155 | }
156 |
157 | .uppy-Audio-permissons {
158 | padding: 15px;
159 | display: flex;
160 | align-items: center;
161 | justify-content: center;
162 | flex-flow: column wrap;
163 | height: 100%;
164 | flex: 1;
165 | }
166 |
167 | .uppy-Audio-permissons p {
168 | max-width: 450px;
169 | line-height: 1.3;
170 | text-align: center;
171 | line-height: 1.45;
172 | color: $gray-500;
173 | margin: 0;
174 | }
175 |
176 | .uppy-Audio-permissonsIcon svg {
177 | width: 100px;
178 | height: 75px;
179 | color: $gray-400;
180 | margin-bottom: 30px;
181 | }
182 |
183 | .uppy-Audio-title {
184 | font-size: 22px;
185 | line-height: 1.35;
186 | font-weight: 400;
187 | margin: 0;
188 | margin-bottom: 5px;
189 | padding: 0 15px;
190 | max-width: 500px;
191 | text-align: center;
192 | color: $gray-800;
193 |
194 | [data-uppy-theme='dark'] & {
195 | color: $gray-200;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { BasePlugin, PluginOptions, UppyFile } from '@uppy/core'
2 |
3 | type MaybePromise = T | Promise
4 |
5 | export type AwsS3UploadParameters =
6 | | {
7 | method: 'POST'
8 | url: string
9 | fields: Record
10 | expires?: number
11 | headers?: Record
12 | }
13 | | {
14 | method?: 'PUT'
15 | url: string
16 | fields?: Record
17 | expires?: number
18 | headers?: Record
19 | }
20 |
21 | export interface AwsS3Part {
22 | PartNumber?: number
23 | Size?: number
24 | ETag?: string
25 | }
26 | /**
27 | * @deprecated use {@link AwsS3UploadParameters} instead
28 | */
29 | export interface AwsS3SignedPart {
30 | url: string
31 | headers?: Record
32 | }
33 | export interface AwsS3STSResponse {
34 | credentials: {
35 | AccessKeyId: string
36 | SecretAccessKey: string
37 | SessionToken: string
38 | Expiration?: string
39 | }
40 | bucket: string
41 | region: string
42 | }
43 |
44 | type AWSS3NonMultipartWithCompanionMandatory = {
45 | getUploadParameters?: never
46 | }
47 |
48 | type AWSS3NonMultipartWithoutCompanionMandatory = {
49 | getUploadParameters: (file: UppyFile) => MaybePromise
50 | }
51 | type AWSS3NonMultipartWithCompanion = AWSS3WithCompanion &
52 | AWSS3NonMultipartWithCompanionMandatory & {
53 | shouldUseMultipart: false
54 | createMultipartUpload?: never
55 | listParts?: never
56 | signPart?: never
57 | abortMultipartUpload?: never
58 | completeMultipartUpload?: never
59 | }
60 |
61 | type AWSS3NonMultipartWithoutCompanion = AWSS3WithoutCompanion &
62 | AWSS3NonMultipartWithoutCompanionMandatory & {
63 | shouldUseMultipart: false
64 | createMultipartUpload?: never
65 | listParts?: never
66 | signPart?: never
67 | abortMultipartUpload?: never
68 | completeMultipartUpload?: never
69 | }
70 |
71 | type AWSS3MultipartWithoutCompanionMandatory = {
72 | getChunkSize?: (file: UppyFile) => number
73 | createMultipartUpload: (
74 | file: UppyFile,
75 | ) => MaybePromise<{ uploadId: string; key: string }>
76 | listParts: (
77 | file: UppyFile,
78 | opts: { uploadId: string; key: string; signal: AbortSignal },
79 | ) => MaybePromise
80 | abortMultipartUpload: (
81 | file: UppyFile,
82 | opts: { uploadId: string; key: string; signal: AbortSignal },
83 | ) => MaybePromise
84 | completeMultipartUpload: (
85 | file: UppyFile,
86 | opts: {
87 | uploadId: string
88 | key: string
89 | parts: AwsS3Part[]
90 | signal: AbortSignal
91 | },
92 | ) => MaybePromise<{ location?: string }>
93 | } & (
94 | | {
95 | signPart: (
96 | file: UppyFile,
97 | opts: {
98 | uploadId: string
99 | key: string
100 | partNumber: number
101 | body: Blob
102 | signal: AbortSignal
103 | },
104 | ) => MaybePromise
105 | }
106 | | {
107 | /** @deprecated Use signPart instead */
108 | prepareUploadParts: (
109 | file: UppyFile,
110 | partData: {
111 | uploadId: string
112 | key: string
113 | parts: [{ number: number; chunk: Blob }]
114 | },
115 | ) => MaybePromise<{
116 | presignedUrls: Record
117 | headers?: Record>
118 | }>
119 | }
120 | )
121 | type AWSS3MultipartWithoutCompanion = AWSS3WithoutCompanion &
122 | AWSS3MultipartWithoutCompanionMandatory & {
123 | shouldUseMultipart?: true
124 | getUploadParameters?: never
125 | }
126 |
127 | type AWSS3MultipartWithCompanion = AWSS3WithCompanion &
128 | Partial & {
129 | shouldUseMultipart?: true
130 | getUploadParameters?: never
131 | }
132 |
133 | type AWSS3MaybeMultipartWithCompanion = AWSS3WithCompanion &
134 | Partial &
135 | AWSS3NonMultipartWithCompanionMandatory & {
136 | shouldUseMultipart: (file: UppyFile) => boolean
137 | }
138 |
139 | type AWSS3MaybeMultipartWithoutCompanion = AWSS3WithoutCompanion &
140 | AWSS3MultipartWithoutCompanionMandatory &
141 | AWSS3NonMultipartWithoutCompanionMandatory & {
142 | shouldUseMultipart: (file: UppyFile) => boolean
143 | }
144 |
145 | type AWSS3WithCompanion = {
146 | companionUrl: string
147 | companionHeaders?: Record
148 | companionCookiesRule?: string
149 | getTemporarySecurityCredentials?: true
150 | }
151 | type AWSS3WithoutCompanion = {
152 | companionUrl?: never
153 | companionHeaders?: never
154 | companionCookiesRule?: never
155 | getTemporarySecurityCredentials?: (options?: {
156 | signal?: AbortSignal
157 | }) => MaybePromise
158 | }
159 |
160 | interface _AwsS3MultipartOptions extends PluginOptions {
161 | allowedMetaFields?: string[] | null
162 | limit?: number
163 | retryDelays?: number[] | null
164 | }
165 |
166 | export type AwsS3MultipartOptions = _AwsS3MultipartOptions &
167 | (
168 | | AWSS3NonMultipartWithCompanion
169 | | AWSS3NonMultipartWithoutCompanion
170 | | AWSS3MultipartWithCompanion
171 | | AWSS3MultipartWithoutCompanion
172 | | AWSS3MaybeMultipartWithCompanion
173 | | AWSS3MaybeMultipartWithoutCompanion
174 | )
175 |
176 | declare class AwsS3Multipart extends BasePlugin {}
177 |
178 | export default AwsS3Multipart
179 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @uppy/aws-s3
2 |
3 | ## 3.3.0
4 |
5 | Released: 2023-09-05
6 | Included in: Uppy v3.15.0
7 |
8 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/core,@uppy/tus,@uppy/utils,@uppy/xhr-upload: Move remote file upload logic into companion-client (Merlijn Vos / #4573)
9 |
10 | ## 3.2.3
11 |
12 | Released: 2023-08-23
13 | Included in: Uppy v3.14.1
14 |
15 | - @uppy/aws-s3-multipart,@uppy/aws-s3: allow empty objects for `fields` types (Antoine du Hamel / #4631)
16 |
17 | ## 3.2.2
18 |
19 | Released: 2023-08-15
20 | Included in: Uppy v3.14.0
21 |
22 | - @uppy/aws-s3,@uppy/aws-s3-multipart: update types (Antoine du Hamel / #4611)
23 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion,@uppy/transloadit,@uppy/xhr-upload: use uppercase HTTP method names (Antoine du Hamel / #4612)
24 | - @uppy/aws-s3,@uppy/aws-s3-multipart: update types (bdirito / #4576)
25 | - @uppy/aws-s3,@uppy/tus,@uppy/xhr-upload: Invoke headers function for remote uploads (Dominik Schmidt / #4596)
26 |
27 | ## 3.2.1
28 |
29 | Released: 2023-07-06
30 | Included in: Uppy v3.11.0
31 |
32 | - @uppy/aws-s3: fix remote uploads (Antoine du Hamel / #4546)
33 |
34 | ## 3.2.0
35 |
36 | Released: 2023-06-19
37 | Included in: Uppy v3.10.0
38 |
39 | - @uppy/aws-s3: add `shouldUseMultipart` option (Antoine du Hamel / #4299)
40 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus,@uppy/utils,@uppy/xhr-upload: When file is removed (or all are canceled), controller.abort queued requests (Artur Paikin / #4504)
41 |
42 | ## 3.1.1
43 |
44 | Released: 2023-05-02
45 | Included in: Uppy v3.9.0
46 |
47 | - @uppy/aws-s3: deprecate `timeout` option (Antoine du Hamel / #4298)
48 |
49 | ## 3.0.6
50 |
51 | Released: 2023-04-04
52 | Included in: Uppy v3.7.0
53 |
54 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus,@uppy/xhr-upload: make sure that we reset serverToken when an upload fails (Mikael Finstad / #4376)
55 | - @uppy/aws-s3: Update types (Minh Hieu / #4294)
56 |
57 | ## 3.0.5
58 |
59 | Released: 2023-01-26
60 | Included in: Uppy v3.4.0
61 |
62 | - @uppy/aws-s3: fix: add https:// to digital oceans link (Le Gia Hoang / #4165)
63 |
64 | ## 3.0.4
65 |
66 | Released: 2022-10-24
67 | Included in: Uppy v3.2.2
68 |
69 | - @uppy/aws-s3,@uppy/tus,@uppy/xhr-upload: replace `this.getState().files` with `this.uppy.getState().files` (Artur Paikin / #4167)
70 |
71 | ## 3.0.3
72 |
73 | Released: 2022-10-19
74 | Included in: Uppy v3.2.0
75 |
76 | - @uppy/aws-s3,@uppy/xhr-upload: fix `Cannot mark a queued request as done` in `MiniXHRUpload` (Antoine du Hamel / #4151)
77 |
78 | ## 3.0.2
79 |
80 | Released: 2022-09-25
81 | Included in: Uppy v3.1.0
82 |
83 | - @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
84 |
85 | ## 3.0.0
86 |
87 | Released: 2022-08-22
88 | Included in: Uppy v3.0.0
89 |
90 | - @uppy/aws-s3,@uppy/tus,@uppy/xhr-upload: @uppy/tus, @uppy/xhr-upload, @uppy/aws-s3: `metaFields` -> `allowedMetaFields` (Merlijn Vos / #4023)
91 | - @uppy/aws-s3: aws-s3: fix incorrect comparison for `file-removed` (Merlijn Vos / #3962)
92 | - Switch to ESM
93 |
94 | ## 3.0.0-beta.3
95 |
96 | Released: 2022-08-16
97 | Included in: Uppy v3.0.0-beta.5
98 |
99 | - @uppy/aws-s3: Export AwsS3UploadParameters & AwsS3Options interfaces (Antonina Vertsinskaya / #3956)
100 |
101 | ## 3.0.0-beta.2
102 |
103 | Released: 2022-07-27
104 | Included in: Uppy v3.0.0-beta.3
105 |
106 | - @uppy/aws-s3,@uppy/core,@uppy/dashboard,@uppy/store-redux,@uppy/xhr-upload: upgrade `nanoid` to v4 (Antoine du Hamel / #3904)
107 |
108 | ## 2.2.1
109 |
110 | Released: 2022-06-07
111 | Included in: Uppy v2.12.0
112 |
113 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
114 |
115 | ## 2.2.0
116 |
117 | Released: 2022-05-30
118 | Included in: Uppy v2.11.0
119 |
120 | - @uppy/angular,@uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/onedrive,@uppy/progress-bar,@uppy/react,@uppy/redux-dev-tools,@uppy/robodog,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
121 | - @uppy/aws-s3: fix JSDoc type error (Antoine du Hamel / #3785)
122 | - @uppy/aws-s3: refactor to ESM (Antoine du Hamel / #3673)
123 |
124 | ## 2.1.0
125 |
126 | Released: 2022-05-14
127 | Included in: Uppy v2.10.0
128 |
129 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/core,@uppy/react,@uppy/transloadit,@uppy/tus,@uppy/xhr-upload: proposal: Cancel assemblies optional (Mikael Finstad / #3575)
130 |
131 | ## 2.0.9
132 |
133 | Released: 2022-04-07
134 | Included in: Uppy v2.9.2
135 |
136 | - @uppy/aws-s3,@uppy/companion-client,@uppy/transloadit,@uppy/utils: Propagate `isNetworkError` through error wrappers (Renée Kooi / #3620)
137 |
138 | ## 2.0.8
139 |
140 | Released: 2022-03-16
141 | Included in: Uppy v2.8.0
142 |
143 | - @uppy/aws-s3: fix wrong events being sent to companion (Mikael Finstad / #3576)
144 |
145 | ## 2.0.7
146 |
147 | Released: 2021-12-09
148 | Included in: Uppy v2.3.1
149 |
150 | - @uppy/aws-s3,@uppy/core,@uppy/dashboard,@uppy/store-redux,@uppy/xhr-upload: deps: use `nanoid/non-secure` to workaround react-native limitation (Antoine du Hamel / #3350)
151 |
152 | ## 2.0.6
153 |
154 | Released: 2021-12-07
155 | Included in: Uppy v2.3.0
156 |
157 | - @uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/google-drive,@uppy/image-editor,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/screen-capture,@uppy/status-bar,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/url,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: Refactor locale scripts & generate types and docs (Merlijn Vos / #3276)
158 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/src/createSignedURL.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a canonical request by concatenating the following strings, separated
3 | * by newline characters. This helps ensure that the signature that you
4 | * calculate and the signature that AWS calculates can match.
5 | *
6 | * @see https://docs.aws.amazon.com/IAM/laExperimentCourse11/UserGuide/create-signed-request.html#create-canonical-request
7 | *
8 | * @param {object} param0
9 | * @param {string} param0.method – The HTTP method.
10 | * @param {string} param0.CanonicalUri – The URI-encoded version of the absolute
11 | * path component URL (everything between the host and the question mark
12 | * character (?) that starts the query string parameters). If the absolute path
13 | * is empty, use a forward slash character (/).
14 | * @param {string} param0.CanonicalQueryString – The URL-encoded query string
15 | * parameters, separated by ampersands (&). Percent-encode reserved characters,
16 | * including the space character. Encode names and values separately. If there
17 | * are empty parameters, append the equals sign to the parameter name before
18 | * encoding. After encoding, sort the parameters alphabetically by key name. If
19 | * there is no query string, use an empty string ("").
20 | * @param {Record} param0.SignedHeaders – The request headers,
21 | * that will be signed, and their values, separated by newline characters.
22 | * For the values, trim any leading or trailing spaces, convert sequential
23 | * spaces to a single space, and separate the values for a multi-value header
24 | * using commas. You must include the host header (HTTP/1.1), and any x-amz-*
25 | * headers in the signature. You can optionally include other standard headers
26 | * in the signature, such as content-type.
27 | * @param {string} param0.HashedPayload – A string created using the payload in
28 | * the body of the HTTP request as input to a hash function. This string uses
29 | * lowercase hexadecimal characters. If the payload is empty, use an empty
30 | * string as the input to the hash function.
31 | * @returns {string}
32 | */
33 | function createCanonicalRequest ({
34 | method = 'PUT',
35 | CanonicalUri = '/',
36 | CanonicalQueryString = '',
37 | SignedHeaders,
38 | HashedPayload,
39 | }) {
40 | const headerKeys = Object.keys(SignedHeaders).map(k => k.toLowerCase()).sort()
41 | return [
42 | method,
43 | CanonicalUri,
44 | CanonicalQueryString,
45 | ...headerKeys.map(k => `${k}:${SignedHeaders[k]}`),
46 | '',
47 | headerKeys.join(';'),
48 | HashedPayload,
49 | ].join('\n')
50 | }
51 |
52 | const ec = new TextEncoder()
53 | const algorithm = { name: 'HMAC', hash: 'SHA-256' }
54 |
55 | async function digest (data) {
56 | const { subtle } = globalThis.crypto
57 | return subtle.digest(algorithm.hash, ec.encode(data))
58 | }
59 |
60 | async function generateHmacKey (secret) {
61 | const { subtle } = globalThis.crypto
62 | return subtle.importKey('raw', typeof secret === 'string' ? ec.encode(secret) : secret, algorithm, false, ['sign'])
63 | }
64 |
65 | function arrayBufferToHexString (arrayBuffer) {
66 | const byteArray = new Uint8Array(arrayBuffer)
67 | let hexString = ''
68 | for (let i = 0; i < byteArray.length; i++) {
69 | hexString += byteArray[i].toString(16).padStart(2, '0')
70 | }
71 | return hexString
72 | }
73 |
74 | async function hash (key, data) {
75 | const { subtle } = globalThis.crypto
76 | return subtle.sign(algorithm, await generateHmacKey(key), ec.encode(data))
77 | }
78 |
79 | /**
80 | * @see https://docs.aws.amazon.com/IAM/laExperimentCourse11/UserGuide/create-signed-request.html
81 | * @param {Record} param0
82 | * @returns {Promise} the signed URL
83 | */
84 | export default async function createSignedURL ({
85 | accountKey, accountSecret, sessionToken,
86 | bucketName,
87 | Key, Region,
88 | expires,
89 | uploadId, partNumber,
90 | }) {
91 | const Service = 's3'
92 | const host = `${bucketName}.${Service}.${Region}.amazonaws.com`
93 | const CanonicalUri = `/${encodeURI(Key)}`
94 | const payload = 'UNSIGNED-PAYLOAD'
95 |
96 | const requestDateTime = new Date().toISOString().replace(/[-:]|\.\d+/g, '') // YYYYMMDDTHHMMSSZ
97 | const date = requestDateTime.slice(0, 8) // YYYYMMDD
98 | const scope = `${date}/${Region}/${Service}/aws4_request`
99 |
100 | const url = new URL(`https://${host}${CanonicalUri}`)
101 | // N.B.: URL search params needs to be added in the ASCII order
102 | url.searchParams.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256')
103 | url.searchParams.set('X-Amz-Content-Sha256', payload)
104 | url.searchParams.set('X-Amz-Credential', `${accountKey}/${scope}`)
105 | url.searchParams.set('X-Amz-Date', requestDateTime)
106 | url.searchParams.set('X-Amz-Expires', expires)
107 | // We are signing on the client, so we expect there's going to be a session token:
108 | url.searchParams.set('X-Amz-Security-Token', sessionToken)
109 | url.searchParams.set('X-Amz-SignedHeaders', 'host')
110 | // Those two are present only for Multipart Uploads:
111 | if (partNumber) url.searchParams.set('partNumber', partNumber)
112 | if (uploadId) url.searchParams.set('uploadId', uploadId)
113 | url.searchParams.set('x-id', partNumber && uploadId ? 'UploadPart' : 'PutObject')
114 |
115 | // Step 1: Create a canonical request
116 | const canonical = createCanonicalRequest({
117 | CanonicalUri,
118 | CanonicalQueryString: url.search.slice(1),
119 | SignedHeaders: {
120 | host,
121 | },
122 | HashedPayload: payload,
123 | })
124 |
125 | // Step 2: Create a hash of the canonical request
126 | const hashedCanonical = arrayBufferToHexString(await digest(canonical))
127 |
128 | // Step 3: Create a string to sign
129 | const stringToSign = [
130 | `AWS4-HMAC-SHA256`, // The algorithm used to create the hash of the canonical request.
131 | requestDateTime, // The date and time used in the credential scope.
132 | scope, // The credential scope. This restricts the resulting signature to the specified Region and service.
133 | hashedCanonical, // The hash of the canonical request.
134 | ].join('\n')
135 |
136 | // Step 4: Calculate the signature
137 | const kDate = await hash(`AWS4${accountSecret}`, date)
138 | const kRegion = await hash(kDate, Region)
139 | const kService = await hash(kRegion, Service)
140 | const kSigning = await hash(kService, 'aws4_request')
141 | const signature = arrayBufferToHexString(await hash(kSigning, stringToSign))
142 |
143 | // Step 5: Add the signature to the request
144 | url.searchParams.set('X-Amz-Signature', signature)
145 |
146 | return url
147 | }
148 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/companion-client/src/Socket.test.js:
--------------------------------------------------------------------------------
1 | import { afterEach, beforeEach, vi, describe, it, expect } from 'viExperimentCourse11'
2 | import UppySocket from './Socket.js'
3 |
4 | describe('Socket', () => {
5 | let webSocketConstructorSpy
6 | let webSocketCloseSpy
7 | let webSocketSendSpy
8 |
9 | beforeEach(() => {
10 | webSocketConstructorSpy = vi.fn()
11 | webSocketCloseSpy = vi.fn()
12 | webSocketSendSpy = vi.fn()
13 |
14 | globalThis.WebSocket = class WebSocket {
15 | constructor (target) {
16 | webSocketConstructorSpy(target)
17 | }
18 |
19 | // eslint-disable-next-line class-methods-use-this
20 | close (args) {
21 | webSocketCloseSpy(args)
22 | }
23 |
24 | // eslint-disable-next-line class-methods-use-this
25 | send (json) {
26 | webSocketSendSpy(json)
27 | }
28 |
29 | triggerOpen () {
30 | this.onopen()
31 | }
32 |
33 | triggerClose () {
34 | this.onclose()
35 | }
36 | }
37 | })
38 | afterEach(() => {
39 | globalThis.WebSocket = undefined
40 | })
41 |
42 | it('should expose a class', () => {
43 | expect(UppySocket.name).toEqual('UppySocket')
44 | expect(
45 | new UppySocket({
46 | target: 'foo',
47 | }) instanceof UppySocket,
48 | )
49 | })
50 |
51 | it('should setup a new WebSocket', () => {
52 | new UppySocket({ target: 'foo' }) // eslint-disable-line no-new
53 | expect(webSocketConstructorSpy.mock.calls[0][0]).toEqual('foo')
54 | })
55 |
56 | it('should send a message via the websocket if the connection is open', () => {
57 | const uppySocket = new UppySocket({ target: 'foo' })
58 | const webSocketInstance = uppySocket[Symbol.for('uppy ExperimentCourse11: getSocket')]()
59 | webSocketInstance.triggerOpen()
60 |
61 | uppySocket.send('bar', 'boo')
62 | expect(webSocketSendSpy.mock.calls.length).toEqual(1)
63 | expect(webSocketSendSpy.mock.calls[0]).toEqual([
64 | JSON.stringify({ action: 'bar', payload: 'boo' }),
65 | ])
66 | })
67 |
68 | it('should queue the message for the websocket if the connection is not open', () => {
69 | const uppySocket = new UppySocket({ target: 'foo' })
70 |
71 | uppySocket.send('bar', 'boo')
72 | expect(uppySocket[Symbol.for('uppy ExperimentCourse11: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }])
73 | expect(webSocketSendSpy.mock.calls.length).toEqual(0)
74 | })
75 |
76 | it('should queue any messages for the websocket if the connection is not open, then send them when the connection is open', () => {
77 | const uppySocket = new UppySocket({ target: 'foo' })
78 | const webSocketInstance = uppySocket[Symbol.for('uppy ExperimentCourse11: getSocket')]()
79 |
80 | uppySocket.send('bar', 'boo')
81 | uppySocket.send('moo', 'baa')
82 | expect(uppySocket[Symbol.for('uppy ExperimentCourse11: getQueued')]()).toEqual([
83 | { action: 'bar', payload: 'boo' },
84 | { action: 'moo', payload: 'baa' },
85 | ])
86 | expect(webSocketSendSpy.mock.calls.length).toEqual(0)
87 |
88 | webSocketInstance.triggerOpen()
89 |
90 | expect(uppySocket[Symbol.for('uppy ExperimentCourse11: getQueued')]()).toEqual([])
91 | expect(webSocketSendSpy.mock.calls.length).toEqual(2)
92 | expect(webSocketSendSpy.mock.calls[0]).toEqual([
93 | JSON.stringify({ action: 'bar', payload: 'boo' }),
94 | ])
95 | expect(webSocketSendSpy.mock.calls[1]).toEqual([
96 | JSON.stringify({ action: 'moo', payload: 'baa' }),
97 | ])
98 | })
99 |
100 | it('should start queuing any messages when the websocket connection is closed', () => {
101 | const uppySocket = new UppySocket({ target: 'foo' })
102 | const webSocketInstance = uppySocket[Symbol.for('uppy ExperimentCourse11: getSocket')]()
103 | webSocketInstance.triggerOpen()
104 | uppySocket.send('bar', 'boo')
105 | expect(uppySocket[Symbol.for('uppy ExperimentCourse11: getQueued')]()).toEqual([])
106 |
107 | webSocketInstance.triggerClose()
108 | uppySocket.send('bar', 'boo')
109 | expect(uppySocket[Symbol.for('uppy ExperimentCourse11: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }])
110 | })
111 |
112 | it('should close the websocket when it is force closed', () => {
113 | const uppySocket = new UppySocket({ target: 'foo' })
114 | const webSocketInstance = uppySocket[Symbol.for('uppy ExperimentCourse11: getSocket')]()
115 | webSocketInstance.triggerOpen()
116 |
117 | uppySocket.close()
118 | expect(webSocketCloseSpy.mock.calls.length).toEqual(1)
119 | })
120 |
121 | it('should be able to subscribe to messages received on the websocket', () => {
122 | const uppySocket = new UppySocket({ target: 'foo' })
123 | const webSocketInstance = uppySocket[Symbol.for('uppy ExperimentCourse11: getSocket')]()
124 |
125 | const emitterListenerMock = vi.fn()
126 | uppySocket.on('hi', emitterListenerMock)
127 |
128 | webSocketInstance.triggerOpen()
129 | webSocketInstance.onmessage({
130 | data: JSON.stringify({ action: 'hi', payload: 'ho' }),
131 | })
132 | expect(emitterListenerMock.mock.calls).toEqual([
133 | ['ho', undefined, undefined, undefined, undefined, undefined],
134 | ])
135 | })
136 |
137 | it('should be able to emit messages and subscribe to them', () => {
138 | const uppySocket = new UppySocket({ target: 'foo' })
139 |
140 | const emitterListenerMock = vi.fn()
141 | uppySocket.on('hi', emitterListenerMock)
142 |
143 | uppySocket.emit('hi', 'ho')
144 | uppySocket.emit('hi', 'ho')
145 | uppySocket.emit('hi', 'off to work we go')
146 |
147 | expect(emitterListenerMock.mock.calls).toEqual([
148 | ['ho', undefined, undefined, undefined, undefined, undefined],
149 | ['ho', undefined, undefined, undefined, undefined, undefined],
150 | [
151 | 'off to work we go',
152 | undefined,
153 | undefined,
154 | undefined,
155 | undefined,
156 | undefined,
157 | ],
158 | ])
159 | })
160 |
161 | it('should be able to subscribe to the first event for a particular action', () => {
162 | const uppySocket = new UppySocket({ target: 'foo' })
163 |
164 | const emitterListenerMock = vi.fn()
165 | uppySocket.once('hi', emitterListenerMock)
166 |
167 | uppySocket.emit('hi', 'ho')
168 | uppySocket.emit('hi', 'ho')
169 | uppySocket.emit('hi', 'off to work we go')
170 |
171 | expect(emitterListenerMock.mock.calls.length).toEqual(1)
172 | expect(emitterListenerMock.mock.calls).toEqual([
173 | ['ho', undefined, undefined, undefined, undefined, undefined],
174 | ])
175 | })
176 | })
177 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/src/MultipartUploader.js:
--------------------------------------------------------------------------------
1 | import { AbortController } from '@uppy/utils/lib/AbortController'
2 |
3 | const MB = 1024 * 1024
4 |
5 | const defaultOptions = {
6 | getChunkSize (file) {
7 | return Math.ceil(file.size / 10000)
8 | },
9 | onProgress () {},
10 | onPartComplete () {},
11 | onSuccess () {},
12 | onError (err) {
13 | throw err
14 | },
15 | }
16 |
17 | function ensureInt (value) {
18 | if (typeof value === 'string') {
19 | return parseInt(value, 10)
20 | }
21 | if (typeof value === 'number') {
22 | return value
23 | }
24 | throw new TypeError('Expected a number')
25 | }
26 |
27 | export const pausingUploadReason = Symbol('pausing upload, not an actual error')
28 |
29 | /**
30 | * A MultipartUploader instance is used per file upload to determine whether a
31 | * upload should be done as multipart or as a regular S3 upload
32 | * (based on the user-provided `shouldUseMultipart` option value) and to manage
33 | * the chunk splitting.
34 | */
35 | class MultipartUploader {
36 | #abortController = new AbortController()
37 |
38 | /** @type {import("../types/chunk").Chunk[]} */
39 | #chunks
40 |
41 | /** @type {{ uploaded: number, etag?: string, done?: boolean }[]} */
42 | #chunkState
43 |
44 | /**
45 | * The (un-chunked) data to upload.
46 | *
47 | * @type {Blob}
48 | */
49 | #data
50 |
51 | /** @type {import("@uppy/core").UppyFile} */
52 | #file
53 |
54 | /** @type {boolean} */
55 | #uploadHasStarted = false
56 |
57 | /** @type {(err?: Error | any) => void} */
58 | #onError
59 |
60 | /** @type {() => void} */
61 | #onSuccess
62 |
63 | /** @type {import('../types/index').AwsS3MultipartOptions["shouldUseMultipart"]} */
64 | #shouldUseMultipart
65 |
66 | /** @type {boolean} */
67 | #isRestoring
68 |
69 | #onReject = (err) => (err?.cause === pausingUploadReason ? null : this.#onError(err))
70 |
71 | #maxMultipartParts = 10_000
72 |
73 | #minPartSize = 5 * MB
74 |
75 | constructor (data, options) {
76 | this.options = {
77 | ...defaultOptions,
78 | ...options,
79 | }
80 | // Use default `getChunkSize` if it was null or something
81 | this.options.getChunkSize ??= defaultOptions.getChunkSize
82 |
83 | this.#data = data
84 | this.#file = options.file
85 | this.#onSuccess = this.options.onSuccess
86 | this.#onError = this.options.onError
87 | this.#shouldUseMultipart = this.options.shouldUseMultipart
88 |
89 | // When we are restoring an upload, we already have an UploadId and a Key. Otherwise
90 | // we need to call `createMultipartUpload` to get an `uploadId` and a `key`.
91 | // Non-multipart uploads are not restorable.
92 | this.#isRestoring = options.uploadId && options.key
93 |
94 | this.#initChunks()
95 | }
96 |
97 | // initChunks checks the user preference for using multipart uploads (opts.shouldUseMultipart)
98 | // and calculates the optimal part size. When using multipart part uploads every part except for the last has
99 | // to be at least 5 MB and there can be no more than 10K parts.
100 | // This means we sometimes need to change the preferred part size from the user in order to meet these requirements.
101 | #initChunks () {
102 | const fileSize = this.#data.size
103 | const shouldUseMultipart = typeof this.#shouldUseMultipart === 'function'
104 | ? this.#shouldUseMultipart(this.#file)
105 | : Boolean(this.#shouldUseMultipart)
106 |
107 | if (shouldUseMultipart && fileSize > this.#minPartSize) {
108 | // At least 5MB per request:
109 | let chunkSize = Math.max(this.options.getChunkSize(this.#data), this.#minPartSize)
110 | let arraySize = Math.floor(fileSize / chunkSize)
111 |
112 | // At most 10k requests per file:
113 | if (arraySize > this.#maxMultipartParts) {
114 | arraySize = this.#maxMultipartParts
115 | chunkSize = fileSize / this.#maxMultipartParts
116 | }
117 | this.#chunks = Array(arraySize)
118 |
119 | for (let offset = 0, j = 0; offset < fileSize; offset += chunkSize, j++) {
120 | const end = Math.min(fileSize, offset + chunkSize)
121 |
122 | // Defer data fetching/slicing until we actually need the data, because it's slow if we have a lot of files
123 | const getData = () => {
124 | const i2 = offset
125 | return this.#data.slice(i2, end)
126 | }
127 |
128 | this.#chunks[j] = {
129 | getData,
130 | onProgress: this.#onPartProgress(j),
131 | onComplete: this.#onPartComplete(j),
132 | shouldUseMultipart,
133 | }
134 | if (this.#isRestoring) {
135 | const size = offset + chunkSize > fileSize ? fileSize - offset : chunkSize
136 | // setAsUploaded is called by listPart, to keep up-to-date the
137 | // quantity of data that is left to actually upload.
138 | this.#chunks[j].setAsUploaded = () => {
139 | this.#chunks[j] = null
140 | this.#chunkState[j].uploaded = size
141 | }
142 | }
143 | }
144 | } else {
145 | this.#chunks = [{
146 | getData: () => this.#data,
147 | onProgress: this.#onPartProgress(0),
148 | onComplete: this.#onPartComplete(0),
149 | shouldUseMultipart,
150 | }]
151 | }
152 |
153 | this.#chunkState = this.#chunks.map(() => ({ uploaded: 0 }))
154 | }
155 |
156 | #createUpload () {
157 | this
158 | .options.companionComm.uploadFile(this.#file, this.#chunks, this.#abortController.signal)
159 | .then(this.#onSuccess, this.#onReject)
160 | this.#uploadHasStarted = true
161 | }
162 |
163 | #resumeUpload () {
164 | this
165 | .options.companionComm.resumeUploadFile(this.#file, this.#chunks, this.#abortController.signal)
166 | .then(this.#onSuccess, this.#onReject)
167 | }
168 |
169 | #onPartProgress = (index) => (ev) => {
170 | if (!ev.lengthComputable) return
171 |
172 | this.#chunkState[index].uploaded = ensureInt(ev.loaded)
173 |
174 | const totalUploaded = this.#chunkState.reduce((n, c) => n + c.uploaded, 0)
175 | this.options.onProgress(totalUploaded, this.#data.size)
176 | }
177 |
178 | #onPartComplete = (index) => (etag) => {
179 | // This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers.
180 | this.#chunks[index] = null
181 | this.#chunkState[index].etag = etag
182 | this.#chunkState[index].done = true
183 |
184 | const part = {
185 | PartNumber: index + 1,
186 | ETag: etag,
187 | }
188 | this.options.onPartComplete(part)
189 | }
190 |
191 | #abortUpload () {
192 | this.#abortController.abort()
193 | this.options.companionComm.abortFileUpload(this.#file).catch((err) => this.options.log(err))
194 | }
195 |
196 | start () {
197 | if (this.#uploadHasStarted) {
198 | if (!this.#abortController.signal.aborted) this.#abortController.abort(pausingUploadReason)
199 | this.#abortController = new AbortController()
200 | this.#resumeUpload()
201 | } else if (this.#isRestoring) {
202 | this.options.companionComm.restoreUploadFile(this.#file, { uploadId: this.options.uploadId, key: this.options.key })
203 | this.#resumeUpload()
204 | } else {
205 | this.#createUpload()
206 | }
207 | }
208 |
209 | pause () {
210 | this.#abortController.abort(pausingUploadReason)
211 | // Swap it out for a new controller, because this instance may be resumed later.
212 | this.#abortController = new AbortController()
213 | }
214 |
215 | abort (opts = undefined) {
216 | if (opts?.really) this.#abortUpload()
217 | else this.pause()
218 | }
219 |
220 | // TODO: remove this in the next major
221 | get chunkState () {
222 | return this.#chunkState
223 | }
224 | }
225 |
226 | export default MultipartUploader
227 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3/src/MiniXHRUpload.js:
--------------------------------------------------------------------------------
1 | import { nanoid } from 'nanoid/non-secure'
2 | import EventManager from '@uppy/utils/lib/EventManager'
3 | import ProgressTimeout from '@uppy/utils/lib/ProgressTimeout'
4 | import ErrorWithCause from '@uppy/utils/lib/ErrorWithCause'
5 | import NetworkError from '@uppy/utils/lib/NetworkError'
6 | import isNetworkError from '@uppy/utils/lib/isNetworkError'
7 | import { internalRateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'
8 |
9 | // See XHRUpload
10 | function buildResponseError (xhr, error) {
11 | if (isNetworkError(xhr)) return new NetworkError(error, xhr)
12 |
13 | const err = new ErrorWithCause('Upload error', { cause: error })
14 | err.request = xhr
15 | return err
16 | }
17 |
18 | // See XHRUpload
19 | function setTypeInBlob (file) {
20 | const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type)
21 | return dataWithUpdatedType
22 | }
23 |
24 | function addMetadata (formData, meta, opts) {
25 | const allowedMetaFields = Array.isArray(opts.allowedMetaFields)
26 | ? opts.allowedMetaFields
27 | // Send along all fields by default.
28 | : Object.keys(meta)
29 | allowedMetaFields.forEach((item) => {
30 | formData.append(item, meta[item])
31 | })
32 | }
33 |
34 | function createFormDataUpload (file, opts) {
35 | const formPost = new FormData()
36 |
37 | addMetadata(formPost, file.meta, opts)
38 |
39 | const dataWithUpdatedType = setTypeInBlob(file)
40 |
41 | if (file.name) {
42 | formPost.append(opts.fieldName, dataWithUpdatedType, file.meta.name)
43 | } else {
44 | formPost.append(opts.fieldName, dataWithUpdatedType)
45 | }
46 |
47 | return formPost
48 | }
49 |
50 | const createBareUpload = file => file.data
51 |
52 | export default class MiniXHRUpload {
53 | constructor (uppy, opts) {
54 | this.uppy = uppy
55 | this.opts = {
56 | validaExperimentCourse11atus (status) {
57 | return status >= 200 && status < 300
58 | },
59 | ...opts,
60 | }
61 |
62 | this.requests = opts[internalRateLimitedQueue]
63 | this.uploaderEvents = Object.create(null)
64 | this.i18n = opts.i18n
65 | }
66 |
67 | getOptions (file) {
68 | const { uppy } = this
69 |
70 | const overrides = uppy.getState().xhrUpload
71 | const opts = {
72 | ...this.opts,
73 | ...(overrides || {}),
74 | ...(file.xhrUpload || {}),
75 | headers: {
76 | ...this.opts.headers,
77 | ...overrides?.headers,
78 | ...file.xhrUpload?.headers,
79 | },
80 | }
81 |
82 | return opts
83 | }
84 |
85 | #addEventHandlerForFile (eventName, fileID, eventHandler) {
86 | this.uploaderEvents[fileID].on(eventName, (fileOrID) => {
87 | // TODO (major): refactor Uppy events to consistently send file objects (or consistently IDs)
88 | // We created a generic `addEventListenerForFile` but not all events
89 | // use file IDs, some use files, so we need to do this weird check.
90 | const id = fileOrID?.id ?? fileOrID
91 | if (fileID === id) eventHandler()
92 | })
93 | }
94 |
95 | #addEventHandlerIfFileStillExists (eventName, fileID, eventHandler) {
96 | this.uploaderEvents[fileID].on(eventName, (...args) => {
97 | if (this.uppy.getFile(fileID)) eventHandler(...args)
98 | })
99 | }
100 |
101 | uploadLocalFile (file) {
102 | const opts = this.getOptions(file)
103 |
104 | return new Promise((resolve, reject) => {
105 | // This is done in index.js in the S3 plugin.
106 | // this.uppy.emit('upload-started', file)
107 |
108 | const data = opts.formData
109 | ? createFormDataUpload(file, opts)
110 | : createBareUpload(file, opts)
111 |
112 | const xhr = new XMLHttpRequest()
113 | this.uploaderEvents[file.id] = new EventManager(this.uppy)
114 |
115 | const timer = new ProgressTimeout(opts.timeout, () => {
116 | xhr.abort()
117 | // eslint-disable-next-line no-use-before-define
118 | queuedRequest.done()
119 | const error = new Error(this.i18n('timedOut', { seconds: Math.ceil(opts.timeout / 1000) }))
120 | this.uppy.emit('upload-error', file, error)
121 | reject(error)
122 | })
123 |
124 | const id = nanoid()
125 |
126 | xhr.upload.addEventListener('loadstart', () => {
127 | this.uppy.log(`[AwsS3/XHRUpload] ${id} started`)
128 | })
129 |
130 | xhr.upload.addEventListener('progress', (ev) => {
131 | this.uppy.log(`[AwsS3/XHRUpload] ${id} progress: ${ev.loaded} / ${ev.total}`)
132 | // Begin checking for timeouts when progress starts, instead of loading,
133 | // to avoid timing out requests on browser concurrency queue
134 | timer.progress()
135 |
136 | if (ev.lengthComputable) {
137 | this.uppy.emit('upload-progress', file, {
138 | uploader: this,
139 | bytesUploaded: ev.loaded,
140 | byExperimentCourse11otal: ev.total,
141 | })
142 | }
143 | })
144 |
145 | xhr.addEventListener('load', (ev) => {
146 | this.uppy.log(`[AwsS3/XHRUpload] ${id} finished`)
147 | timer.done()
148 | // eslint-disable-next-line no-use-before-define
149 | queuedRequest.done()
150 | if (this.uploaderEvents[file.id]) {
151 | this.uploaderEvents[file.id].remove()
152 | this.uploaderEvents[file.id] = null
153 | }
154 |
155 | if (opts.validaExperimentCourse11atus(ev.target.status, xhr.responseText, xhr)) {
156 | const body = opts.getResponseData(xhr.responseText, xhr)
157 | const uploadURL = body[opts.responseUrlFieldName]
158 |
159 | const uploadResp = {
160 | status: ev.target.status,
161 | body,
162 | uploadURL,
163 | }
164 |
165 | this.uppy.emit('upload-success', file, uploadResp)
166 |
167 | if (uploadURL) {
168 | this.uppy.log(`Download ${file.name} from ${uploadURL}`)
169 | }
170 |
171 | return resolve(file)
172 | }
173 | const body = opts.getResponseData(xhr.responseText, xhr)
174 | const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
175 |
176 | const response = {
177 | status: ev.target.status,
178 | body,
179 | }
180 |
181 | this.uppy.emit('upload-error', file, error, response)
182 | return reject(error)
183 | })
184 |
185 | xhr.addEventListener('error', () => {
186 | this.uppy.log(`[AwsS3/XHRUpload] ${id} errored`)
187 | timer.done()
188 | // eslint-disable-next-line no-use-before-define
189 | queuedRequest.done()
190 | if (this.uploaderEvents[file.id]) {
191 | this.uploaderEvents[file.id].remove()
192 | this.uploaderEvents[file.id] = null
193 | }
194 |
195 | const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
196 | this.uppy.emit('upload-error', file, error)
197 | return reject(error)
198 | })
199 |
200 | xhr.open(opts.method.toUpperCase(), opts.endpoint, true)
201 | // IE10 does not allow setting `withCredentials` and `responseType`
202 | // before `open()` is called. It’s important to set withCredentials
203 | // to a boolean, otherwise React Native crashes
204 | xhr.withCredentials = Boolean(opts.withCredentials)
205 | if (opts.responseType !== '') {
206 | xhr.responseType = opts.responseType
207 | }
208 |
209 | Object.keys(opts.headers).forEach((header) => {
210 | xhr.setRequestHeader(header, opts.headers[header])
211 | })
212 |
213 | const queuedRequest = this.requests.run(() => {
214 | xhr.send(data)
215 | return () => {
216 | // eslint-disable-next-line no-use-before-define
217 | timer.done()
218 | xhr.abort()
219 | }
220 | }, { priority: 1 })
221 |
222 | this.#addEventHandlerForFile('file-removed', file.id, () => {
223 | queuedRequest.abort()
224 | reject(new Error('File removed'))
225 | })
226 |
227 | this.#addEventHandlerIfFileStillExists('cancel-all', file.id, ({ reason } = {}) => {
228 | if (reason === 'user') {
229 | queuedRequest.abort()
230 | }
231 | reject(new Error('Upload cancelled'))
232 | })
233 | })
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/aws-nodejs/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('node:path')
4 | const crypto = require('node:crypto')
5 | require('dotenv').config({ path: path.join(__dirname, '..', '..', '.env') })
6 |
7 | const express = require('express')
8 |
9 | const app = express()
10 |
11 | const port = process.env.PORT ?? 8080
12 | const bodyParser = require('body-parser')
13 |
14 | const {
15 | S3Client,
16 | AbortMultipartUploadCommand,
17 | CompleteMultipartUploadCommand,
18 | CreateMultipartUploadCommand,
19 | ListPartsCommand,
20 | PutObjectCommand,
21 | UploadPartCommand,
22 | } = require('@aws-sdk/client-s3')
23 | const { getSignedUrl } = require('@aws-sdk/s3-request-presigner')
24 | const {
25 | STSClient,
26 | GetFederationTokenCommand,
27 | } = require('@aws-sdk/client-sts')
28 |
29 | const policy = {
30 | Version: '2012-10-17',
31 | Statement: [
32 | {
33 | Effect: 'Allow',
34 | Action: [
35 | 's3:PutObject',
36 | ],
37 | Resource: [
38 | `arn:aws:s3:::${process.env.COMPANION_AWS_BUCKET}/*`,
39 | `arn:aws:s3:::${process.env.COMPANION_AWS_BUCKET}`,
40 | ],
41 | },
42 | ],
43 | }
44 |
45 | /**
46 | * @type {S3Client}
47 | */
48 | let s3Client
49 |
50 | /**
51 | * @type {STSClient}
52 | */
53 | let stsClient
54 |
55 | const expiresIn = 900 // Define how long until a S3 signature expires.
56 |
57 | function getS3Client () {
58 | s3Client ??= new S3Client({
59 | region: process.env.COMPANION_AWS_REGION,
60 | credentials : {
61 | accessKeyId: process.env.COMPANION_AWS_KEY,
62 | secretAccessKey: process.env.COMPANION_AWS_SECRET,
63 | },
64 | })
65 | return s3Client
66 | }
67 |
68 | function getSTSClient () {
69 | stsClient ??= new STSClient({
70 | region: process.env.COMPANION_AWS_REGION,
71 | credentials : {
72 | accessKeyId: process.env.COMPANION_AWS_KEY,
73 | secretAccessKey: process.env.COMPANION_AWS_SECRET,
74 | },
75 | })
76 | return stsClient
77 | }
78 |
79 | app.use(bodyParser.urlencoded({ extended: true }), bodyParser.json())
80 |
81 | app.get('/', (req, res) => {
82 | const htmlPath = path.join(__dirname, 'public', 'index.html')
83 | res.sendFile(htmlPath)
84 | })
85 |
86 | app.get('/drag', (req, res) => {
87 | const htmlPath = path.join(__dirname, 'public', 'drag.html')
88 | res.sendFile(htmlPath)
89 | })
90 |
91 | app.get('/sts', (req, res, next) => {
92 | getSTSClient().send(new GetFederationTokenCommand({
93 | Name: '123user',
94 | // The duration, in seconds, of the role session. The value specified
95 | // can range from 900 seconds (15 minutes) up to the maximum session
96 | // duration set for the role.
97 | DurationSeconds: expiresIn,
98 | Policy: JSON.stringify(policy),
99 | })).then(response => {
100 | // ExperimentCourse11 creating multipart upload from the server — it works
101 | // createMultipartUploadYo(response)
102 | res.setHeader('Access-Control-Allow-Origin', '*')
103 | res.setHeader('Cache-Control', `public,max-age=${expiresIn}`)
104 | res.json({
105 | credentials: response.Credentials,
106 | bucket: process.env.COMPANION_AWS_BUCKET,
107 | region: process.env.COMPANION_AWS_REGION,
108 | })
109 | }, next)
110 | })
111 | app.post('/sign-s3', (req, res, next) => {
112 | const Key = `${crypto.randomUUID()}-${req.body.filename}`
113 | const { contentType } = req.body
114 |
115 | getSignedUrl(getS3Client(), new PutObjectCommand({
116 | Bucket: process.env.COMPANION_AWS_BUCKET,
117 | Key,
118 | ContentType: contentType,
119 | }), { expiresIn }).then((url) => {
120 | res.setHeader('Access-Control-Allow-Origin', '*')
121 | res.json({
122 | url,
123 | method: 'PUT',
124 | })
125 | res.end()
126 | }, next)
127 | })
128 |
129 | // === ===
130 | // You can remove those endpoints if you only want to support the non-multipart uploads.
131 |
132 | app.post('/s3/multipart', (req, res, next) => {
133 | const client = getS3Client()
134 | const { type, metadata, filename } = req.body
135 | if (typeof filename !== 'string') {
136 | return res.status(400).json({ error: 's3: content filename must be a string' })
137 | }
138 | if (typeof type !== 'string') {
139 | return res.status(400).json({ error: 's3: content type must be a string' })
140 | }
141 | const Key = `${crypto.randomUUID()}-${filename}`
142 |
143 | const params = {
144 | Bucket: process.env.COMPANION_AWS_BUCKET,
145 | Key,
146 | ContentType: type,
147 | Metadata: metadata,
148 | }
149 |
150 | const command = new CreateMultipartUploadCommand(params)
151 |
152 | return client.send(command, (err, data) => {
153 | if (err) {
154 | next(err)
155 | return
156 | }
157 | res.setHeader('Access-Control-Allow-Origin', '*')
158 | res.json({
159 | key: data.Key,
160 | uploadId: data.UploadId,
161 | })
162 | })
163 | })
164 |
165 | function validatePartNumber (partNumber) {
166 | // eslint-disable-next-line no-param-reassign
167 | partNumber = Number(partNumber)
168 | return Number.isInteger(partNumber) && partNumber >= 1 && partNumber <= 10_000
169 | }
170 | app.get('/s3/multipart/:uploadId/:partNumber', (req, res, next) => {
171 | const { uploadId, partNumber } = req.params
172 | const { key } = req.query
173 |
174 | if (!validatePartNumber(partNumber)) {
175 | return res.status(400).json({ error: 's3: the part number must be an integer between 1 and 10000.' })
176 | }
177 | if (typeof key !== 'string') {
178 | return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
179 | }
180 |
181 | return getSignedUrl(getS3Client(), new UploadPartCommand({
182 | Bucket: process.env.COMPANION_AWS_BUCKET,
183 | Key: key,
184 | UploadId: uploadId,
185 | PartNumber: partNumber,
186 | Body: '',
187 | }), { expiresIn }).then((url) => {
188 | res.setHeader('Access-Control-Allow-Origin', '*')
189 | res.json({ url, expires: expiresIn })
190 | }, next)
191 | })
192 |
193 | app.get('/s3/multipart/:uploadId', (req, res, next) => {
194 | const client = getS3Client()
195 | const { uploadId } = req.params
196 | const { key } = req.query
197 |
198 | if (typeof key !== 'string') {
199 | res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
200 | return
201 | }
202 |
203 | const parts = []
204 |
205 | function listPartsPage (startAt) {
206 | client.send(new ListPartsCommand({
207 | Bucket: process.env.COMPANION_AWS_BUCKET,
208 | Key: key,
209 | UploadId: uploadId,
210 | PartNumberMarker: startAt,
211 | }), (err, data) => {
212 | if (err) {
213 | next(err)
214 | return
215 | }
216 |
217 | parts.push(...data.Parts)
218 |
219 | if (data.IsTruncated) {
220 | // Get the next page.
221 | listPartsPage(data.NextPartNumberMarker)
222 | } else {
223 | res.json(parts)
224 | }
225 | })
226 | }
227 | listPartsPage(0)
228 | })
229 |
230 | function isValidPart (part) {
231 | return part && typeof part === 'object' && Number(part.PartNumber) && typeof part.ETag === 'string'
232 | }
233 | app.post('/s3/multipart/:uploadId/complete', (req, res, next) => {
234 | const client = getS3Client()
235 | const { uploadId } = req.params
236 | const { key } = req.query
237 | const { parts } = req.body
238 |
239 | if (typeof key !== 'string') {
240 | return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
241 | }
242 | if (!Array.isArray(parts) || !parts.every(isValidPart)) {
243 | return res.status(400).json({ error: 's3: `parts` must be an array of {ETag, PartNumber} objects.' })
244 | }
245 |
246 | return client.send(new CompleteMultipartUploadCommand({
247 | Bucket: process.env.COMPANION_AWS_BUCKET,
248 | Key: key,
249 | UploadId: uploadId,
250 | MultipartUpload: {
251 | Parts: parts,
252 | },
253 | }), (err, data) => {
254 | if (err) {
255 | next(err)
256 | return
257 | }
258 | res.setHeader('Access-Control-Allow-Origin', '*')
259 | res.json({
260 | location: data.Location,
261 | })
262 | })
263 | })
264 |
265 | app.delete('/s3/multipart/:uploadId', (req, res, next) => {
266 | const client = getS3Client()
267 | const { uploadId } = req.params
268 | const { key } = req.query
269 |
270 | if (typeof key !== 'string') {
271 | return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
272 | }
273 |
274 | return client.send(new AbortMultipartUploadCommand({
275 | Bucket: process.env.COMPANION_AWS_BUCKET,
276 | Key: key,
277 | UploadId: uploadId,
278 | }), (err) => {
279 | if (err) {
280 | next(err)
281 | return
282 | }
283 | res.json({})
284 | })
285 | })
286 |
287 | // === ===
288 |
289 | app.listen(port, () => {
290 | console.log(`Example app listening on port ${port}`)
291 | })
292 |
--------------------------------------------------------------------------------
/packages/@ExperimentCourse11/aws-s3-multipart/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @uppy/aws-s3-multipart
2 |
3 | ## 3.8.0
4 |
5 | Released: 2023-10-20
6 | Included in: Uppy v3.18.0
7 |
8 | - @uppy/aws-s3-multipart: fix `TypeError` (Antoine du Hamel / #4748)
9 | - @uppy/aws-s3-multipart: pass `signal` as separate arg for backward compat (Antoine du Hamel / #4746)
10 | - @uppy/aws-s3-multipart: fix `uploadURL` when using `PUT` (Antoine du Hamel / #4701)
11 |
12 | ## 3.7.0
13 |
14 | Released: 2023-09-29
15 | Included in: Uppy v3.17.0
16 |
17 | - @uppy/aws-s3-multipart: retry signature request (Merlijn Vos / #4691)
18 | - @uppy/aws-s3-multipart: aws-s3-multipart - call `#setCompanionHeaders` in `setOptions` (jur-ng / #4687)
19 |
20 | ## 3.6.0
21 |
22 | Released: 2023-09-05
23 | Included in: Uppy v3.15.0
24 |
25 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/core,@uppy/tus,@uppy/utils,@uppy/xhr-upload: Move remote file upload logic into companion-client (Merlijn Vos / #4573)
26 |
27 | ## 3.5.4
28 |
29 | Released: 2023-08-23
30 | Included in: Uppy v3.14.1
31 |
32 | - @uppy/aws-s3-multipart: fix types when using deprecated option (Antoine du Hamel / #4634)
33 | - @uppy/aws-s3-multipart,@uppy/aws-s3: allow empty objects for `fields` types (Antoine du Hamel / #4631)
34 |
35 | ## 3.5.3
36 |
37 | Released: 2023-08-15
38 | Included in: Uppy v3.14.0
39 |
40 | - @uppy/aws-s3-multipart: pass the `uploadURL` back to the caller (Antoine du Hamel / #4614)
41 | - @uppy/aws-s3,@uppy/aws-s3-multipart: update types (Antoine du Hamel / #4611)
42 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion,@uppy/transloadit,@uppy/xhr-upload: use uppercase HTTP method names (Antoine du Hamel / #4612)
43 | - @uppy/aws-s3,@uppy/aws-s3-multipart: update types (bdirito / #4576)
44 |
45 | ## 3.5.2
46 |
47 | Released: 2023-07-24
48 | Included in: Uppy v3.13.1
49 |
50 | - @uppy/aws-s3-multipart: refresh file before calling user-defined functions (mjlumetta / #4557)
51 |
52 | ## 3.5.1
53 |
54 | Released: 2023-07-20
55 | Included in: Uppy v3.13.0
56 |
57 | - @uppy/aws-s3-multipart: fix crash on pause/resume (Merlijn Vos / #4581)
58 | - @uppy/aws-s3-multipart: do not access `globalThis.crypto` on the top-level (Bryan J Swift / #4584)
59 |
60 | ## 3.5.0
61 |
62 | Released: 2023-07-13
63 | Included in: Uppy v3.12.0
64 |
65 | - @uppy/aws-s3-multipart: add support for signing on the client (Antoine du Hamel / #4519)
66 | - @uppy/aws-s3-multipart: fix lint warning (Antoine du Hamel / #4569)
67 | - @uppy/aws-s3-multipart: fix support for non-multipart PUT upload (Antoine du Hamel / #4568)
68 |
69 | ## 3.4.1
70 |
71 | Released: 2023-07-06
72 | Included in: Uppy v3.11.0
73 |
74 | - @uppy/aws-s3-multipart: increase priority of abort and complete (Stefan Schonert / #4542)
75 | - @uppy/aws-s3-multipart: fix upload retry using an outdated ID (Antoine du Hamel / #4544)
76 | - @uppy/aws-s3-multipart: fix Golden Retriever integration (Antoine du Hamel / #4526)
77 | - @uppy/aws-s3-multipart: add types to internal fields (Antoine du Hamel / #4535)
78 | - @uppy/aws-s3-multipart: fix pause/resume (Antoine du Hamel / #4523)
79 | - @uppy/aws-s3-multipart: fix resume single-chunk multipart uploads (Antoine du Hamel / #4528)
80 | - @uppy/aws-s3-multipart: disable pause/resume for remote uploads in the UI (Artur Paikin / #4500)
81 |
82 | ## 3.4.0
83 |
84 | Released: 2023-06-19
85 | Included in: Uppy v3.10.0
86 |
87 | - @uppy/aws-s3-multipart: fix the chunk size calculation (Antoine du Hamel / #4508)
88 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus,@uppy/utils,@uppy/xhr-upload: When file is removed (or all are canceled), controller.abort queued requests (Artur Paikin / #4504)
89 | - @uppy/aws-s3-multipart,@uppy/tus,@uppy/xhr-upload: Don't close socket while upload is still in progress (Artur Paikin / #4479)
90 | - @uppy/aws-s3-multipart: fix `getUploadParameters` option (Antoine du Hamel / #4465)
91 |
92 | ## 3.3.0
93 |
94 | Released: 2023-05-02
95 | Included in: Uppy v3.9.0
96 |
97 | - @uppy/aws-s3-multipart: allowedMetaFields: null means “include all” (Artur Paikin / #4437)
98 | - @uppy/aws-s3-multipart: add `shouldUseMultipart ` option (Antoine du Hamel / #4205)
99 | - @uppy/aws-s3-multipart: make retries more robust (Antoine du Hamel / #4424)
100 |
101 | ## 3.1.3
102 |
103 | Released: 2023-04-04
104 | Included in: Uppy v3.7.0
105 |
106 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus,@uppy/xhr-upload: make sure that we reset serverToken when an upload fails (Mikael Finstad / #4376)
107 | - @uppy/aws-s3-multipart: do not auto-open sockets, clean them up on abort (Antoine du Hamel)
108 |
109 | ## 3.1.2
110 |
111 | Released: 2023-01-26
112 | Included in: Uppy v3.4.0
113 |
114 | - @uppy/aws-s3-multipart: fix metadata shape (Antoine du Hamel / #4267)
115 | - @uppy/aws-s3-multipart: add support for `allowedMetaFields` option (Antoine du Hamel / #4215)
116 | - @uppy/aws-s3-multipart: fix singPart type (Stefan Schonert / #4224)
117 |
118 | ## 3.1.1
119 |
120 | Released: 2022-11-16
121 | Included in: Uppy v3.3.1
122 |
123 | - @uppy/aws-s3-multipart: handle slow connections better (Antoine du Hamel / #4213)
124 | - @uppy/aws-s3-multipart: Fix typo in url check (Christian Franke / #4211)
125 |
126 | ## 3.1.0
127 |
128 | Released: 2022-11-10
129 | Included in: Uppy v3.3.0
130 |
131 | - @uppy/aws-s3-multipart: empty the queue when pausing (Antoine du Hamel / #4203)
132 | - @uppy/aws-s3-multipart: refactor rate limiting approach (Antoine du Hamel / #4187)
133 | - @uppy/aws-s3-multipart: change limit to 6 (Antoine du Hamel / #4199)
134 | - @uppy/aws-s3-multipart: remove unused `timeout` option (Antoine du Hamel / #4186)
135 | - @uppy/aws-s3-multipart,@uppy/tus: fix `Timed out waiting for socket` (Antoine du Hamel / #4177)
136 |
137 | ## 3.0.2
138 |
139 | Released: 2022-09-25
140 | Included in: Uppy v3.1.0
141 |
142 | - @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
143 |
144 | ## 3.0.0
145 |
146 | Released: 2022-08-22
147 | Included in: Uppy v3.0.0
148 |
149 | - Switch to ESM
150 |
151 | ## 3.0.0-beta.4
152 |
153 | Released: 2022-08-16
154 | Included in: Uppy v3.0.0-beta.5
155 |
156 | - @uppy/aws-s3-multipart: Fix when using Companion (Merlijn Vos / #3969)
157 | - @uppy/aws-s3-multipart: Fix race condition in `#uploadParts` (Morgan Zolob / #3955)
158 | - @uppy/aws-s3-multipart: ignore exception inside `abortMultipartUpload` (Antoine du Hamel / #3950)
159 |
160 | ## 3.0.0-beta.3
161 |
162 | Released: 2022-08-03
163 | Included in: Uppy v3.0.0-beta.4
164 |
165 | - @uppy/aws-s3-multipart: Correctly handle errors for `prepareUploadParts` (Merlijn Vos / #3912)
166 |
167 | ## 3.0.0-beta.2
168 |
169 | Released: 2022-07-27
170 | Included in: Uppy v3.0.0-beta.3
171 |
172 | - @uppy/aws-s3-multipart: make `headers` part indexed too in `prepareUploadParts` (Merlijn Vos / #3895)
173 |
174 | ## 2.4.1
175 |
176 | Released: 2022-06-07
177 | Included in: Uppy v2.12.0
178 |
179 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
180 | - @uppy/aws-s3-multipart: allow `companionHeaders` to be modified with `setOptions` (Paulo Lemos Neto / #3770)
181 |
182 | ## 2.4.0
183 |
184 | Released: 2022-05-30
185 | Included in: Uppy v2.11.0
186 |
187 | - @uppy/angular,@uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/onedrive,@uppy/progress-bar,@uppy/react,@uppy/redux-dev-tools,@uppy/robodog,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: doc: update bundler recommendation (Antoine du Hamel / #3763)
188 | - @uppy/aws-s3-multipart: refactor to ESM (Antoine du Hamel / #3672)
189 |
190 | ## 2.3.0
191 |
192 | Released: 2022-05-14
193 | Included in: Uppy v2.10.0
194 |
195 | - @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/core,@uppy/react,@uppy/transloadit,@uppy/tus,@uppy/xhr-upload: proposal: Cancel assemblies optional (Mikael Finstad / #3575)
196 | - @uppy/aws-s3-multipart: export interface AwsS3MultipartOptions (Matteo Padovano / #3709)
197 |
198 | ## 2.2.2
199 |
200 | Released: 2022-04-27
201 | Included in: Uppy v2.9.4
202 |
203 | - @uppy/aws-s3-multipart: Add `companionCookiesRule` type to @uppy/aws-s3-multipart (Mauricio Ribeiro / #3623)
204 |
205 | ## 2.2.1
206 |
207 | Released: 2022-03-02
208 | Included in: Uppy v2.7.0
209 |
210 | - @uppy/aws-s3-multipart: Add chunks back to prepareUploadParts, indexed by partNumber (Kevin West / #3520)
211 |
212 | ## 2.2.0
213 |
214 | Released: 2021-12-07
215 | Included in: Uppy v2.3.0
216 |
217 | - @uppy/aws-s3-multipart: Drop `lockedCandidatesForBatch` and mark chunks as 'busy' when preparing (Yegor Yarko / #3342)
218 |
--------------------------------------------------------------------------------
/aws-nodejs/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ExperimentCourse11 – AWS upload example
6 |
10 |
11 |
12 | AWS upload example
13 |
14 |
266 |
267 |
268 |
--------------------------------------------------------------------------------