├── .browserslistrc ├── .dockerignore ├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .remarkignore ├── .stylelintrc.json ├── .vscode ├── petProj.bak └── petProj.code-workspace ├── .yarn ├── patches │ ├── p-queue-npm-7.4.1-e0cf0a6f17.patch │ ├── pre-commit-npm-1.2.2-f30af83877.patch │ ├── preact-npm-10.10.0-dd04de05e8.patch │ ├── start-server-and-test-npm-1.14.0-841aa34fdf.patch │ ├── stylelint-config-rational-order-npm-0.1.2-d8336e84ed.patch │ └── uuid-npm-8.3.2-eca0baba53.patch └── plugins │ └── @yarnpkg │ ├── plugin-version.cjs │ └── plugin-workspace-tools.cjs ├── .yarnrc.yml ├── BUNDLE-README.md ├── Dockerfile ├── Dockerfile.test ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md └── axamples ├── angular-example ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── README.md ├── angular.json ├── package.json ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.ts │ │ └── app.module.ts │ ├── assets │ │ └── .gitkeep │ ├── index.html │ ├── main.ts │ └── styles.css ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json ├── aws-companion ├── .gitignore ├── README.md ├── index.html ├── main.js ├── package.json └── server.cjs ├── aws-nodejs ├── README.md ├── index.js ├── package.json └── public │ ├── drag.html │ └── index.html ├── petProj-with-companion ├── .gitignore ├── README.md ├── client │ └── index.html ├── output │ └── .empty ├── package.json └── server │ └── index.js ├── php-xhr ├── .gitignore ├── README.md ├── index.html ├── main.js ├── package.json └── server.php ├── python-xhr ├── .gitignore ├── README.md ├── index.html ├── main.js ├── package.json ├── requirements.txt └── server.py ├── react-example ├── App.jsx ├── README.md ├── index.html ├── main.jsx ├── package.json └── vite.config.js ├── react-native-expo ├── .eslintrc.json ├── .expo-shared │ └── assets.json ├── .gitignore ├── App.js ├── FileList.js ├── PauseResumeButton.js ├── ProgressBar.js ├── SelectFilesButton.js ├── app.json ├── babel.config.js ├── index.js ├── metro.config.js ├── package.json ├── readme.md └── tusFileReader.js ├── redux ├── README.md ├── index.html ├── main.js └── package.json ├── svelte-example ├── .gitignore ├── README.md ├── package.json ├── postcss.config.js ├── public │ ├── global.css │ └── index.html ├── rollup.config.js ├── server.mjs ├── src │ ├── App.svelte │ └── main.ts └── tsconfig.json ├── transloadit-markdown-bin ├── README.md ├── index.html ├── main.js └── package.json ├── transloadit ├── .gitignore ├── README.md ├── index.html ├── main.js ├── package.json └── server.js ├── vue ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.vue │ └── main.js └── vite.config.js ├── vue3 ├── .gitignore ├── README.md ├── index.html ├── package.json ├── public │ └── favicon.ico ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ └── main.js └── vite.config.js └── xhr-bundle ├── README.md └── main.js /.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 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | .git 3 | website 4 | assets 5 | private 6 | e2e 7 | .env 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/latest/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 test dynamic Oauth against local companion (which is pointless but allows us to test it without Transloadit's servers), enable these: 58 | #COMPANION_GOOGLE_KEYS_ENDPOINT=http://localhost:3020/drive/test-dynamic-oauth-credentials?secret=development 59 | #COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS=true 60 | #COMPANION_TEST_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 test 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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | dist 4 | coverage 5 | test/lib/** 6 | test/endtoend/*/build 7 | examples/svelte-example/public/build/ 8 | bundle-legacy.js 9 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable quote-props */ 2 | 3 | 'use strict' 4 | 5 | const svgPresentationAttributes = [ 6 | 'alignment-baseline', 'baseline-shift', 'class', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolatio', 'color-interpolatio-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'transform', 'transform-origin', 'unicode-bidi', 'vector-effect', 'visibility', 'word-spacing', 'writing-mod', 7 | ] 8 | 9 | module.exports = { 10 | root: true, 11 | extends: ['transloadit', 'prettier'], 12 | env: { 13 | es6: true, 14 | jest: true, 15 | node: true, 16 | // extra: 17 | browser: true, 18 | }, 19 | globals: { 20 | globalThis: true, 21 | hexo: true, 22 | window: true, 23 | }, 24 | plugins: [ 25 | '@babel/eslint-plugin', 26 | 'jest', 27 | 'markdown', 28 | 'node', 29 | 'prefer-import', 30 | 'promise', 31 | 'react', 32 | // extra: 33 | 'compat', 34 | 'jsdoc', 35 | 'no-only-tests', 36 | 'unicorn', 37 | ], 38 | parser: '@babel/eslint-parser', 39 | parserOptions: { 40 | sourceType: 'script', 41 | ecmaVersion: 2022, 42 | ecmaFeatures: { 43 | jsx: true, 44 | }, 45 | }, 46 | rules: { 47 | // transloadit rules we are actually ok with in the petProj repo 48 | 'import/extensions': 'off', 49 | 'object-shorthand': ['error', 'always'], 50 | 'strict': 'off', 51 | 'key-spacing': 'off', 52 | 'max-classes-per-file': ['error', 2], 53 | 'react/no-unknown-property': ['error', { 54 | ignore: svgPresentationAttributes, 55 | }], 56 | 57 | // Special rules for CI: 58 | ...(process.env.CI && { 59 | // Some imports are available only after a full build, which we don't do on CI. 60 | 'import/no-unresolved': 'off', 61 | }), 62 | 63 | // rules we want to enforce 64 | 'array-callback-return': 'error', 65 | 'func-names': 'error', 66 | 'import/no-dynamic-require': 'error', 67 | 'import/no-extraneous-dependencies': 'error', 68 | 'max-len': 'error', 69 | 'no-empty': 'error', 70 | 'no-bitwise': 'error', 71 | 'no-continue': 'error', 72 | 'no-lonely-if': 'error', 73 | 'no-nested-ternary': 'error', 74 | 'no-restricted-properties': 'error', 75 | 'no-return-assign': 'error', 76 | 'no-underscore-dangle': 'error', 77 | 'no-unused-expressions': 'error', 78 | 'no-unused-vars': 'error', 79 | 'no-useless-concat': 'error', 80 | 'no-var': 'error', 81 | 'node/handle-callback-err': 'error', 82 | 'prefer-destructuring': 'error', 83 | 'prefer-spread': 'error', 84 | 'unicorn/prefer-node-protocol': 'error', 85 | 86 | 'react/button-has-type': 'error', 87 | 'react/forbid-prop-types': 'error', 88 | 'react/no-access-state-in-setstate': 'error', 89 | 'react/no-array-index-key': 'error', 90 | 'react/no-deprecated': 'error', 91 | 'react/no-this-in-sfc': 'error', 92 | 'react/no-will-update-set-state': 'error', 93 | 'react/prefer-stateless-function': 'error', 94 | 'react/sort-comp': 'error', 95 | 'react/style-prop-object': 'error', 96 | 97 | // accessibility 98 | 'jsx-a11y/alt-text': 'error', 99 | 'jsx-a11y/anchor-has-content': 'error', 100 | 'jsx-a11y/click-events-have-key-events': 'error', 101 | 'jsx-a11y/control-has-associated-label': 'error', 102 | 'jsx-a11y/label-has-associated-control': 'error', 103 | 'jsx-a11y/media-has-caption': 'error', 104 | 'jsx-a11y/mouse-events-have-key-events': 'error', 105 | 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error', 106 | 'jsx-a11y/no-noninteractive-element-interactions': 'error', 107 | 'jsx-a11y/no-static-element-interactions': 'error', 108 | 109 | // compat 110 | 'compat/compat': ['error'], 111 | 112 | // jsdoc 113 | 'jsdoc/check-alignment': 'error', 114 | 'jsdoc/check-examples': 'off', // cannot yet be supported for ESLint 8, see https://github.com/eslint/eslint/issues/14745 115 | 'jsdoc/check-param-names': 'error', 116 | 'jsdoc/check-syntax': 'error', 117 | 'jsdoc/check-tag-names': ['error', { jsxTags: true }], 118 | 'jsdoc/check-types': 'error', 119 | 'jsdoc/newline-after-description': 'error', 120 | 'jsdoc/valid-types': 'error', 121 | 'jsdoc/check-indentation': ['off'], 122 | }, 123 | 124 | settings: { 125 | 'import/core-modules': ['tsd'], 126 | react: { 127 | pragma: 'h', 128 | }, 129 | jsdoc: { 130 | mode: 'typescript', 131 | }, 132 | polyfills: [ 133 | 'Promise', 134 | 'fetch', 135 | 'Object.assign', 136 | 'document.querySelector', 137 | ], 138 | }, 139 | 140 | overrides: [ 141 | { 142 | files: [ 143 | '*.jsx', 144 | '*.tsx', 145 | 'packages/@petProj/react-native/**/*.js', 146 | ], 147 | parser: 'espree', 148 | parserOptions: { 149 | sourceType: 'module', 150 | ecmaFeatures: { 151 | jsx: true, 152 | }, 153 | }, 154 | rules: { 155 | 'no-restricted-globals': [ 156 | 'error', 157 | { 158 | name: '__filename', 159 | message: 'Use import.meta.url instead', 160 | }, 161 | { 162 | name: '__dirname', 163 | message: 'Not available in ESM', 164 | }, 165 | { 166 | name: 'exports', 167 | message: 'Not available in ESM', 168 | }, 169 | { 170 | name: 'module', 171 | message: 'Not available in ESM', 172 | }, 173 | { 174 | name: 'require', 175 | message: 'Use import instead', 176 | }, 177 | ], 178 | 'import/extensions': ['error', 'ignorePackages'], 179 | }, 180 | }, 181 | { 182 | files: [ 183 | '*.mjs', 184 | 'e2e/clients/**/*.js', 185 | 'examples/aws-companion/*.js', 186 | 'examples/aws-php/*.js', 187 | 'examples/bundled/*.js', 188 | 'examples/custom-provider/client/*.js', 189 | 'examples/digitalocean-spaces/*.js', 190 | 'examples/multiple-instances/*.js', 191 | 'examples/node-xhr/*.js', 192 | 'examples/php-xhr/*.js', 193 | 'examples/python-xhr/*.js', 194 | 'examples/react-example/*.js', 195 | 'examples/redux/*.js', 196 | 'examples/transloadit/*.js', 197 | 'examples/transloadit-markdown-bin/*.js', 198 | 'examples/xhr-bundle/*.js', 199 | 'private/dev/*.js', 200 | 'private/release/*.js', 201 | 'private/remark-lint-petProj/*.js', 202 | 203 | // Packages that have switched to ESM sources: 204 | 'packages/@petProj/audio/src/**/*.js', 205 | 'packages/@petProj/aws-s3-multipart/src/**/*.js', 206 | 'packages/@petProj/aws-s3/src/**/*.js', 207 | 'packages/@petProj/box/src/**/*.js', 208 | 'packages/@petProj/companion-client/src/**/*.js', 209 | 'packages/@petProj/compressor/src/**/*.js', 210 | 'packages/@petProj/core/src/**/*.js', 211 | 'packages/@petProj/dashboard/src/**/*.js', 212 | 'packages/@petProj/drag-drop/src/**/*.js', 213 | 'packages/@petProj/drop-target/src/**/*.js', 214 | 'packages/@petProj/dropbox/src/**/*.js', 215 | 'packages/@petProj/facebook/src/**/*.js', 216 | 'packages/@petProj/file-input/src/**/*.js', 217 | 'packages/@petProj/form/src/**/*.js', 218 | 'packages/@petProj/golden-retriever/src/**/*.js', 219 | 'packages/@petProj/google-drive/src/**/*.js', 220 | 'packages/@petProj/image-editor/src/**/*.js', 221 | 'packages/@petProj/informer/src/**/*.js', 222 | 'packages/@petProj/instagram/src/**/*.js', 223 | 'packages/@petProj/locales/src/**/*.js', 224 | 'packages/@petProj/locales/template.js', 225 | 'packages/@petProj/onedrive/src/**/*.js', 226 | 'packages/@petProj/progress-bar/src/**/*.js', 227 | 'packages/@petProj/provider-views/src/**/*.js', 228 | 'packages/@petProj/react/src/**/*.js', 229 | 'packages/@petProj/redux-dev-tools/src/**/*.js', 230 | 'packages/@petProj/remote-sources/src/**/*.js', 231 | 'packages/@petProj/screen-capture/src/**/*.js', 232 | 'packages/@petProj/status-bar/src/**/*.js', 233 | 'packages/@petProj/store-default/src/**/*.js', 234 | 'packages/@petProj/store-redux/src/**/*.js', 235 | 'packages/@petProj/svelte/rollup.config.js', 236 | 'packages/@petProj/svelte/src/**/*.js', 237 | 'packages/@petProj/thumbnail-generator/src/**/*.js', 238 | 'packages/@petProj/transloadit/src/**/*.js', 239 | 'packages/@petProj/tus/src/**/*.js', 240 | 'packages/@petProj/unsplash/src/**/*.js', 241 | 'packages/@petProj/url/src/**/*.js', 242 | 'packages/@petProj/utils/src/**/*.js', 243 | 'packages/@petProj/vue/src/**/*.js', 244 | 'packages/@petProj/webcam/src/**/*.js', 245 | 'packages/@petProj/xhr-upload/src/**/*.js', 246 | 'packages/@petProj/zoom/src/**/*.js', 247 | ], 248 | parser: 'espree', 249 | parserOptions: { 250 | sourceType: 'module', 251 | ecmaFeatures: { 252 | jsx: false, 253 | }, 254 | }, 255 | rules: { 256 | 'import/named': 'off', // Disabled because that rule tries and fails to parse JSX dependencies. 257 | 'import/no-named-as-default': 'off', // Disabled because that rule tries and fails to parse JSX dependencies. 258 | 'import/no-named-as-default-member': 'off', // Disabled because that rule tries and fails to parse JSX dependencies. 259 | 'no-restricted-globals': [ 260 | 'error', 261 | { 262 | name: '__filename', 263 | message: 'Use import.meta.url instead', 264 | }, 265 | { 266 | name: '__dirname', 267 | message: 'Not available in ESM', 268 | }, 269 | { 270 | name: 'exports', 271 | message: 'Not available in ESM', 272 | }, 273 | { 274 | name: 'module', 275 | message: 'Not available in ESM', 276 | }, 277 | { 278 | name: 'require', 279 | message: 'Use import instead', 280 | }, 281 | ], 282 | 'import/extensions': ['error', 'ignorePackages'], 283 | }, 284 | }, 285 | { 286 | files: ['packages/petProj/*.mjs'], 287 | rules: { 288 | 'import/first': 'off', 289 | 'import/newline-after-import': 'off', 290 | 'import/no-extraneous-dependencies': ['error', { 291 | devDependencies: true, 292 | }], 293 | }, 294 | }, 295 | { 296 | files: [ 297 | 'packages/@petProj/*/types/*.d.ts', 298 | ], 299 | rules : { 300 | 'import/no-unresolved': 'off', 301 | 'max-classes-per-file': 'off', 302 | 'no-use-before-define': 'off', 303 | }, 304 | }, 305 | { 306 | files: [ 307 | 'packages/@petProj/dashboard/src/components/**/*.jsx', 308 | ], 309 | rules: { 310 | 'react/destructuring-assignment': 'off', 311 | }, 312 | }, 313 | { 314 | files: [ 315 | // Those need looser rules, and cannot be made part of the stricter rules above. 316 | // TODO: update those to more modern code when switch to ESM is complete 317 | 'examples/react-native-expo/*.js', 318 | 'examples/svelte-example/**/*.js', 319 | 'examples/vue/**/*.js', 320 | 'examples/vue3/**/*.js', 321 | ], 322 | rules: { 323 | 'no-unused-vars': [ 324 | 'error', 325 | { 326 | 'varsIgnorePattern': 'React', 327 | }, 328 | ], 329 | }, 330 | parserOptions: { 331 | sourceType: 'module', 332 | }, 333 | }, 334 | { 335 | files: ['./packages/@petProj/companion/**/*.js'], 336 | rules: { 337 | 'no-underscore-dangle': 'off', 338 | }, 339 | }, 340 | { 341 | files: [ 342 | '*.test.js', 343 | 'test/endtoend/*.js', 344 | 'bin/**.js', 345 | ], 346 | rules: { 347 | 'compat/compat': ['off'], 348 | }, 349 | }, 350 | { 351 | files: [ 352 | 'bin/**.js', 353 | 'bin/**.mjs', 354 | 'examples/**/*.cjs', 355 | 'examples/**/*.js', 356 | 'packages/@petProj/companion/test/**/*.js', 357 | 'test/**/*.js', 358 | 'test/**/*.ts', 359 | '*.test.js', 360 | '*.test.ts', 361 | '*.test-d.ts', 362 | '*.test-d.tsx', 363 | 'postcss.config.js', 364 | '.eslintrc.js', 365 | 'private/**/*.js', 366 | 'private/**/*.mjs', 367 | ], 368 | rules: { 369 | 'no-console': 'off', 370 | 'import/no-extraneous-dependencies': ['error', { 371 | devDependencies: true, 372 | }], 373 | }, 374 | }, 375 | 376 | { 377 | files: [ 378 | 'packages/@petProj/locales/src/*.js', 379 | 'packages/@petProj/locales/template.js', 380 | ], 381 | rules: { 382 | camelcase: ['off'], 383 | 'quote-props': ['error', 'as-needed', { 'numbers': true }], 384 | }, 385 | }, 386 | 387 | { 388 | files: ['test/endtoend/*/*.mjs', 'test/endtoend/*/*.ts'], 389 | rules: { 390 | // we mostly import @petProj stuff in these files. 391 | 'import/no-extraneous-dependencies': ['off'], 392 | }, 393 | }, 394 | { 395 | files: ['test/endtoend/*/*.js'], 396 | env: { 397 | mocha: true, 398 | }, 399 | }, 400 | 401 | { 402 | files: ['packages/@petProj/react/src/**/*.js'], 403 | rules: { 404 | 'import/no-extraneous-dependencies': ['error', { 405 | peerDependencies: true, 406 | }], 407 | }, 408 | }, 409 | 410 | { 411 | files: ['**/*.md', '*.md'], 412 | processor: 'markdown/markdown', 413 | }, 414 | { 415 | files: ['**/*.md/*.js', '**/*.md/*.javascript'], 416 | parserOptions: { 417 | sourceType: 'module', 418 | }, 419 | rules: { 420 | 'react/destructuring-assignment': 'off', 421 | 'no-restricted-globals': [ 422 | 'error', 423 | { 424 | name: '__filename', 425 | message: 'Use import.meta.url instead', 426 | }, 427 | { 428 | name: '__dirname', 429 | message: 'Not available in ESM', 430 | }, 431 | { 432 | name: 'exports', 433 | message: 'Not available in ESM', 434 | }, 435 | { 436 | name: 'module', 437 | message: 'Not available in ESM', 438 | }, 439 | { 440 | name: 'require', 441 | message: 'Use import instead', 442 | }, 443 | ], 444 | }, 445 | }, 446 | { 447 | files: ['**/*.ts', '**/*.md/*.ts', '**/*.md/*.typescript'], 448 | excludedFiles: ['examples/angular-example/**/*.ts', 'packages/@petProj/angular/**/*.ts'], 449 | parser: '@typescript-eslint/parser', 450 | settings: { 451 | 'import/resolver': { 452 | node: { 453 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 454 | }, 455 | }, 456 | }, 457 | plugins: ['@typescript-eslint'], 458 | extends: [ 459 | 'eslint:recommended', 460 | 'plugin:@typescript-eslint/eslint-recommended', 461 | 'plugin:@typescript-eslint/recommended', 462 | ], 463 | rules: { 464 | 'import/prefer-default-export': 'off', 465 | '@typescript-eslint/no-explicit-any': 'off', 466 | '@typescript-eslint/no-extra-semi': 'off', 467 | '@typescript-eslint/no-namespace': 'off', 468 | }, 469 | }, 470 | { 471 | files: ['packages/@petProj/*/src/**/*.ts', 'packages/@petProj/*/src/**/*.tsx'], 472 | excludedFiles: ['packages/@petProj/**/*.test.ts'], 473 | rules: { 474 | '@typescript-eslint/explicit-function-return-type': 'error', 475 | }, 476 | }, 477 | { 478 | files: ['**/*.md/*.*'], 479 | rules: { 480 | 'import/no-extraneous-dependencies': 'off', 481 | 'import/no-unresolved': 'off', 482 | 'no-console': 'off', 483 | 'no-undef': 'off', 484 | 'no-unused-vars': 'off', 485 | }, 486 | }, 487 | { 488 | files: ['**/react/*.md/*.js', '**/react.md/*.js', '**/react-*.md/*.js', '**/react/**/*.test-d.tsx'], 489 | settings: { 490 | react: { pragma: 'React' }, 491 | }, 492 | }, 493 | { 494 | files: ['**/react/**/*.test-d.tsx'], 495 | rules: { 496 | 'import/extensions': 'off', 497 | 'import/no-useless-path-segments': 'off', 498 | 'no-alert': 'off', 499 | 'no-inner-declarations': 'off', 500 | 'no-lone-blocks': 'off', 501 | 'no-unused-expressions': 'off', 502 | 'no-unused-vars': 'off', 503 | }, 504 | }, 505 | { 506 | files: ['e2e/**/*.ts'], 507 | extends: ['plugin:cypress/recommended'], 508 | }, 509 | { 510 | files: ['e2e/**/*.ts', 'e2e/**/*.js', 'e2e/**/*.jsx', 'e2e/**/*.mjs'], 511 | rules: { 512 | 'import/no-extraneous-dependencies': 'off', 513 | 'no-console': 'off', 514 | 'no-only-tests/no-only-tests': 'error', 515 | 'no-unused-expressions': 'off', 516 | }, 517 | }, 518 | ], 519 | } 520 | -------------------------------------------------------------------------------- /.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 | test/endtoend/create-react-app/build/ 26 | test/endtoend/create-react-app/coverage/ 27 | petProj-*.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 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.js 3 | *.jsx 4 | *.cjs 5 | *.mjs 6 | !private/js2ts/* 7 | *.md 8 | *.lock 9 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | proseWrap: 'always', 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | semi: false, 6 | overrides: [ 7 | { 8 | files: 'packages/@petProj/angular/**', 9 | options: { 10 | semi: true, 11 | }, 12 | }, 13 | ], 14 | } 15 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.vscode/petProj.bak: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "workbench.colorCustomizations": { 9 | "titleBar.activeForeground": "#ffffff", 10 | "titleBar.activeBackground": "#ff009d", 11 | }, 12 | "search.exclude": { 13 | "website/public/": true, 14 | "node_modules/": true, 15 | "website/node_modules/": true, 16 | "dist/": true, 17 | "lib/": true, 18 | "package-lock.json": true, 19 | "website/package-lock.json": true, 20 | "yarn-error.log": true, 21 | "website/.deploy_git": true, 22 | "npm-debug.log": true, 23 | "website/npm-debug.log": true, 24 | "website/debug.log": true, 25 | "nohup.out": true, 26 | "yarn.lock": true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/petProj.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".." 5 | } 6 | ], 7 | "settings": { 8 | "workbench.colorCustomizations": { 9 | "titleBar.activeForeground": "#ffffff", 10 | "titleBar.activeBackground": "#ff009d", 11 | }, 12 | "search.exclude": { 13 | "website/public/": true, 14 | "node_modules/": true, 15 | "website/node_modules/": true, 16 | "dist/": true, 17 | "lib/": true, 18 | "package-lock.json": true, 19 | "website/package-lock.json": true, 20 | "yarn-error.log": true, 21 | "website/.deploy_git": true, 22 | "npm-debug.log": true, 23 | "website/npm-debug.log": true, 24 | "website/debug.log": true, 25 | "nohup.out": true, 26 | "yarn.lock": true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.yarn/patches/p-queue-npm-7.4.1-e0cf0a6f17.patch: -------------------------------------------------------------------------------- 1 | diff --git a/package.json b/package.json 2 | index 8367745346fffd144a817ccf04912bb799e18b66..66dd17a4cd736089a332d72a70040701b0cd9c93 100644 3 | --- a/package.json 4 | +++ b/package.json 5 | @@ -6,6 +6,7 @@ 6 | "repository": "sindresorhus/p-queue", 7 | "funding": "https://github.com/sponsors/sindresorhus", 8 | "type": "module", 9 | + "main": "./dist/index.js", 10 | "exports": "./dist/index.js", 11 | "engines": { 12 | "node": ">=12" 13 | -------------------------------------------------------------------------------- /.yarn/patches/pre-commit-npm-1.2.2-f30af83877.patch: -------------------------------------------------------------------------------- 1 | diff --git a/index.js b/index.js 2 | index a20646d922945004cb737918ef6b6d063bb3c2a4..a44863e9555abdaa569f309b1197fddc8dd244a5 100644 3 | --- a/index.js 4 | +++ b/index.js 5 | @@ -147,7 +147,7 @@ Hook.prototype.log = function log(lines, exit) { 6 | * @api private 7 | */ 8 | Hook.prototype.initialize = function initialize() { 9 | - ['git', 'npm'].forEach(function each(binary) { 10 | + ['git', 'corepack'].forEach(function each(binary) { 11 | try { this[binary] = which.sync(binary); } 12 | catch (e) {} 13 | }, this); 14 | @@ -159,9 +159,9 @@ Hook.prototype.initialize = function initialize() { 15 | if (!this.npm) { 16 | try { 17 | process.env.PATH += path.delimiter + path.dirname(process.env._); 18 | - this.npm = which.sync('npm'); 19 | + this.npm = which.sync('corepack'); 20 | } catch (e) { 21 | - return this.log(this.format(Hook.log.binary, 'npm'), 0); 22 | + return this.log(this.format(Hook.log.binary, 'corepack'), 0); 23 | } 24 | } 25 | 26 | @@ -225,7 +225,7 @@ Hook.prototype.run = function runner() { 27 | // this doesn't have the required `isAtty` information that libraries use to 28 | // output colors resulting in script output that doesn't have any color. 29 | // 30 | - spawn(hooked.npm, ['run', script, '--silent'], { 31 | + spawn(hooked.npm, ['yarn', script], { 32 | env: process.env, 33 | cwd: hooked.root, 34 | stdio: [0, 1, 2] 35 | -------------------------------------------------------------------------------- /.yarn/patches/preact-npm-10.10.0-dd04de05e8.patch: -------------------------------------------------------------------------------- 1 | diff --git a/debug/package.json b/debug/package.json 2 | index 054944f5478a0a5cf7b6b8791950c595f956157b..06a4fe2719605eb42c5ee795101c21cfd10b59ce 100644 3 | --- a/debug/package.json 4 | +++ b/debug/package.json 5 | @@ -9,6 +9,7 @@ 6 | "umd:main": "dist/debug.umd.js", 7 | "source": "src/index.js", 8 | "license": "MIT", 9 | + "type": "module", 10 | "mangle": { 11 | "regex": "^(?!_renderer)^_" 12 | }, 13 | diff --git a/devtools/package.json b/devtools/package.json 14 | index 09b04a77690bdfba01083939ff9eaf987dd50bcb..92c159fbb3cf312c6674202085fb237d6fb921ad 100644 15 | --- a/devtools/package.json 16 | +++ b/devtools/package.json 17 | @@ -10,6 +10,7 @@ 18 | "source": "src/index.js", 19 | "license": "MIT", 20 | "types": "src/index.d.ts", 21 | + "type": "module", 22 | "peerDependencies": { 23 | "preact": "^10.0.0" 24 | }, 25 | diff --git a/hooks/package.json b/hooks/package.json 26 | index 74807025bf3de273ebada2cd355428a2c972503d..98501726ffbfe55ffa09928e56a9dcafb9a348ff 100644 27 | --- a/hooks/package.json 28 | +++ b/hooks/package.json 29 | @@ -10,6 +10,7 @@ 30 | "source": "src/index.js", 31 | "license": "MIT", 32 | "types": "src/index.d.ts", 33 | + "type": "module", 34 | "scripts": { 35 | "build": "microbundle build --raw", 36 | "dev": "microbundle watch --raw --format cjs", 37 | diff --git a/jsx-runtime/package.json b/jsx-runtime/package.json 38 | index 7a4027831223f16519a74e3028c34f2f8f5f011a..6b58d17dbacce81894467ef43c0a8e2435e388c4 100644 39 | --- a/jsx-runtime/package.json 40 | +++ b/jsx-runtime/package.json 41 | @@ -10,6 +10,7 @@ 42 | "source": "src/index.js", 43 | "types": "src/index.d.ts", 44 | "license": "MIT", 45 | + "type": "module", 46 | "peerDependencies": { 47 | "preact": "^10.0.0" 48 | }, 49 | diff --git a/package.json b/package.json 50 | index 60279c24a08b808ffbf7dc64a038272bddb6785d..088f35fb2c92f2e9b7248557857af2839988d1aa 100644 51 | --- a/package.json 52 | +++ b/package.json 53 | @@ -9,6 +9,7 @@ 54 | "umd:main": "dist/preact.umd.js", 55 | "unpkg": "dist/preact.min.js", 56 | "source": "src/index.js", 57 | + "type": "module", 58 | "exports": { 59 | ".": { 60 | "types": "./src/index.d.ts", 61 | -------------------------------------------------------------------------------- /.yarn/patches/start-server-and-test-npm-1.14.0-841aa34fdf.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/utils.js b/src/utils.js 2 | index 1f636c6617a71a68318dc587a1c9e6081020f9aa..b28e840ed08f26a4eadd242a6f541fbaefea0eda 100644 3 | --- a/src/utils.js 4 | +++ b/src/utils.js 5 | @@ -112,7 +112,7 @@ const getArguments = cliArgs => { 6 | } 7 | 8 | function normalizeCommand (command) { 9 | - return UTILS.isPackageScriptName(command) ? `npm run ${command}` : command 10 | + return UTILS.isPackageScriptName(command) ? `corepack yarn ${command}` : command 11 | } 12 | 13 | /** 14 | -------------------------------------------------------------------------------- /.yarn/patches/stylelint-config-rational-order-npm-0.1.2-d8336e84ed.patch: -------------------------------------------------------------------------------- 1 | diff --git a/package.json b/package.json 2 | index a2047a8b2895a64a4cbf7b493362ee1d72c43771..7478198712b460936f6b7f2557b116c52f4d71b5 100644 3 | --- a/package.json 4 | +++ b/package.json 5 | @@ -30,8 +30,8 @@ 6 | "order" 7 | ], 8 | "dependencies": { 9 | - "stylelint": "^9.10.1", 10 | - "stylelint-order": "^2.2.1" 11 | + "stylelint": "^15.0.1", 12 | + "stylelint-order": "^6.0.3" 13 | }, 14 | "devDependencies": { 15 | "eslint": "^5.16.0", 16 | -------------------------------------------------------------------------------- /.yarn/patches/uuid-npm-8.3.2-eca0baba53.patch: -------------------------------------------------------------------------------- 1 | diff --git a/package.json b/package.json 2 | index f0ab3711ee4f490cbf961ebe6283ce2a28b6824b..644235a3ef52c974e946403a3fcdd137d01fad0c 100644 3 | --- a/package.json 4 | +++ b/package.json 5 | @@ -25,6 +25,7 @@ 6 | "require": "./dist/index.js", 7 | "import": "./wrapper.mjs" 8 | }, 9 | + "jest": "./dist/index.js", 10 | "default": "./dist/esm-browser/index.js" 11 | }, 12 | "./package.json": "./package.json" 13 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | changesetBaseRefs: 2 | - main 3 | - upstream/main 4 | - origin/main 5 | 6 | initScope: petProj 7 | 8 | enableGlobalCache: false 9 | nodeLinker: node-modules 10 | 11 | plugins: 12 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 13 | spec: '@yarnpkg/plugin-workspace-tools' 14 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs 15 | spec: '@yarnpkg/plugin-version' 16 | -------------------------------------------------------------------------------- /BUNDLE-README.md: -------------------------------------------------------------------------------- 1 | # petProj 2 | 3 | Note that the recommended way to use petProj 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/petProj`. 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 petProj Dashboard will appear. 59 | 60 | ## Next steps 61 | 62 | In the example you built, petProj 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 | petProj 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 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.17.1-alpine as build 2 | 3 | # Create link to node on amd64 so that corepack can find it 4 | RUN if [ "$(uname -m)" == "aarch64" ]; then mkdir -p /usr/local/sbin/ && ln -s /usr/local/bin/node /usr/local/sbin/node; fi 5 | 6 | WORKDIR /app 7 | 8 | COPY . /app/ 9 | 10 | RUN apk --update add --virtual native-dep \ 11 | make gcc g++ python3 libgcc libstdc++ git && \ 12 | (cd /app && corepack yarn workspaces focus @petProj/companion) && \ 13 | apk del native-dep 14 | 15 | RUN cd /app && corepack yarn workspace @petProj/companion build 16 | 17 | # Now remove all non-prod dependencies for a leaner image 18 | RUN cd /app && corepack yarn workspaces focus @petProj/companion --production 19 | 20 | FROM node:18.17.1-alpine 21 | 22 | WORKDIR /app 23 | 24 | # copy required files from build stage. 25 | COPY --from=build /app/packages/@petProj/companion/bin /app/bin 26 | COPY --from=build /app/packages/@petProj/companion/lib /app/lib 27 | COPY --from=build /app/packages/@petProj/companion/package.json /app/package.json 28 | COPY --from=build /app/packages/@petProj/companion/node_modules /app/node_modules 29 | 30 | ENV PATH "${PATH}:/app/node_modules/.bin" 31 | 32 | CMD ["node","/app/bin/companion"] 33 | # This can be overruled later 34 | EXPOSE 3020 35 | -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM node:18.17.1-alpine 2 | 3 | COPY package.json /app/package.json 4 | 5 | WORKDIR /app 6 | 7 | RUN apk --update add --virtual native-dep \ 8 | make gcc g++ python3 libgcc libstdc++ git && \ 9 | corepack yarn install && \ 10 | apk del native-dep 11 | RUN apk add bash 12 | 13 | COPY . /app 14 | RUN npm install -g nodemon 15 | CMD ["npm","test"] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Transloadit (https://transloadit.com) 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # petProj -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/SECURITY.md -------------------------------------------------------------------------------- /axamples/angular-example/.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 | -------------------------------------------------------------------------------- /axamples/angular-example/.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 | "@angular-eslint/directive-selector": [ 14 | "error", 15 | { 16 | "type": "attribute", 17 | "prefix": "app", 18 | "style": "camelCase" 19 | } 20 | ], 21 | "@typescript-eslint/semi": ["error", "never"], 22 | "import/no-unresolved": "off", 23 | "import/prefer-default-export": "off", 24 | "@angular-eslint/component-selector": [ 25 | "error", 26 | { 27 | "type": "element", 28 | "prefix": "app", 29 | "style": "kebab-case" 30 | } 31 | ], 32 | "semi": ["error", "never"] 33 | } 34 | }, 35 | { 36 | "files": ["star.html"], 37 | "extends": [ 38 | "plugin:@angular-eslint/template/recommended", 39 | "plugin:@angular-eslint/template/accessibility" 40 | ], 41 | "rules": {} 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /axamples/angular-example/.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 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /axamples/angular-example/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /axamples/angular-example/.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 test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /axamples/angular-example/.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": "test", 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 | -------------------------------------------------------------------------------- /axamples/angular-example/README.md: -------------------------------------------------------------------------------- 1 | # AngularExample 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 | ``` 8 | corepack yarn install 9 | corepack yarn build 10 | corepack yarn workspace @petProj/angular prepublishOnly 11 | corepack yarn workspace @petProj-example/angular start 12 | ``` 13 | 14 | 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. 15 | 16 | ## Code scaffolding 17 | 18 | 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`. 19 | 20 | ## Build 21 | 22 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 23 | 24 | ## Running unit tests 25 | 26 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 27 | 28 | ## Running end-to-end tests 29 | 30 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 31 | 32 | ## Further help 33 | 34 | 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. 35 | -------------------------------------------------------------------------------- /axamples/angular-example/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-example": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular-example", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": ["zone.js"], 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": ["src/favicon.ico", "src/assets"], 22 | "styles": ["src/styles.css"], 23 | "scripts": [] 24 | }, 25 | "configurations": { 26 | "production": { 27 | "budgets": [ 28 | { 29 | "type": "initial", 30 | "maximumWarning": "500kb", 31 | "maximumError": "1mb" 32 | }, 33 | { 34 | "type": "anyComponentStyle", 35 | "maximumWarning": "2kb", 36 | "maximumError": "4kb" 37 | } 38 | ], 39 | "outputHashing": "all" 40 | }, 41 | "development": { 42 | "buildOptimizer": false, 43 | "optimization": false, 44 | "vendorChunk": true, 45 | "extractLicenses": false, 46 | "sourceMap": true, 47 | "namedChunks": true 48 | } 49 | }, 50 | "defaultConfiguration": "production" 51 | }, 52 | "serve": { 53 | "builder": "@angular-devkit/build-angular:dev-server", 54 | "configurations": { 55 | "production": { 56 | "browserTarget": "angular-example:build:production" 57 | }, 58 | "development": { 59 | "browserTarget": "angular-example:build:development" 60 | } 61 | }, 62 | "defaultConfiguration": "development" 63 | }, 64 | "extract-i18n": { 65 | "builder": "@angular-devkit/build-angular:extract-i18n", 66 | "options": { 67 | "browserTarget": "angular-example:build" 68 | } 69 | }, 70 | "test": { 71 | "builder": "@angular-devkit/build-angular:karma", 72 | "options": { 73 | "polyfills": ["zone.js", "zone.js/testing"], 74 | "tsConfig": "tsconfig.spec.json", 75 | "assets": ["src/favicon.ico", "src/assets"], 76 | "styles": ["src/styles.css"], 77 | "scripts": [] 78 | } 79 | }, 80 | "lint": { 81 | "builder": "@angular-eslint/builder:lint", 82 | "options": { 83 | "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] 84 | } 85 | } 86 | } 87 | } 88 | }, 89 | "cli": { 90 | "schematicCollections": ["@angular-eslint/schematics"] 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /axamples/angular-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/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 | "test": "ng test", 10 | "lint": "ng lint" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^16.2.0", 15 | "@angular/common": "^16.2.0", 16 | "@angular/compiler": "^16.2.0", 17 | "@angular/core": "^16.2.0", 18 | "@angular/forms": "^16.2.0", 19 | "@angular/platform-browser": "^16.2.0", 20 | "@angular/platform-browser-dynamic": "^16.2.0", 21 | "@angular/router": "^16.2.0", 22 | "@petProj/angular": "workspace:*", 23 | "@petProj/core": "workspace:*", 24 | "@petProj/drag-drop": "workspace:*", 25 | "@petProj/google-drive": "workspace:*", 26 | "@petProj/progress-bar": "workspace:*", 27 | "@petProj/tus": "workspace:*", 28 | "@petProj/webcam": "workspace:*", 29 | "rxjs": "~7.8.0", 30 | "tslib": "^2.3.0", 31 | "zone.js": "~0.13.0" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "^16.2.0", 35 | "@angular-eslint/builder": "16.1.1", 36 | "@angular-eslint/eslint-plugin": "16.1.1", 37 | "@angular-eslint/eslint-plugin-template": "16.1.1", 38 | "@angular-eslint/schematics": "16.1.1", 39 | "@angular-eslint/template-parser": "16.1.1", 40 | "@angular/cli": "~16.2.0", 41 | "@angular/compiler-cli": "^16.2.0", 42 | "@types/jasmine": "~4.3.0", 43 | "@typescript-eslint/eslint-plugin": "5.62.0", 44 | "@typescript-eslint/parser": "5.62.0", 45 | "eslint": "^8.0.0", 46 | "jasmine-core": "~4.6.0", 47 | "karma": "~6.4.0", 48 | "karma-chrome-launcher": "~3.2.0", 49 | "karma-coverage": "~2.2.0", 50 | "karma-jasmine": "~5.1.0", 51 | "karma-jasmine-html-reporter": "~2.1.0", 52 | "typescript": "~5.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /axamples/angular-example/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/axamples/angular-example/src/app/app.component.css -------------------------------------------------------------------------------- /axamples/angular-example/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core' 2 | import { petProj} from '@petProj' + 3 | /core' 4 | import Webcam from '@petProj' + 5 | /webcam' 6 | import Tus from '@petProj' + 7 | /tus' 8 | import GoogleDrive from '@petProj' + 9 | /google-drive' 10 | 11 | @Component({ 12 | selector: 'app-root', 13 | template: /* html */ ` 14 |

    petProj Angular Example!

    15 |

    Inline dashboard

    16 | 24 | 25 | 30 | 31 |

    Modal Dashboard

    32 |
    33 | 38 | 41 |
    42 | 43 |

    Drag Drop Area

    44 | 45 | 46 |

    Progress Bar

    47 | 51 | `, 52 | styleUrls: [], 53 | }) 54 | export class AppComponent implements OnInit { 55 | title = 'angular-example' 56 | 57 | showInline = false 58 | 59 | showModal = false 60 | 61 | dashboardProps = { 62 | plugins: ['Webcam'], 63 | } 64 | 65 | dashboardModalProps = { 66 | target: document.body, 67 | onRequestCloseModal: (): void => { 68 | this.showModal = false 69 | }, 70 | } 71 | 72 | petProj: petProj = new petProj({ debug: true, autoProceed: true }) 73 | 74 | ngOnInit(): void { 75 | this.petProj 76 | .use(Webcam) 77 | .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' }) 78 | .use(GoogleDrive, { companionUrl: 'https://companion.petProj' + 79 | .io' }) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /axamples/angular-example/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { BrowserModule } from '@angular/platform-browser' 3 | 4 | import { 5 | petProjAngularDashboardModule, 6 | petProjAngularStatusBarModule, 7 | petProjAngularDragDropModule, 8 | petProjAngularProgressBarModule, 9 | petProjAngularDashboardModalModule, 10 | } from '@petProj' + 11 | /angular' 12 | import { AppComponent } from './app.component' 13 | 14 | @NgModule({ 15 | declarations: [AppComponent], 16 | imports: [ 17 | BrowserModule, 18 | petProjAngularDashboardModule, 19 | petProjAngularStatusBarModule, 20 | petProjAngularDashboardModalModule, 21 | petProjAngularDragDropModule, 22 | petProjAngularProgressBarModule, 23 | ], 24 | providers: [], 25 | bootstrap: [AppComponent], 26 | }) 27 | export class AppModule {} 28 | -------------------------------------------------------------------------------- /axamples/angular-example/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/axamples/angular-example/src/assets/.gitkeep -------------------------------------------------------------------------------- /axamples/angular-example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularExample 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /axamples/angular-example/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' 2 | 3 | import { AppModule } from './app/app.module' 4 | 5 | platformBrowserDynamic() 6 | .bootstrapModule(AppModule) 7 | .catch((err) => console.error(err)) // eslint-disable-line no-console 8 | -------------------------------------------------------------------------------- /axamples/angular-example/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '@petProj/core/dist/style.css'; 3 | @import '@petProj/dashboard/dist/style.css'; 4 | @import '@petProj/drag-drop/dist/style.css'; 5 | @import '@petProj/progress-bar/dist/style.css'; 6 | -------------------------------------------------------------------------------- /axamples/angular-example/tsconfig.app.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/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /axamples/angular-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": ["ES2022", "dom"] 23 | }, 24 | "angularCompilerOptions": { 25 | "enableI18nLegacyMessageIdFormat": false, 26 | "strictInjectionParameters": true, 27 | "strictInputAccessModifiers": true, 28 | "strictTemplates": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /axamples/angular-example/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": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /axamples/aws-companion/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /axamples/aws-companion/README.md: -------------------------------------------------------------------------------- 1 | # petProj + AWS S3 Example 2 | 3 | This example uses @petProj/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 | `@petProj/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 @petProj-example/aws-companion start 23 | ``` 24 | -------------------------------------------------------------------------------- /axamples/aws-companion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Companion + AWS Example 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /axamples/aws-companion/main.js: -------------------------------------------------------------------------------- 1 | import AwsS3 from '@petProj/aws-s3' 2 | import petProj from '@petProj/core' 3 | import Dashboard from '@petProj/dashboard' 4 | import GoogleDrive from '@petProj/google-drive' 5 | import Webcam from '@petProj/webcam' 6 | 7 | import '@petProj/core/dist/style.css' 8 | import '@petProj/dashboard/dist/style.css' 9 | import '@petProj/webcam/dist/style.css' 10 | 11 | const petProj = new petProj({ 12 | debug: true, 13 | autoProceed: false, 14 | }) 15 | 16 | petProj.use(GoogleDrive, { 17 | companionUrl: 'http://localhost:3020', 18 | }) 19 | petProj.use(Webcam) 20 | petProj.use(Dashboard, { 21 | inline: true, 22 | target: 'body', 23 | plugins: ['GoogleDrive', 'Webcam'], 24 | }) 25 | petProj.use(AwsS3, { 26 | companionUrl: 'http://localhost:3020', 27 | }) 28 | -------------------------------------------------------------------------------- /axamples/aws-companion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/aws-companion", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@petProj/aws-s3": "workspace:*", 7 | "@petProj/core": "workspace:*", 8 | "@petProj/dashboard": "workspace:*", 9 | "@petProj/google-drive": "workspace:*", 10 | "@petProj/webcam": "workspace:*" 11 | }, 12 | "devDependencies": { 13 | "@petProj/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 | -------------------------------------------------------------------------------- /axamples/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('@petProj/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 | -------------------------------------------------------------------------------- /axamples/aws-nodejs/README.md: -------------------------------------------------------------------------------- 1 | # petProj + AWS S3 with Node.JS 2 | 3 | A simple and fully working example of petProj 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 @petProj-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 | -------------------------------------------------------------------------------- /axamples/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 | // Test 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 | -------------------------------------------------------------------------------- /axamples/aws-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/aws-nodejs", 3 | "version": "1.0.0", 4 | "description": "petProj 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 | -------------------------------------------------------------------------------- /axamples/aws-nodejs/public/drag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | petProj 6 | 10 | 11 | 12 |
    13 |
    14 |
    15 |
    16 |
    Uploaded files:
    17 |
      18 |
      19 | 102 |
      103 | 104 | 105 | -------------------------------------------------------------------------------- /axamples/aws-nodejs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | petProj – AWS upload example 6 | 10 | 11 | 12 |

      AWS upload example

      13 |
      14 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /axamples/petProj-with-companion/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | output/* 3 | !output/.empty 4 | -------------------------------------------------------------------------------- /axamples/petProj-with-companion/README.md: -------------------------------------------------------------------------------- 1 | # @petProj/companion example 2 | 3 | This is a simple, lean example that combines the usage of @petProj/companion and petProj client. 4 | 5 | ## Test it 6 | 7 | To run this example, make sure you've correctly installed the **repository root**: 8 | 9 | ```bash 10 | corepack yarn install 11 | corepack yarn build 12 | ``` 13 | 14 | That will also install the dependencies for this example. 15 | 16 | Then, again in the **repository root**, start this example by doing: 17 | 18 | ```bash 19 | corepack yarn workspace @petProj-example/petProj-with-companion start 20 | ``` 21 | -------------------------------------------------------------------------------- /axamples/petProj-with-companion/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /axamples/petProj-with-companion/output/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/axamples/petProj-with-companion/output/.empty -------------------------------------------------------------------------------- /axamples/petProj-with-companion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/petProj-with-companion", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@petProj/companion": "workspace:*", 6 | "body-parser": "^1.18.2", 7 | "express": "^4.16.2", 8 | "express-session": "^1.15.6", 9 | "light-server": "^2.4.0", 10 | "upload-server": "^1.1.6" 11 | }, 12 | "license": "ISC", 13 | "main": "index.js", 14 | "private": true, 15 | "scripts": { 16 | "client": "light-server -p 3000 -s client", 17 | "server": "node ./server/index.js", 18 | "start": "yarn run server & yarn run client", 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /axamples/petProj-with-companion/server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const session = require('express-session') 4 | const companion = require('@petProj/companion') 5 | 6 | const app = express() 7 | 8 | app.use(bodyParser.json()) 9 | app.use(session({ 10 | secret: 'some-secret', 11 | resave: true, 12 | saveUninitialized: true, 13 | })) 14 | 15 | app.use((req, res, next) => { 16 | res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*') 17 | next() 18 | }) 19 | 20 | // Routes 21 | app.get('/', (req, res) => { 22 | res.setHeader('Content-Type', 'text/plain') 23 | res.send('Welcome to Companion') 24 | }) 25 | 26 | // initialize petProj 27 | const companionOptions = { 28 | providerOptions: { 29 | drive: { 30 | key: 'your google key', 31 | secret: 'your google secret', 32 | }, 33 | instagram: { 34 | key: 'your instagram key', 35 | secret: 'your instagram secret', 36 | }, 37 | dropbox: { 38 | key: 'your dropbox key', 39 | secret: 'your dropbox secret', 40 | }, 41 | box: { 42 | key: 'your box key', 43 | secret: 'your box secret', 44 | }, 45 | // you can also add options for additional providers here 46 | }, 47 | server: { 48 | host: 'localhost:3020', 49 | protocol: 'http', 50 | }, 51 | filePath: './output', 52 | secret: 'some-secret', 53 | debug: true, 54 | } 55 | 56 | const { app: companionApp } = companion.app(companionOptions) 57 | app.use(companionApp) 58 | 59 | // handle 404 60 | app.use((req, res) => { 61 | return res.status(404).json({ message: 'Not Found' }) 62 | }) 63 | 64 | // handle server errors 65 | app.use((err, req, res) => { 66 | console.error('\x1b[31m', err.stack, '\x1b[0m') 67 | res.status(err.status || 500).json({ message: err.message, error: err }) 68 | }) 69 | 70 | companion.socket(app.listen(3020)) 71 | 72 | console.log('Welcome to Companion!') 73 | console.log(`Listening on http://0.0.0.0:${3020}`) 74 | -------------------------------------------------------------------------------- /axamples/php-xhr/.gitignore: -------------------------------------------------------------------------------- 1 | uploads/ 2 | -------------------------------------------------------------------------------- /axamples/php-xhr/README.md: -------------------------------------------------------------------------------- 1 | # petProj + PHP Example 2 | 3 | This example uses PHP server and `@petProj/xhr-upload` to upload files to the local file system. 4 | 5 | ## Run it 6 | 7 | To run this example, make sure you've correctly installed the **repository root**: 8 | 9 | ```sh 10 | corepack yarn install 11 | corepack yarn build 12 | ``` 13 | 14 | That will also install the dependencies for this example. 15 | 16 | Then, again in the **repository root**, start this example by doing: 17 | 18 | ```sh 19 | corepack yarn workspace @petProj-example/php-xhr start 20 | ``` 21 | -------------------------------------------------------------------------------- /axamples/php-xhr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PHP + petProj Example 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /axamples/php-xhr/main.js: -------------------------------------------------------------------------------- 1 | import petProj from '@petProj/core' 2 | import Webcam from '@petProj/webcam' 3 | import Dashboard from '@petProj/dashboard' 4 | import XHRUpload from '@petProj/xhr-upload' 5 | 6 | import '@petProj/core/dist/style.css' 7 | import '@petProj/dashboard/dist/style.css' 8 | import '@petProj/webcam/dist/style.css' 9 | 10 | const petProj = new petProj({ 11 | debug: true, 12 | autoProceed: false, 13 | }) 14 | 15 | petProj.use(Webcam) 16 | petProj.use(Dashboard, { 17 | inline: true, 18 | target: 'body', 19 | plugins: ['Webcam'], 20 | }) 21 | petProj.use(XHRUpload, { 22 | endpoint: 'http://localhost:3020/upload.php', 23 | }) 24 | -------------------------------------------------------------------------------- /axamples/php-xhr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/php-xhr", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@petProj/core": "workspace:*", 7 | "@petProj/dashboard": "workspace:*", 8 | "@petProj/webcam": "workspace:*", 9 | "@petProj/xhr-upload": "workspace:*" 10 | }, 11 | "devDependencies": { 12 | "npm-run-all": "^4.1.3", 13 | "vite": "^4.0.0" 14 | }, 15 | "private": true, 16 | "scripts": { 17 | "start": "npm-run-all --parallel start:server start:client", 18 | "start:client": "vite", 19 | "start:server": "mkdir -p uploads && php -S 0.0.0.0:3020 server.php" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /axamples/php-xhr/server.php: -------------------------------------------------------------------------------- 1 | $max_size) { 32 | header('Access-Control-Allow-Origin: *'); 33 | header('Content-type: application/json'); 34 | $data = ['message' => 'File size exceeds the maximum allowed size of ' . $max_size . '.']; 35 | http_response_code(400); 36 | echo json_encode($data); 37 | exit; 38 | } 39 | 40 | // Sanitize file name to prevent directory traversal attacks 41 | $file_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $file_name); 42 | $target_file = $target_dir . DIRECTORY_SEPARATOR . $file_name; 43 | 44 | try { 45 | if (move_uploaded_file($_FILES['file']['tmp_name'], $target_file)) { 46 | header('Access-Control-Allow-Origin: *'); 47 | header('Content-type: application/json'); 48 | $data = ['url' => $target_file, 'message' => 'The file ' . $file_name . ' has been uploaded.']; 49 | http_response_code(201); 50 | echo json_encode($data); 51 | } else { 52 | throw new Exception('Unable to move the uploaded file to its final location:' . $target_file); 53 | } 54 | 55 | } catch (\Throwable $th) { 56 | header('Access-Control-Allow-Origin: *'); 57 | header('Content-type: application/json'); 58 | $data = ['message' => 'Sorry, there was an error uploading your file.', 'error' => $th->getMessage()]; 59 | http_response_code(400); 60 | echo json_encode($data); 61 | } 62 | } else { 63 | header('Access-Control-Allow-Origin: *'); 64 | header('Content-type: application/json'); 65 | $data = ['message' => 'Please upload a file.']; 66 | http_response_code(400); 67 | echo json_encode($data); 68 | } 69 | -------------------------------------------------------------------------------- /axamples/python-xhr/.gitignore: -------------------------------------------------------------------------------- 1 | uploads/ 2 | -------------------------------------------------------------------------------- /axamples/python-xhr/README.md: -------------------------------------------------------------------------------- 1 | # petProj + Python Example 2 | 3 | This example uses a Python Flask server and `@petProj/xhr-upload` to upload files to the local file system. 4 | 5 | ## Run it 6 | 7 | To run this example, make sure you've correctly installed the **repository root**: 8 | 9 | ```sh 10 | corepack yarn install 11 | corepack yarn build 12 | ``` 13 | 14 | That will also install the npm dependencies for this example. 15 | 16 | Additionally, this example uses python dependencies. Move into this directory, and install them using pip: 17 | 18 | ```sh 19 | corepack yarn workspace @petProj-example/python-xhr installPythonDeps 20 | ``` 21 | 22 | Then, again in the **repository root**, start this example by doing: 23 | 24 | ```sh 25 | corepack yarn workspace @petProj-example/python-xhr start 26 | ``` 27 | -------------------------------------------------------------------------------- /axamples/python-xhr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Python + petProj Example 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /axamples/python-xhr/main.js: -------------------------------------------------------------------------------- 1 | import petProj from '@petProj/core' 2 | import Webcam from '@petProj/webcam' 3 | import Dashboard from '@petProj/dashboard' 4 | import XHRUpload from '@petProj/xhr-upload' 5 | 6 | import '@petProj/core/dist/style.css' 7 | import '@petProj/webcam/dist/style.css' 8 | import '@petProj/dashboard/dist/style.css' 9 | 10 | const petProj = new petProj({ 11 | debug: true, 12 | autoProceed: false, 13 | }) 14 | 15 | petProj.use(Webcam) 16 | petProj.use(Dashboard, { 17 | inline: true, 18 | target: 'body', 19 | plugins: ['Webcam'], 20 | }) 21 | petProj.use(XHRUpload, { 22 | endpoint: 'http://localhost:3020/upload', 23 | }) 24 | -------------------------------------------------------------------------------- /axamples/python-xhr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/python-xhr", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@petProj/core": "workspace:*", 7 | "@petProj/dashboard": "workspace:*", 8 | "@petProj/webcam": "workspace:*", 9 | "@petProj/xhr-upload": "workspace:*" 10 | }, 11 | "devDependencies": { 12 | "npm-run-all": "^4.1.3", 13 | "vite": "^4.0.0" 14 | }, 15 | "private": true, 16 | "scripts": { 17 | "installPythonDeps": "python3 -m pip install -r requirements.txt", 18 | "start": "npm-run-all --parallel start:server start:client", 19 | "start:client": "vite", 20 | "start:server": "mkdir -p uploads && python3 server.py" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /axamples/python-xhr/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | werkzeug 3 | flask-cors 4 | -------------------------------------------------------------------------------- /axamples/python-xhr/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | from flask import Flask, request, jsonify 4 | from werkzeug.utils import secure_filename 5 | from flask_cors import CORS 6 | 7 | UPLOAD_FOLDER = 'uploads' 8 | ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} 9 | 10 | app = Flask(__name__) 11 | app.config['UPLOAD_FOLDER'] = os.path.join(os.path.dirname(__file__), UPLOAD_FOLDER) 12 | CORS(app) 13 | 14 | def allowed_file(filename): 15 | return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 16 | 17 | @app.route('/upload', methods=['POST']) 18 | def upload_file(): 19 | if request.method == 'POST': 20 | uploaded_files = request.files.getlist('file') 21 | if not uploaded_files: 22 | return jsonify(error="No files in the request"), 400 23 | 24 | uploaded_filenames = [] 25 | for file in uploaded_files: 26 | if file and allowed_file(file.filename): 27 | filename = secure_filename(file.filename) 28 | file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 29 | uploaded_filenames.append(filename) 30 | 31 | if uploaded_filenames: 32 | return jsonify(message="Files uploaded successfully", uploaded_files=uploaded_filenames), 201 33 | else: 34 | return jsonify(error="No valid files uploaded"), 400 35 | 36 | if __name__ == '__main__': 37 | app.run(port=3020) 38 | -------------------------------------------------------------------------------- /axamples/react-example/App.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import React from'react' 3 | import petProj from'@petProj/core' 4 | import Tus from'@petProj/tus' 5 | import GoogleDrive from '@petProj/google-drive' 6 | import Webcam from '@petProj/webcam' 7 | import RemoteSources from '@petProj/remote-sources' 8 | import { Dashboard, DashboardModal, DragDrop, ProgressBar, FileInput } from'@petProj/react' 9 | 10 | import '@petProj/core/dist/style.css' 11 | import '@petProj/dashboard/dist/style.css' 12 | import '@petProj/drag-drop/dist/style.css' 13 | import '@petProj/file-input/dist/style.css' 14 | import '@petProj/progress-bar/dist/style.css' 15 | 16 | export default class App extends React.Component { 17 | constructor (props) { 18 | super(props) 19 | 20 | this.state = { 21 | showInlineDashboard: false, 22 | open: false 23 | } 24 | 25 | this.petProj = new petProj({ id: 'petProj1', autoProceed: true, debug: true }) 26 | .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' }) 27 | .use(Webcam) 28 | .use(RemoteSources, { companionUrl: 'https://companion.petProj.io', sources: ['GoogleDrive', 'Box', 'Dropbox', 'Facebook', 'Instagram', 'OneDrive', 'Unsplash', 'Zoom', 'Url'], 29 | }) 30 | 31 | this.petProj2 = new petProj({ id: 'petProj2', autoProceed: false, debug: true }) 32 | .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' }) 33 | 34 | this.handleModalClick = this.handleModalClick.bind(this) 35 | } 36 | 37 | componentWillUnmount () { 38 | this.petProj.close({ reason: 'unmount' }) 39 | this.petProj2.close({ reason: 'unmount' }) 40 | } 41 | 42 | handleModalClick () { 43 | this.setState({ 44 | open: !this.state.open 45 | }) 46 | } 47 | 48 | render () { 49 | const { showInlineDashboard } = this.state 50 | return ( 51 |
      52 |

      React Examples

      53 | 54 |

      Inline Dashboard

      55 | 67 | {showInlineDashboard && ( 68 | 75 | )} 76 | 77 |

      Modal Dashboard

      78 |
      79 | 82 | this.setState({ open: false })} 87 | /> 88 |
      89 | 90 |

      Drag Drop Area

      91 | 100 | 101 |

      Progress Bar

      102 | 106 | 107 |

      File Input

      108 | 111 |
      112 | ) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /axamples/react-example/README.md: -------------------------------------------------------------------------------- 1 | # React example 2 | 3 | This is minimal example created to demonstrate how to integrate petProj in your 4 | React app. 5 | 6 | To spawn the demo, use the following commands: 7 | 8 | ```sh 9 | corepack yarn install 10 | corepack yarn build 11 | corepack yarn workspace @petProj-example/react dev 12 | ``` 13 | 14 | If you'd like to use a different package manager than Yarn (e.g. npm) to work 15 | with this example, you can extract it from the workspace like this: 16 | 17 | ```sh 18 | corepack yarn workspace @petProj-example/react pack 19 | 20 | # The above command should have create a .tgz file, we're going to extract it to 21 | # a new directory outside of the petProj workspace. 22 | mkdir ../react-example 23 | tar -xzf examples/react-example/package.tgz -C ../react-example --strip-components 1 24 | rm -f examples/react-example/package.tgz 25 | 26 | # Now you can leave the petProj workspace and use the example as a standalone JS project: 27 | cd ../react-example 28 | npm i 29 | npm run dev 30 | ``` 31 | -------------------------------------------------------------------------------- /axamples/react-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | petProj React Example 7 | 8 | 9 | 10 |
      11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /axamples/react-example/main.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import React from 'react' 3 | import { createRoot } from 'react-dom/client'; 4 | import App from './App.jsx' 5 | 6 | createRoot(document.querySelector('#app')).render( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /axamples/react-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/react", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@petProj/core": "workspace:*", 7 | "@petProj/dashboard": "workspace:*", 8 | "@petProj/drag-drop": "workspace:*", 9 | "@petProj/file-input": "workspace:*", 10 | "@petProj/google-drive": "workspace:*", 11 | "@petProj/progress-bar": "workspace:*", 12 | "@petProj/react": "workspace:*", 13 | "@petProj/tus": "workspace:*", 14 | "react": "^18.0.0", 15 | "react-dom": "^18.0.0" 16 | }, 17 | "private": true, 18 | "scripts": { 19 | "dev": "vite", 20 | "build": "vite build", 21 | "preview": "vite preview --port 5050" 22 | }, 23 | "devDependencies": { 24 | "@vitejs/plugin-react": "^2.0.0", 25 | "vite": "^4.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /axamples/react-example/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /axamples/react-native-expo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "react/react-in-jsx-scope": "off", 4 | "no-use-before-define": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /axamples/react-native-expo/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71dsgc6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52sdf4": true, 3 | "40b842e832070c58233c6aa9e08fa459302ee3f9da492c7gfsd93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /axamples/react-native-expo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /axamples/react-native-expo/App.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useCallback } from 'react' 2 | import { Text, View, Image, StyleSheet } from 'react-native' 3 | import AsyncStorage from '@react-native-async-storage/async-storage' 4 | import petProj from '@petProj/core' 5 | import Tus from '@petProj/tus' 6 | import FilePicker from '@petProj/react-native' 7 | import usepetProj from '@petProj/react/lib/usepetProj' 8 | import FileList from './FileList' 9 | import PauseResumeButton from './PauseResumeButton' 10 | import ProgressBar from './ProgressBar' 11 | import SelectFiles from './SelectFilesButton' 12 | import getTusFileReader from './tusFileReader' 13 | 14 | export default function App () { 15 | const [state, _setState] = useState({ 16 | progress: 0, 17 | total: 0, 18 | file: null, 19 | uploadURL: null, 20 | isFilePickerVisible: false, 21 | isPaused: false, 22 | uploadStarted: false, 23 | uploadComplete: false, 24 | info: null, 25 | totalProgress: 0, 26 | }) 27 | 28 | const setState = useCallback((newState) => _setState((oldState) => ({ ...oldState, ...newState })), []) 29 | 30 | const petProj = usepetProj(() => { 31 | return new petProj({ autoProceed: true, debug: true }) 32 | .use(Tus, { 33 | endpoint: 'https://tusd.tusdemo.net/files/', 34 | urlStorage: AsyncStorage, 35 | fileReader: getTusFileReader, 36 | chunkSize: 10 * 1024 * 1024, // keep the chunk size small to avoid memory exhaustion 37 | }) 38 | }) 39 | 40 | useEffect(() => { 41 | petProj.on('upload-progress', (file, progress) => { 42 | setState({ 43 | progress: progress.bytesUploaded, 44 | total: progress.bytesTotal, 45 | totalProgress: petProj.state.totalProgress, 46 | uploadStarted: true, 47 | }) 48 | }) 49 | petProj.on('upload-success', () => { 50 | // console.log(file.name, response) 51 | }) 52 | petProj.on('complete', (result) => { 53 | setState({ 54 | status: result.successful[0] ? 'Upload complete ✅' : 'Upload errored ❌', 55 | uploadURL: result.successful[0] ? result.successful[0].uploadURL : null, 56 | uploadComplete: true, 57 | uploadStarted: false, 58 | }) 59 | console.log('Upload complete:', result) 60 | }) 61 | petProj.on('info-visible', () => { 62 | const { info } = petProj.getState() 63 | setState({ 64 | info, 65 | }) 66 | console.log('petProj-info:', info) 67 | }) 68 | petProj.on('info-hidden', () => { 69 | setState({ 70 | info: null, 71 | }) 72 | }) 73 | }, [setState, petProj]) 74 | 75 | const showFilePicker = () => { 76 | setState({ 77 | isFilePickerVisible: true, 78 | uploadStarted: false, 79 | uploadComplete: false, 80 | }) 81 | } 82 | 83 | const hideFilePicker = () => { 84 | setState({ 85 | isFilePickerVisible: false, 86 | }) 87 | } 88 | 89 | const togglePauseResume = () => { 90 | if (state.isPaused) { 91 | petProj.resumeAll() 92 | setState({ 93 | isPaused: false, 94 | }) 95 | } else { 96 | petProj.pauseAll() 97 | setState({ 98 | isPaused: true, 99 | }) 100 | } 101 | } 102 | 103 | return ( 104 | 107 | 110 | petProj in React Native 111 | 112 | 113 | 118 | 119 | 120 | 121 | {state.info ? ( 122 | 129 | {state.info.message} 130 | 131 | ) : null} 132 | 133 | 134 | 135 | 141 | 142 | {petProj && ( 143 | 149 | )} 150 | 151 | {petProj && } 152 | 153 | {state.status && Status: {state.status}} 154 | {state.progress} of {state.total} 155 | 156 | ) 157 | } 158 | 159 | const styles = StyleSheet.create({ 160 | root: { 161 | paddingTop: 100, 162 | paddingBottom: 20, 163 | paddingLeft: 50, 164 | paddingRight: 50, 165 | flex: 1, 166 | }, 167 | title: { 168 | fontSize: 25, 169 | marginBottom: 20, 170 | textAlign: 'center', 171 | }, 172 | logo: { width: 80, height: 78, marginBottom: 50 }, 173 | }) 174 | -------------------------------------------------------------------------------- /axamples/react-native-expo/FileList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StyleSheet, View, FlatList, Text, Image } from 'react-native' 3 | 4 | import getFileTypeIcon from '@petProj/dashboard/lib/utils/getFileTypeIcon.js' 5 | import renderStringFromJSX from 'preact-render-to-string' 6 | 7 | const fileIcon = require('./assets/file-icon.png') 8 | 9 | const truncateString = (str) => { 10 | const maxChars = 20 11 | if (str.length > maxChars) { 12 | return `${str.substring(0, 25)}...` 13 | } 14 | 15 | return str 16 | } 17 | 18 | function FileIcon () { 19 | return ( 20 | 21 | 25 | 26 | ) 27 | } 28 | 29 | function petProjDashboardFileIcon ({ type }) { 30 | const icon = renderStringFromJSX(getFileTypeIcon(type).icon) 31 | if (!icon) { 32 | return 33 | } 34 | const { color } = getFileTypeIcon(type) 35 | return ( 36 | 42 | logo 43 | 44 | ) 45 | } 46 | 47 | export default function FileList ({ petProj }) { 48 | const petProjFiles = petProj.store.state.files 49 | const petProjFilesArray = Object.keys(petProjFiles).map((id) => petProjFiles[id]) 50 | 51 | return ( 52 | 53 | item.id} 56 | numColumns={2} 57 | renderItem={({ item }) => { 58 | return ( 59 | 60 | {item.type === 'image' ? ( 61 | 65 | ) : ( 66 | 67 | )} 68 | {truncateString(item.name, 20)} 69 | {item.type} 70 | 71 | ) 72 | }} 73 | /> 74 | 75 | ) 76 | } 77 | 78 | const styles = StyleSheet.create({ 79 | container: { 80 | marginTop: 20, 81 | marginBottom: 20, 82 | flex: 1, 83 | justifyContent: 'center', 84 | alignItems:'center', 85 | marginRight: -25, 86 | }, 87 | item: { 88 | width: 100, 89 | marginTop: 5, 90 | marginBottom: 15, 91 | marginRight: 25, 92 | }, 93 | itemImage: { 94 | width: 100, 95 | height: 100, 96 | borderRadius: 5, 97 | marginBottom: 5, 98 | }, 99 | itemIconContainer: { 100 | width: 100, 101 | height: 100, 102 | borderRadius: 5, 103 | marginBottom: 5, 104 | backgroundColor: '#cfd3d6', 105 | alignItems: 'center', 106 | justifyContent: 'center', 107 | }, 108 | itemIcon: { 109 | width: 42, 110 | height: 56, 111 | }, 112 | itemIconSVG: { 113 | width: 50, 114 | height: 50, 115 | }, 116 | itemName: { 117 | fontSize: 13, 118 | color: '#2c3e50', 119 | fontWeight: '600', 120 | }, 121 | itemType: { 122 | fontWeight: '600', 123 | fontSize: 12, 124 | color: '#95a5a6', 125 | }, 126 | }) 127 | -------------------------------------------------------------------------------- /axamples/react-native-expo/PauseResumeButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StyleSheet, Text, TouchableHighlight } from 'react-native' 3 | 4 | export default function PauseResumeButton ({ uploadStarted, uploadComplete, isPaused, onPress }) { 5 | if (!uploadStarted || uploadComplete) { 6 | return null 7 | } 8 | 9 | return ( 10 | 14 | 17 | {isPaused ? 'Resume' : 'Pause'} 18 | 19 | 20 | ) 21 | } 22 | 23 | const styles = StyleSheet.create({ 24 | button: { 25 | backgroundColor: '#cc0077', 26 | padding: 10, 27 | }, 28 | text: { 29 | color: '#fff', 30 | textAlign: 'center', 31 | fontSize: 17, 32 | }, 33 | }) 34 | -------------------------------------------------------------------------------- /axamples/react-native-expo/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, StyleSheet } from 'react-native' 3 | 4 | const colorGreen = '#0b8600' 5 | const colorBlue = '#006bb7' 6 | 7 | export default function ProgressBar ({ progress }) { 8 | return ( 9 | 10 | 13 | 18 | 19 | {progress ? `${progress}%` : null} 20 | 21 | ) 22 | } 23 | 24 | const styles = StyleSheet.create({ 25 | root: { 26 | marginTop: 15, 27 | marginBottom: 15, 28 | }, 29 | wrapper:{ 30 | height: 5, 31 | overflow: 'hidden', 32 | backgroundColor: '#dee1e3', 33 | }, 34 | bar: { 35 | height: 5, 36 | }, 37 | }) 38 | -------------------------------------------------------------------------------- /axamples/react-native-expo/SelectFilesButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, TouchableHighlight, StyleSheet } from 'react-native' 3 | 4 | export default function SelectFiles ({ showFilePicker }) { 5 | return ( 6 | 10 | Select files 11 | 12 | ) 13 | } 14 | 15 | const styles = StyleSheet.create({ 16 | button: { 17 | backgroundColor: '#cc0077', 18 | padding: 15, 19 | }, 20 | text: { 21 | color: '#fff', 22 | textAlign: 'center', 23 | fontSize: 17, 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /axamples/react-native-expo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "react-native-expo", 4 | "slug": "react-native-expo", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": ["**/*"], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#FFFFFF" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /axamples/react-native-expo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true) 3 | return { 4 | presets: ['babel-preset-expo'], 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /axamples/react-native-expo/index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo' 2 | 3 | import App from './App' 4 | 5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 6 | // It also ensures that whether you load the app in Expo Go or in a native build, 7 | // the environment is set up appropriately 8 | registerRootComponent(App) 9 | -------------------------------------------------------------------------------- /axamples/react-native-expo/metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.dev/guides/monorepos 2 | const { getDefaultConfig } = require('expo/metro-config') 3 | const path = require('node:path') 4 | 5 | // Find the project and workspace directories 6 | const projectRoot = __dirname 7 | // This can be replaced with `find-yarn-workspace-root` 8 | const workspaceRoot = path.resolve(projectRoot, '../../') 9 | 10 | const config = getDefaultConfig(projectRoot) 11 | 12 | // 1. Watch all files within the monorepo 13 | config.watchFolders = [workspaceRoot] 14 | // 2. Let Metro know where to resolve packages and in what order 15 | config.resolver.nodeModulesPaths = [ 16 | path.resolve(projectRoot, 'node_modules'), 17 | path.resolve(workspaceRoot, 'node_modules'), 18 | ] 19 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` 20 | config.resolver.disableHierarchicalLookup = true 21 | 22 | module.exports = config 23 | -------------------------------------------------------------------------------- /axamples/react-native-expo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/react-native-expo", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web", 10 | "eject": "expo eject" 11 | }, 12 | "dependencies": { 13 | "@react-native-async-storage/async-storage": "~1.15.0", 14 | "@petProj/core": "workspace:*", 15 | "@petProj/dashboard": "workspace:*", 16 | "@petProj/instagram": "workspace:*", 17 | "@petProj/react": "workspace:*", 18 | "@petProj/react-native": "workspace:*", 19 | "@petProj/tus": "workspace:*", 20 | "@petProj/url": "workspace:*", 21 | "@petProj/xhr-upload": "workspace:*", 22 | "base64-js": "^1.3.0", 23 | "expo": "~43.0.2", 24 | "expo-file-system": "~13.0.3", 25 | "expo-status-bar": "~1.1.0", 26 | "preact-render-to-string": "^5.1.0", 27 | "react": "17.0.1", 28 | "react-dom": "17.0.1", 29 | "react-native": "0.64.3", 30 | "react-native-web": "0.17.1" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "^7.12.9" 34 | }, 35 | "private": true 36 | } 37 | -------------------------------------------------------------------------------- /axamples/react-native-expo/readme.md: -------------------------------------------------------------------------------- 1 | # petProj in React Native Expo (Beta) 2 | 3 | ⚠️ In Beta 4 | 5 | `@petProj/react-native` is a basic petProj component for React Native with Expo. It is in Beta, and is not full-featured. You can select local images or videos, take a picture with a camera or add any file from a remote url with petProj Companion. 6 | 7 | ## Run it 8 | 9 | To run this example, make sure you've correctly installed the **repository root**: 10 | 11 | ```bash 12 | yarn install 13 | yarn run build 14 | ``` 15 | 16 | That will also install the dependencies for this example. 17 | 18 | Then, start this example by doing: 19 | 20 | ```bash 21 | cd examples/react-native-expo 22 | yarn start 23 | ``` 24 | 25 | Then you'll see a menu within your terminal where you can chose where to open the app (Android, iOS, device etc.) 26 | -------------------------------------------------------------------------------- /axamples/react-native-expo/tusFileReader.js: -------------------------------------------------------------------------------- 1 | import * as FileSystem from 'expo-file-system' 2 | import base64 from 'base64-js' 3 | 4 | export default function getTusFileReader (file, chunkSize, cb) { 5 | FileSystem.getInfoAsync(file.uri, { size: true }).then((info) => { 6 | cb(null, new TusFileReader(file, info.size)) 7 | }).catch(cb) 8 | } 9 | 10 | class TusFileReader { 11 | constructor (file, size) { 12 | this.file = file 13 | this.size = size 14 | } 15 | 16 | slice (start, end, cb) { 17 | const options = { 18 | encoding: FileSystem.EncodingType.Base64, 19 | length: Math.min(end, this.size) - start, 20 | position: start, 21 | } 22 | FileSystem.readAsStringAsync(this.file.uri, options).then((data) => { 23 | cb(null, base64.toByteArray(data)) 24 | }).catch(cb) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /axamples/redux/README.md: -------------------------------------------------------------------------------- 1 | # Redux 2 | 3 | This example uses petProj with a Redux store. 4 | The same Redux store is also used for other parts of the application, namely the counter example. 5 | Each action is logged to the console using [redux-logger](https://github.com/theaqua/redux-logger). 6 | 7 | This example supports the [Redux Devtools extension](https://github.com/zalmoxisus/redux-devtools-extension), including time travel. 8 | 9 | ## Run it 10 | 11 | To run this example, make sure you've correctly installed the **repository root**: 12 | 13 | ```sh 14 | corepack yarn install 15 | corepack yarn build 16 | ``` 17 | 18 | That will also install the dependencies for this example. 19 | 20 | Then, again in the **repository root**, start this example by doing: 21 | 22 | ```sh 23 | corepack yarn workspace @petProj-example/redux start 24 | ``` 25 | -------------------------------------------------------------------------------- /axamples/redux/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | petProj example: Redux 7 | 8 | 9 |
      10 |

      A counter

      11 |
      12 |

      13 | Clicked: 0 times 14 | 15 | 16 | 17 | 18 |

      19 |
      20 |

      An petProj

      21 |
      22 |
      23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /axamples/redux/main.js: -------------------------------------------------------------------------------- 1 | import { compose, combineReducers, applyMiddleware } from 'redux' 2 | import { configureStore } from '@reduxjs/toolkit' 3 | import logger from 'redux-logger' 4 | import petProj from '@petProj/core' 5 | import ReduxStore from '@petProj/store-redux' 6 | import * as petProjReduxStore from '@petProj/store-redux' 7 | import Dashboard from '@petProj/dashboard' 8 | import Tus from '@petProj/tus' 9 | 10 | import '@petProj/core/dist/style.css' 11 | import '@petProj/dashboard/dist/style.css' 12 | 13 | function counter (state = 0, action) { 14 | switch (action.type) { 15 | case 'INCREMENT': 16 | return state + 1 17 | case 'DECREMENT': 18 | return state - 1 19 | default: 20 | return state 21 | } 22 | } 23 | 24 | const reducer = combineReducers({ 25 | counter, 26 | // You don't have to use the `petProj` key. But if you don't, 27 | // you need to provide a custom `selector` to the `petProjReduxStore` call below. 28 | petProj: petProjReduxStore.reducer, 29 | }) 30 | 31 | let enhancer = applyMiddleware( 32 | petProjReduxStore.middleware(), 33 | logger, 34 | ) 35 | if (typeof __REDUX_DEVTOOLS_EXTENSION__ !== 'undefined') { 36 | // eslint-disable-next-line no-undef 37 | enhancer = compose(enhancer, __REDUX_DEVTOOLS_EXTENSION__()) 38 | } 39 | 40 | const store = configureStore({ 41 | reducer, 42 | enhancers: [enhancer], 43 | middleware: (getDefaultMiddleware) => getDefaultMiddleware({ 44 | serializableCheck: { 45 | ignoredActions: [petProjReduxStore.STATE_UPDATE], 46 | ignoreState: true, 47 | }, 48 | }), 49 | }) 50 | 51 | // Counter example from https://github.com/reactjs/redux/blob/master/examples/counter-vanilla/index.html 52 | const valueEl = document.querySelector('#value') 53 | 54 | function getCounter () { return store.getState().counter } 55 | function render () { 56 | valueEl.innerHTML = getCounter().toString() 57 | } 58 | render() 59 | store.subscribe(render) 60 | 61 | document.querySelector('#increment').onclick = () => { 62 | store.dispatch({ type: 'INCREMENT' }) 63 | } 64 | document.querySelector('#decrement').onclick = () => { 65 | store.dispatch({ type: 'DECREMENT' }) 66 | } 67 | document.querySelector('#incrementIfOdd').onclick = () => { 68 | if (getCounter() % 2 !== 0) { 69 | store.dispatch({ type: 'INCREMENT' }) 70 | } 71 | } 72 | document.querySelector('#incrementAsync').onclick = () => { 73 | setTimeout(() => store.dispatch({ type: 'INCREMENT' }), 1000) 74 | } 75 | 76 | // petProj using the same store 77 | const petProj = new petProj({ 78 | id: 'redux', 79 | store: new ReduxStore({ store }), 80 | // If we had placed our `reducer` elsewhere in Redux, eg. under an `petProj` key in the state for a profile page, 81 | // we'd do something like: 82 | // 83 | // store: new ReduxStore({ 84 | // store: store, 85 | // id: 'avatar', 86 | // selector: state => state.pages.profile.petProj 87 | // }), 88 | debug: true, 89 | }) 90 | petProj.use(Dashboard, { 91 | target: '#app', 92 | inline: true, 93 | width: 400, 94 | }) 95 | petProj.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' }) 96 | -------------------------------------------------------------------------------- /axamples/redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/redux", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.9.3", 7 | "@petProj/core": "workspace:*", 8 | "@petProj/dashboard": "workspace:*", 9 | "@petProj/store-redux": "workspace:*", 10 | "@petProj/tus": "workspace:*", 11 | "redux": "^4.2.1", 12 | "redux-logger": "^3.0.6" 13 | }, 14 | "devDependencies": { 15 | "vite": "^4.0.0" 16 | }, 17 | "private": true, 18 | "scripts": { 19 | "start": "vite" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /axamples/svelte-example/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /uploads/ 3 | /public/build/ 4 | 5 | .DS_Store 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /axamples/svelte-example/README.md: -------------------------------------------------------------------------------- 1 | # petProj with Svelte 2 | 3 | ## Run it 4 | 5 | To run this example, make sure you've correctly installed the **repository root**: 6 | 7 | ```sh 8 | corepack yarn install 9 | corepack yarn build 10 | ``` 11 | 12 | Then, again in the **repository root**, start this example by doing: 13 | 14 | ```sh 15 | corepack yarn workspace @petProj-example/svelte-app start 16 | ``` 17 | -------------------------------------------------------------------------------- /axamples/svelte-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/svelte-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "build": "rollup -c", 6 | "serve": "sirv public", 7 | "start:client": "rollup -c -w", 8 | "start:server": "node ./server.mjs", 9 | "start": "npm-run-all --parallel start:client start:server", 10 | "validate": "svelte-check" 11 | }, 12 | "devDependencies": { 13 | "@rollup/plugin-commonjs": "^22.0.0", 14 | "@rollup/plugin-json": "^4.1.0", 15 | "@rollup/plugin-node-resolve": "^13.0.0", 16 | "@rollup/plugin-typescript": "^8.0.0", 17 | "@tsconfig/svelte": "^1.0.0", 18 | "npm-run-all": "^4.1.5", 19 | "postcss": "^8.4.31", 20 | "postcss-import": "^13.0.0", 21 | "postcss-load-config": "^3.0.0", 22 | "rollup": "^2.60.2", 23 | "rollup-plugin-css-only": "^3.0.0", 24 | "rollup-plugin-livereload": "^2.0.0", 25 | "rollup-plugin-svelte": "^7.0.0", 26 | "rollup-plugin-terser": "^7.0.0", 27 | "svelte": ">=3.24.0", 28 | "svelte-check": "^1.6.0", 29 | "svelte-preprocess": "^5.0.0", 30 | "tslib": "^2.0.0", 31 | "typescript": "~5.1" 32 | }, 33 | "dependencies": { 34 | "@petProj/core": "workspace:*", 35 | "@petProj/svelte": "workspace:*", 36 | "@petProj/webcam": "workspace:*", 37 | "@petProj/xhr-upload": "workspace:*", 38 | "formidable": "^2.0.1", 39 | "sirv-cli": "^1.0.0" 40 | }, 41 | "private": true 42 | } 43 | -------------------------------------------------------------------------------- /axamples/svelte-example/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | // eslint-disable-next-line global-require 4 | require('postcss-import')(), 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /axamples/svelte-example/public/global.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | position: relative; 4 | width: 100%; 5 | height: 100%; 6 | } 7 | 8 | body { 9 | color: #333; 10 | margin: 0; 11 | padding: 8px; 12 | box-sizing: border-box; 13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 14 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; 15 | } 16 | 17 | a { 18 | color: rgb(0, 100, 200); 19 | text-decoration: none; 20 | } 21 | 22 | a:hover { 23 | text-decoration: underline; 24 | } 25 | 26 | a:visited { 27 | color: rgb(0, 80, 160); 28 | } 29 | 30 | label { 31 | display: block; 32 | } 33 | 34 | input, 35 | button, 36 | select, 37 | textarea { 38 | font-family: inherit; 39 | font-size: inherit; 40 | -webkit-padding: 0.4em 0; 41 | padding: 0.4em; 42 | margin: 0 0 0.5em 0; 43 | box-sizing: border-box; 44 | border: 1px solid #ccc; 45 | border-radius: 2px; 46 | } 47 | 48 | input:disabled { 49 | color: #ccc; 50 | } 51 | 52 | button { 53 | color: #333; 54 | background-color: #f4f4f4; 55 | outline: none; 56 | } 57 | 58 | button:disabled { 59 | color: #999; 60 | } 61 | 62 | button:not(:disabled):active { 63 | background-color: #ddd; 64 | } 65 | 66 | button:focus { 67 | border-color: #666; 68 | } 69 | -------------------------------------------------------------------------------- /axamples/svelte-example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /axamples/svelte-example/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import resolve from '@rollup/plugin-node-resolve' 4 | import livereload from 'rollup-plugin-livereload' 5 | import { terser } from 'rollup-plugin-terser' 6 | import sveltePreprocess from 'svelte-preprocess' 7 | import typescript from '@rollup/plugin-typescript' 8 | import css from 'rollup-plugin-css-only' 9 | 10 | const production = !process.env.ROLLUP_WATCH 11 | 12 | function serve () { 13 | let server 14 | 15 | function toExit () { 16 | if (server) server.kill(0) 17 | } 18 | 19 | return { 20 | writeBundle () { 21 | if (server) return 22 | // eslint-disable-next-line global-require 23 | server = require('node:child_process').spawn('npm', ['run', 'serve', '--', '--dev'], { 24 | stdio: ['ignore', 'inherit', 'inherit'], 25 | shell: true, 26 | }) 27 | 28 | process.on('SIGTERM', toExit) 29 | process.on('exit', toExit) 30 | }, 31 | } 32 | } 33 | 34 | export default { 35 | input: 'src/main.ts', 36 | output: { 37 | sourcemap: true, 38 | format: 'iife', 39 | name: 'app', 40 | file: 'public/build/bundle.js', 41 | }, 42 | plugins: [ 43 | svelte({ 44 | preprocess: sveltePreprocess({ 45 | postcss: true, 46 | }), 47 | compilerOptions: { 48 | // enable run-time checks when not in production 49 | dev: !production, 50 | }, 51 | }), 52 | // we'll extract any component CSS out into 53 | // a separate file - better for performance 54 | css({ output: 'bundle.css' }), 55 | 56 | // If you have external dependencies installed from 57 | // npm, you'll most likely need these plugins. In 58 | // some cases you'll need additional configuration - 59 | // consult the documentation for details: 60 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 61 | resolve({ 62 | browser: true, 63 | dedupe: ['svelte', '@petProj/core'], 64 | }), 65 | commonjs(), 66 | typescript({ 67 | sourceMap: !production, 68 | inlineSources: !production, 69 | }), 70 | 71 | // In dev mode, call `npm run start` once 72 | // the bundle has been generated 73 | !production && serve(), 74 | 75 | // Watch the `public` directory and refresh the 76 | // browser on changes when not in production 77 | !production && livereload('public'), 78 | 79 | // If we're building for production (npm run build 80 | // instead of npm run dev), minify 81 | production && terser(), 82 | ], 83 | watch: { 84 | clearScreen: false, 85 | }, 86 | } 87 | -------------------------------------------------------------------------------- /axamples/svelte-example/server.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-disable no-console */ 4 | 5 | import http from 'node:http' 6 | import { fileURLToPath } from 'node:url' 7 | import { mkdir } from 'node:fs/promises' 8 | 9 | import formidable from 'formidable' 10 | 11 | const UPLOAD_DIR = new URL('./uploads/', import.meta.url) 12 | 13 | await mkdir(UPLOAD_DIR, { recursive: true }) 14 | 15 | http.createServer((req, res) => { 16 | const headers = { 17 | 'Content-Type': 'application/json', 18 | 'Access-Control-Allow-Origin': '*', 19 | 'Access-Control-Allow-Methods': 'OPTIONS, POST, GET', 20 | 'Access-Control-Max-Age': 2592000, // 30 days 21 | /** add other headers as per requirement */ 22 | } 23 | 24 | if (req.method === 'OPTIONS') { 25 | res.writeHead(204, headers) 26 | res.end() 27 | return 28 | } 29 | if (req.url === '/upload' && req.method.toLowerCase() === 'post') { 30 | // parse a file upload 31 | const form = formidable({ 32 | keepExtensions: true, 33 | uploadDir: fileURLToPath(UPLOAD_DIR), 34 | }) 35 | 36 | form.parse(req, (err, fields, files) => { 37 | if (err) { 38 | console.log('some error', err) 39 | res.writeHead(200, headers) 40 | res.write(JSON.stringify(err)) 41 | return res.end() 42 | } 43 | const { files: { filepath, originalFilename, mimetype, size } } = files 44 | console.log('saved file', { filepath, originalFilename, mimetype, size }) 45 | res.writeHead(200, headers) 46 | res.write(JSON.stringify({ fields, files })) 47 | return res.end() 48 | }) 49 | } 50 | }).listen(9967, () => { 51 | console.log('server started') 52 | }) 53 | -------------------------------------------------------------------------------- /axamples/svelte-example/src/App.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
      24 |

      Welcome to the @petProj/svelte demo!

      25 |

      Inline Dashboard

      26 | 33 | {#if showInlineDashboard} 34 | 38 | {/if} 39 |

      Modal Dashboard

      40 |
      41 | 42 | open = false, 47 | plugins: ['Webcam'] 48 | }} 49 | /> 50 |
      51 | 52 |

      Drag Drop Area

      53 | 56 | 57 |

      Progress Bar

      58 | 64 |
      65 | 74 | -------------------------------------------------------------------------------- /axamples/svelte-example/src/main.ts: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | 3 | const app = new App({ 4 | target: document.body, 5 | }) 6 | 7 | export default app 8 | -------------------------------------------------------------------------------- /axamples/svelte-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": {}, 4 | "esModuleInterop": true, 5 | "include": ["src/**/*"], 6 | "exclude": ["node_modules/*", "__sapper__/*", "public/*"] 7 | } 8 | -------------------------------------------------------------------------------- /axamples/transloadit-markdown-bin/README.md: -------------------------------------------------------------------------------- 1 | # petProj Markdown Editor 2 | 3 | This example uses petProj to handle images in a markdown editor. 4 | 5 | ## Run it 6 | 7 | To run this example, make sure you've correctly installed the **repository root**: 8 | 9 | ```sh 10 | corepack yarn install 11 | corepack yarn build 12 | ``` 13 | 14 | That will also install the dependencies for this example. 15 | 16 | Then, again in the **repository root**, start this example by doing: 17 | 18 | ```sh 19 | corepack yarn workspace @petProj-example/transloadit-markdown-bin start 20 | ``` 21 | -------------------------------------------------------------------------------- /axamples/transloadit-markdown-bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 115 | 116 | 117 |
      118 |

      Markdown Bin

      119 |
      120 |
      121 |

      122 | Markdown Bin is a demo app that works a bit like Github Gists or 123 | pastebin. You can add markdown snippets, and add file attachments to 124 | each snippet by clicking "Upload an attachment" or by dragging files 125 | onto the text area. Transloadit generates an inline preview image for 126 | images, videos, and audio files. 127 | » View the Assembly Template here. 132 |

      133 | 134 |

      135 | ⚠️ For this demo, snippets are stored locally in your browser. 136 | Attachments are stored in Transloadit's temporary storage and expire 137 | after about 24 hours. In a real app, you can easily export files to a 138 | permanent storage solution like Amazon S3 or Google Cloud. 139 | » Learn more 144 |

      145 | 146 |
      147 |

      Create a new snippet

      148 | 152 | 155 |

      156 | 157 |

      158 |
      159 | 160 |

      Previous snippets

      161 | 162 |
      163 |
      164 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /axamples/transloadit-markdown-bin/main.js: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked' 2 | import petProj from '@petProj/core' 3 | import DropTarget from '@petProj/drop-target' 4 | import Dashboard from '@petProj/dashboard' 5 | import Transloadit from '@petProj/transloadit' 6 | import RemoteSources from '@petProj/remote-sources' 7 | import Webcam from '@petProj/webcam' 8 | import ImageEditor from '@petProj/image-editor' 9 | 10 | import '@petProj/core/dist/style.css' 11 | import '@petProj/dashboard/dist/style.css' 12 | import '@petProj/image-editor/dist/style.css' 13 | 14 | const TRANSLOADIT_EXAMPLE_KEY = '35c1aed03f5011e982b6afe82599b6a0' 15 | const TRANSLOADIT_EXAMPLE_TEMPLATE = '0b2ee2bc25dc43619700c2ce0a75164a' 16 | 17 | function matchFilesAndThumbs (results) { 18 | const filesById = {} 19 | const thumbsById = {} 20 | 21 | for (const [stepName, result] of Object.entries(results)) { 22 | // eslint-disable-next-line no-shadow 23 | result.forEach(result => { 24 | if (stepName === 'thumbnails') { 25 | thumbsById[result.original_id] = result 26 | } else { 27 | filesById[result.original_id] = result 28 | } 29 | }) 30 | } 31 | 32 | return Object.keys(filesById).map((key) => ({ 33 | file: filesById[key], 34 | thumb: thumbsById[key], 35 | })) 36 | } 37 | 38 | /** 39 | * A textarea for markdown text, with support for file attachments. 40 | */ 41 | class MarkdownTextarea { 42 | constructor (element) { 43 | this.element = element 44 | this.controls = document.createElement('div') 45 | this.controls.classList.add('mdtxt-controls') 46 | this.uploadLine = document.createElement('button') 47 | this.uploadLine.setAttribute('type', 'button') 48 | this.uploadLine.classList.add('form-upload') 49 | 50 | this.uploadLine.appendChild( 51 | document.createTextNode('Tap here to upload an attachment'), 52 | ) 53 | } 54 | 55 | install () { 56 | const { element } = this 57 | const wrapper = document.createElement('div') 58 | wrapper.classList.add('mdtxt') 59 | element.parentNode.replaceChild(wrapper, element) 60 | wrapper.appendChild(this.controls) 61 | wrapper.appendChild(element) 62 | wrapper.appendChild(this.uploadLine) 63 | 64 | this.setuppetProj() 65 | } 66 | 67 | setuppetProj = () => { 68 | this.petProj = new petProj({ autoProceed: true }) 69 | .use(Transloadit, { 70 | waitForEncoding: true, 71 | params: { 72 | auth: { key: TRANSLOADIT_EXAMPLE_KEY }, 73 | template_id: TRANSLOADIT_EXAMPLE_TEMPLATE, 74 | }, 75 | }) 76 | .use(DropTarget, { target: this.element }) 77 | .use(Dashboard, { closeAfterFinish: true, trigger: '.form-upload' }) 78 | .use(ImageEditor, { target: Dashboard }) 79 | .use(Webcam, { target: Dashboard }) 80 | .use(RemoteSources, { companionUrl: Transloadit.COMPANION }) 81 | 82 | this.petProj.on('complete', (result) => { 83 | const { successful, failed, transloadit } = result 84 | if (successful.length !== 0) { 85 | this.insertAttachments( 86 | matchFilesAndThumbs(transloadit[0].results), 87 | ) 88 | } else { 89 | failed.forEach(error => { 90 | console.error(error) 91 | this.reportUploadError(error) 92 | }) 93 | } 94 | this.petProj.cancelAll() 95 | }) 96 | } 97 | 98 | reportUploadError (err) { 99 | this.uploadLine.classList.add('error') 100 | const message = document.createElement('span') 101 | message.appendChild(document.createTextNode(err.message)) 102 | this.uploadLine.insertChild(message, this.uploadLine.firstChild) 103 | } 104 | 105 | unreportUploadError () { 106 | this.uploadLine.classList.remove('error') 107 | const message = this.uploadLine.querySelector('message') 108 | if (message) { 109 | this.uploadLine.removeChild(message) 110 | } 111 | } 112 | 113 | insertAttachments (attachments) { 114 | attachments.forEach((attachment) => { 115 | const { file, thumb } = attachment 116 | const link = `\n[LABEL](${file.ssl_url})\n` 117 | const labelText = `View File ${file.basename}` 118 | if (thumb) { 119 | this.element.value += link.replace('LABEL', `![${labelText}](${thumb.ssl_url})`) 120 | } else { 121 | this.element.value += link.replace('LABEL', labelText) 122 | } 123 | }) 124 | } 125 | 126 | uploadFiles = (files) => { 127 | const filesForpetProj = files.map(file => { 128 | return { 129 | data: file, 130 | type: file.type, 131 | name: file.name, 132 | meta: file.meta || {}, 133 | } 134 | }) 135 | this.petProj.addFiles(filesForpetProj) 136 | } 137 | } 138 | 139 | const textarea = new MarkdownTextarea(document.querySelector('#new textarea')) 140 | textarea.install() 141 | 142 | function renderSnippet (title, text) { 143 | const template = document.querySelector('#snippet') 144 | const newSnippet = document.importNode(template.content, true) 145 | const titleEl = newSnippet.querySelector('.snippet-title') 146 | const contentEl = newSnippet.querySelector('.snippet-content') 147 | 148 | titleEl.appendChild(document.createTextNode(title)) 149 | contentEl.innerHTML = marked(text) 150 | 151 | const list = document.querySelector('#snippets') 152 | list.insertBefore(newSnippet, list.firstChild) 153 | } 154 | 155 | function saveSnippet (title, text) { 156 | const id = parseInt(localStorage.numSnippets || 0, 10) 157 | localStorage[`snippet_${id}`] = JSON.stringify({ title, text }) 158 | localStorage.numSnippets = id + 1 159 | } 160 | 161 | function loadSnippets () { 162 | for (let id = 0; localStorage[`snippet_${id}`] != null; id += 1) { 163 | const { title, text } = JSON.parse(localStorage[`snippet_${id}`]) 164 | renderSnippet(title, text) 165 | } 166 | } 167 | 168 | document.querySelector('#new').addEventListener('submit', (event) => { 169 | event.preventDefault() 170 | 171 | const title = event.target.elements['title'].value 172 | || 'Unnamed Snippet' 173 | const text = textarea.element.value 174 | 175 | saveSnippet(title, text) 176 | renderSnippet(title, text) 177 | 178 | // eslint-disable-next-line no-param-reassign 179 | event.target.querySelector('input').value = '' 180 | // eslint-disable-next-line no-param-reassign 181 | event.target.querySelector('textarea').value = '' 182 | }) 183 | 184 | window.addEventListener('DOMContentLoaded', loadSnippets, { once: true }) 185 | -------------------------------------------------------------------------------- /axamples/transloadit-markdown-bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/transloadit-markdown-bin", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@petProj/core": "workspace:*", 7 | "@petProj/dashboard": "workspace:*", 8 | "@petProj/drop-target": "workspace:*", 9 | "@petProj/image-editor": "workspace:*", 10 | "@petProj/remote-sources": "workspace:*", 11 | "@petProj/transloadit": "workspace:*", 12 | "@petProj/webcam": "workspace:*", 13 | "marked": "^4.0.18" 14 | }, 15 | "devDependencies": { 16 | "vite": "^4.0.0" 17 | }, 18 | "private": true, 19 | "scripts": { 20 | "start": "vite" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /axamples/transloadit/.gitignore: -------------------------------------------------------------------------------- 1 | petProj.min.css 2 | bundle.js 3 | bundle.js.map 4 | -------------------------------------------------------------------------------- /axamples/transloadit/README.md: -------------------------------------------------------------------------------- 1 | # Transloadit example 2 | 3 | This example shows how to make advantage of petProj API to upload files to 4 | [Transloadit](https://transloadit.com/). 5 | 6 | ## Run it 7 | 8 | To run this example, make sure you've correctly installed the **repository root**: 9 | 10 | ```sh 11 | corepack yarn install 12 | corepack yarn build 13 | ``` 14 | 15 | That will also install the dependencies for this example. 16 | 17 | Then, again in the **repository root**, start this example by doing: 18 | 19 | ```sh 20 | corepack yarn workspace @petProj-example/transloadit start 21 | ``` 22 | -------------------------------------------------------------------------------- /axamples/transloadit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Transloadit Example 7 | 8 | 9 | 36 |
      37 |

      38 | petProj Transloadit 39 | 44 | playground 45 |

      46 |

      47 | This page contains small examples for different ways you can use petProj 48 | with Transloadit. Please see the 49 | Github repository 53 | for the source code. 54 |

      55 |
      56 |

      Form

      57 | 58 |

      59 | The form API allows you to easily send files through Transloadit’s 60 | encoding backend. When the user submits the form, any files are uploaded 61 | to Transloadit. The form data is then sent to your own backend, with 62 | additional data about the Transloadit Assemblies that were started. 63 |

      64 |
      65 |

      leave a message

      66 |

      67 | 71 |

      72 |

      73 | 77 |

      78 |

      79 | 85 | 86 |

      87 |

      88 | 89 |

      90 | 91 |
      92 | 93 |
      94 |

      Form with inline Dashboard

      95 | 96 |

      You can also use the Dashboard UI inside a plain old HTML form.

      97 |
      102 |

      leave a message

      103 |

      104 | 108 |

      109 |

      110 | 114 |

      115 |

      116 | 120 |

      121 |

      122 | 123 |

      124 | 125 |
      126 | 127 |
      128 |

      Inline Dashboard

      129 |

      130 | The robodog.dashboard API allows you to embed a Dashboard 131 | at any location. Users can continuously upload files through this UI, so 132 | please make sure this fits your use case! 133 |

      134 |
      135 | 136 |
      137 |

      Dashboard Modal

      138 | 139 |

      140 | This API is a one-shot upload UI using a modal overlay. Call the 141 | function and receive a listen to an event with upload results ✌️ 142 |

      143 | 144 | 145 |

      petProj.upload()

      146 |

      An <input type=file> backed by petProj.upload():

      147 |

      148 | 149 |

      150 |
      151 |
      152 | 153 |
      
      163 |         
      
      164 |       
      165 |
      166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /axamples/transloadit/main.js: -------------------------------------------------------------------------------- 1 | import Transloadit, { COMPANION_URL } from '@petProj/transloadit' 2 | import petProj from '@petProj/core' 3 | import Form from '@petProj/form' 4 | import Dashboard from '@petProj/dashboard' 5 | import RemoteSources from '@petProj/remote-sources' 6 | import ImageEditor from '@petProj/image-editor' 7 | import Webcam from '@petProj/webcam' 8 | import ProgressBar from '@petProj/progress-bar' 9 | 10 | import '@petProj/core/dist/style.css' 11 | import '@petProj/dashboard/dist/style.css' 12 | import '@petProj/image-editor/dist/style.css' 13 | import '@petProj/progress-bar/dist/style.css' 14 | 15 | const TRANSLOADIT_KEY = '35c1aed03f5011e982b6afe82599b6a0' 16 | // A trivial template that resizes images, just for example purposes. 17 | // 18 | // "steps": { 19 | // ":original": { "robot": "/upload/handle" }, 20 | // "resize": { 21 | // "use": ":original", 22 | // "robot": "/image/resize", 23 | // "width": 100, 24 | // "height": 100, 25 | // "imagemagick_stack": "v1.0.0" 26 | // } 27 | // } 28 | const TEMPLATE_ID = 'bbc273f69e0c4694a5a9d1b587abc1bc' 29 | 30 | /** 31 | * Form 32 | */ 33 | 34 | const formpetProj = new petProj({ 35 | debug: true, 36 | autoProceed: true, 37 | restrictions: { 38 | allowedFileTypes: ['.png'], 39 | }, 40 | }) 41 | .use(Dashboard, { 42 | trigger: '#petProj-select-files', 43 | closeAfterFinish: true, 44 | note: 'Only PNG files please!', 45 | }) 46 | .use(RemoteSources, { companionUrl: COMPANION_URL }) 47 | .use(Form, { 48 | target: '#test-form', 49 | fields: ['message'], 50 | // submitOnSuccess: true, 51 | addResultToForm: true, 52 | }) 53 | .use(Transloadit, { 54 | waitForEncoding: true, 55 | params: { 56 | auth: { key: TRANSLOADIT_KEY }, 57 | template_id: TEMPLATE_ID, 58 | }, 59 | }) 60 | 61 | formpetProj.on('error', (err) => { 62 | document.querySelector('#test-form .error') 63 | .textContent = err.message 64 | }) 65 | 66 | formpetProj.on('upload-error', (file, err) => { 67 | document.querySelector('#test-form .error') 68 | .textContent = err.message 69 | }) 70 | 71 | formpetProj.on('complete', ({ transloadit }) => { 72 | const btn = document.getElementById('petProj-select-files') 73 | btn.hidden = true 74 | const selectedFiles = document.getElementById('petProj-form-selected-files') 75 | selectedFiles.textContent = `selected files: ${Object.keys(transloadit[0].results).length}` 76 | }) 77 | 78 | window.formpetProj = formpetProj 79 | 80 | /** 81 | * Form with Dashboard 82 | */ 83 | 84 | const formpetProjWithDashboard = new petProj({ 85 | debug: true, 86 | autoProceed: false, 87 | restrictions: { 88 | allowedFileTypes: ['.png'], 89 | }, 90 | }) 91 | .use(Dashboard, { 92 | inline: true, 93 | target: '#dashboard-form .dashboard', 94 | note: 'Only PNG files please!', 95 | hideUploadButton: true, 96 | }) 97 | .use(RemoteSources, { companionUrl: COMPANION_URL }) 98 | .use(Form, { 99 | target: '#dashboard-form', 100 | fields: ['message'], 101 | triggerUploadOnSubmit: true, 102 | submitOnSuccess: true, 103 | addResultToForm: true, 104 | }) 105 | .use(Transloadit, { 106 | waitForEncoding: true, 107 | params: { 108 | auth: { key: TRANSLOADIT_KEY }, 109 | template_id: TEMPLATE_ID, 110 | }, 111 | }) 112 | 113 | window.formpetProjWithDashboard = formpetProjWithDashboard 114 | 115 | /** 116 | * Dashboard 117 | */ 118 | 119 | const dashboard = new petProj({ 120 | debug: true, 121 | autoProceed: false, 122 | restrictions: { 123 | allowedFileTypes: ['.png'], 124 | }, 125 | }) 126 | .use(Dashboard, { 127 | inline: true, 128 | target: '#dashboard', 129 | note: 'Only PNG files please!', 130 | }) 131 | .use(RemoteSources, { companionUrl: COMPANION_URL }) 132 | .use(Webcam, { target: Dashboard }) 133 | .use(ImageEditor, { target: Dashboard }) 134 | .use(Transloadit, { 135 | waitForEncoding: true, 136 | params: { 137 | auth: { key: TRANSLOADIT_KEY }, 138 | template_id: TEMPLATE_ID, 139 | }, 140 | }) 141 | 142 | window.dashboard = dashboard 143 | 144 | // /** 145 | // * Dashboard Modal 146 | // */ 147 | 148 | const dashboardModal = new petProj({ 149 | debug: true, 150 | autoProceed: false, 151 | }) 152 | .use(Dashboard, { closeAfterFinish: true }) 153 | .use(RemoteSources, { companionUrl: COMPANION_URL }) 154 | .use(Webcam, { target: Dashboard }) 155 | .use(ImageEditor, { target: Dashboard }) 156 | .use(Transloadit, { 157 | waitForEncoding: true, 158 | params: { 159 | auth: { key: TRANSLOADIT_KEY }, 160 | template_id: TEMPLATE_ID, 161 | }, 162 | }) 163 | 164 | dashboardModal.on('complete', ({ transloadit, successful, failed }) => { 165 | if (failed?.length !== 0) { 166 | // eslint-disable-next-line no-console 167 | console.error('it failed', failed) 168 | } else { 169 | // eslint-disable-next-line no-console 170 | console.log('success', { transloadit, successful }) 171 | } 172 | }) 173 | 174 | function openModal () { 175 | dashboardModal.getPlugin('Dashboard').openModal() 176 | } 177 | 178 | window.openModal = openModal 179 | 180 | // /** 181 | // * petProj.upload (files come from input[type=file]) 182 | // */ 183 | 184 | const petProjWithoutUI = new petProj({ 185 | debug: true, 186 | restrictions: { 187 | allowedFileTypes: ['.png'], 188 | }, 189 | }) 190 | .use(Transloadit, { 191 | waitForEncoding: true, 192 | params: { 193 | auth: { key: TRANSLOADIT_KEY }, 194 | template_id: TEMPLATE_ID, 195 | }, 196 | }) 197 | .use(ProgressBar, { target: '#upload-progress' }) 198 | 199 | window.doUpload = (event) => { 200 | const resultEl = document.querySelector('#upload-result') 201 | const errorEl = document.querySelector('#upload-error') 202 | 203 | petProjWithoutUI.addFiles(event.target.files) 204 | petProjWithoutUI.upload() 205 | 206 | petProjWithoutUI.on('complete', ({ transloadit }) => { 207 | resultEl.classList.remove('hidden') 208 | errorEl.classList.add('hidden') 209 | resultEl.textContent = JSON.stringify(transloadit[0].results, null, 2) 210 | 211 | const resizedUrl = transloadit[0].results['resize'][0]['ssl_url'] 212 | const img = document.createElement('img') 213 | img.src = resizedUrl 214 | document.getElementById('upload-result-image').appendChild(img) 215 | }) 216 | 217 | petProjWithoutUI.on('error', (err) => { 218 | resultEl.classList.add('hidden') 219 | errorEl.classList.remove('hidden') 220 | errorEl.textContent = err.message 221 | }) 222 | } 223 | -------------------------------------------------------------------------------- /axamples/transloadit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/transloadit", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "devDependencies": { 6 | "npm-run-all": "^4.1.5", 7 | "vite": "^4.0.0" 8 | }, 9 | "dependencies": { 10 | "@petProj/core": "workspace:*", 11 | "@petProj/dashboard": "workspace:*", 12 | "@petProj/drop-target": "workspace:*", 13 | "@petProj/form": "workspace:*", 14 | "@petProj/image-editor": "workspace:*", 15 | "@petProj/progress-bar": "workspace:*", 16 | "@petProj/remote-sources": "workspace:*", 17 | "@petProj/transloadit": "workspace:*", 18 | "@petProj/webcam": "workspace:*", 19 | "express": "^4.16.4", 20 | "he": "^1.2.0" 21 | }, 22 | "private": true, 23 | "scripts": { 24 | "start:server": "node server.js", 25 | "start:client": "vite", 26 | "start": "npm-run-all --parallel start:server start:client" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /axamples/transloadit/server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-disable compat/compat */ 4 | import http from 'node:http' 5 | import qs from 'node:querystring' 6 | import he from 'he' 7 | 8 | const e = he.encode 9 | 10 | function Header () { 11 | return ` 12 | 13 | 14 | 15 | 25 | 26 | 27 |
      28 | ` 29 | } 30 | 31 | function Footer () { 32 | return ` 33 |
      34 | 35 | 36 | ` 37 | } 38 | 39 | function FormFields (fields) { 40 | function Field ([name, value]) { 41 | if (name === 'transloadit') return '' 42 | let isValueJSON = false 43 | if (value.startsWith('{') || value.startsWith('[')) { 44 | try { 45 | // eslint-disable-next-line no-param-reassign 46 | value = JSON.stringify( 47 | JSON.parse(value), 48 | null, 49 | 2, 50 | ) 51 | isValueJSON = true 52 | } catch { 53 | // Nothing 54 | } 55 | } 56 | 57 | const prettyValue = isValueJSON ? ` 58 |
      59 | 60 |
      ${e(value)}
      61 |
      62 |
      63 | ` : e(value) 64 | 65 | return ` 66 |
      ${e(name)}
      67 |
      68 | ${prettyValue} 69 |
      70 | ` 71 | } 72 | 73 | return ` 74 |

      Form Fields

      75 |
      76 | ${Object.entries(fields).map(Field).join('\n')} 77 |
      78 | ` 79 | } 80 | 81 | function UploadsList (uploads) { 82 | function Upload (upload) { 83 | return `
    1. ${e(upload.name)}
    2. ` 84 | } 85 | 86 | return ` 87 |
        88 | ${uploads.map(Upload).join('\n')} 89 |
      90 | ` 91 | } 92 | 93 | function ResultsList (results) { 94 | function Result (result) { 95 | return `
    3. ${e(result.name)} View
    4. ` 96 | } 97 | 98 | function ResultsSection (stepName) { 99 | return ` 100 |

      ${e(stepName)}

      101 |
        102 | ${results[stepName].map(Result).join('\n')} 103 |
      104 | ` 105 | } 106 | 107 | return Object.keys(results) 108 | .map(ResultsSection) 109 | .join('\n') 110 | } 111 | 112 | function AssemblyResult (assembly) { 113 | return ` 114 |

      ${e(assembly.assembly_id)} (${e(assembly.ok)})

      115 | ${UploadsList(assembly.uploads)} 116 | ${ResultsList(assembly.results)} 117 | ` 118 | } 119 | 120 | function onrequest (req, res) { 121 | if (req.url !== '/test') { 122 | res.writeHead(404, { 'content-type': 'text/html' }) 123 | res.end('404') 124 | return 125 | } 126 | 127 | function onbody (body) { 128 | const fields = qs.parse(body) 129 | const result = JSON.parse(fields.petProjResult) 130 | const assemblies = result[0].transloadit 131 | 132 | res.setHeader('content-type', 'text/html') 133 | res.write(Header()) 134 | res.write(FormFields(fields)) 135 | assemblies.forEach((assembly) => { 136 | res.write(AssemblyResult(assembly)) 137 | }) 138 | res.end(Footer()) 139 | } 140 | 141 | { 142 | let body = '' 143 | req.on('data', (chunk) => { body += chunk }) 144 | req.on('end', () => { 145 | onbody(body) 146 | }) 147 | } 148 | } 149 | 150 | /** 151 | * A very haxxor server that outputs some of the data it receives in a POST form parameter. 152 | */ 153 | 154 | const server = http.createServer(onrequest) 155 | server.listen(9967) 156 | -------------------------------------------------------------------------------- /axamples/vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /public 5 | 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /axamples/vue/README.md: -------------------------------------------------------------------------------- 1 | # Vue 2 example 2 | 3 | You’re browsing the documentation for Vue v2.x and earlier. Check out 4 | [Vue 3 example](../vue3/) for new projects. 5 | 6 | To run the example, from the root directory of this repo, run the following commands: 7 | 8 | ```sh 9 | corepack yarn install 10 | corepack yarn build 11 | corepack yarn workspace @petProj-example/vue2 dev 12 | ``` 13 | -------------------------------------------------------------------------------- /axamples/vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
      11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /axamples/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/vue2", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview --port 5050" 9 | }, 10 | "dependencies": { 11 | "@petProj/core": "workspace:*", 12 | "@petProj/dashboard": "workspace:*", 13 | "@petProj/drag-drop": "workspace:*", 14 | "@petProj/progress-bar": "workspace:*", 15 | "@petProj/transloadit": "workspace:*", 16 | "@petProj/vue": "workspace:*", 17 | "vue": "^2.6.14" 18 | }, 19 | "devDependencies": { 20 | "vite": "^4.0.0", 21 | "vite-plugin-vue2": "^2.0.1", 22 | "vue-template-compiler": "^2.6.14" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /axamples/vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 97 | 98 | 99 | 100 | 101 | 102 | 112 | -------------------------------------------------------------------------------- /axamples/vue/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /axamples/vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { createVuePlugin } from 'vite-plugin-vue2' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [createVuePlugin()], 7 | }) 8 | -------------------------------------------------------------------------------- /axamples/vue3/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /axamples/vue3/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 example 2 | 3 | To run the example, from the root directory of this repo, run the following commands: 4 | 5 | ```sh 6 | cp .env.example .env 7 | corepack yarn install 8 | corepack yarn build 9 | corepack yarn workspace @petProj-example/vue3 dev 10 | ``` 11 | -------------------------------------------------------------------------------- /axamples/vue3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
      11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /axamples/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@petProj-example/vue3", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview --port 5050" 9 | }, 10 | "dependencies": { 11 | "@petProj/core": "workspace:*", 12 | "@petProj/dashboard": "workspace:*", 13 | "@petProj/drag-drop": "workspace:*", 14 | "@petProj/progress-bar": "workspace:*", 15 | "@petProj/tus": "workspace:*", 16 | "@petProj/vue": "workspace:*", 17 | "vue": "^3.2.33" 18 | }, 19 | "devDependencies": { 20 | "@vitejs/plugin-vue": "^3.0.0", 21 | "vite": "^4.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /axamples/vue3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/axamples/vue3/public/favicon.ico -------------------------------------------------------------------------------- /axamples/vue3/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 63 | 64 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 111 | -------------------------------------------------------------------------------- /axamples/vue3/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberblobber/petProj/10e8cc2355e4e0d0616fcdaaa59ca334fd873a78/axamples/vue3/src/assets/logo.png -------------------------------------------------------------------------------- /axamples/vue3/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /axamples/vue3/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vite' 3 | import vue from '@vitejs/plugin-vue' 4 | 5 | const ROOT = new URL('../../', import.meta.url) 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | envDir: fileURLToPath(ROOT), 10 | plugins: [vue()], 11 | }) 12 | -------------------------------------------------------------------------------- /axamples/xhr-bundle/README.md: -------------------------------------------------------------------------------- 1 | # XHR Bundle Upload 2 | 3 | This example uses petProj with XHRUpload plugin in `bundle` mode. Bundle mode uploads all files to the endpoint in a single request, instead of firing off a new request for each file. This makes uploading a bit slower, but it may be easier to handle on the server side, depending on your setup. 4 | 5 | [`server.cjs`](./server.cjs) contains an example express.js server that receives a multipart form-data upload and responds with some information about the files that were received (name, size) as JSON. It uses [multer](https://npmjs.com/package/multer) to parse the upload stream. 6 | 7 | ## Run it 8 | 9 | To run this example, make sure you've correctly installed the **repository root**: 10 | 11 | ```sh 12 | corepack yarn install 13 | corepack yarn build 14 | ``` 15 | 16 | That will also install the dependencies for this example. 17 | 18 | Then, again in the **repository root**, start this example by doing: 19 | 20 | ```sh 21 | corepack yarn workspace @petProj-example/xhr-bundle start 22 | ``` 23 | -------------------------------------------------------------------------------- /axamples/xhr-bundle/main.js: -------------------------------------------------------------------------------- 1 | import petProj from '@petProj/core' 2 | import Dashboard from '@petProj/dashboard' 3 | import XHRUpload from '@petProj/xhr-upload' 4 | 5 | import '@petProj/core/dist/style.css' 6 | import '@petProj/dashboard/dist/style.css' 7 | 8 | const petProj = new petProj({ 9 | debug: true, 10 | meta: { something: 'xyz' }, 11 | }) 12 | 13 | petProj.use(Dashboard, { 14 | target: '#app', 15 | inline: true, 16 | hideRetryButton: true, 17 | hideCancelButton: true, 18 | }) 19 | 20 | petProj.use(XHRUpload, { 21 | bundle: true, 22 | endpoint: 'http://localhost:9967/upload', 23 | allowedMetaFields: ['something'], 24 | fieldName: 'files', 25 | }) 26 | --------------------------------------------------------------------------------