BTHLABS-50: Safari Web extension
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl> Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2
services/extension/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
26
services/extension/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
ARG APP_USER_UID=1000
|
||||
ARG APP_USER_GID=1000
|
||||
ARG IMAGE_ID=development.00000000
|
||||
|
||||
FROM docker-hosted.nexus.bthlabs.pl/hotpocket/base:build-node-20250819-01 AS development
|
||||
|
||||
ARG APP_USER_UID
|
||||
ARG APP_USER_GID
|
||||
ARG IMAGE_ID
|
||||
|
||||
USER root
|
||||
|
||||
# COPY --chown=$APP_USER_UID:$APP_USER_GID extension/ops/bin/*.sh /srv/bin/
|
||||
RUN chown -R ${APP_USER_UID}:${APP_USER_GID} /srv
|
||||
|
||||
USER app
|
||||
|
||||
VOLUME ["/srv/node_modules", "/srv/venv"]
|
||||
|
||||
FROM development AS ci
|
||||
|
||||
COPY --chown=$APP_USER_UID:$APP_USER_GID extension/ /srv/app/
|
||||
COPY --chown=$APP_USER_UID:$APP_USER_GID packages/ /srv/packages/
|
||||
COPY --chown=$APP_USER_UID:$APP_USER_GID tls/ /srv/tls/
|
||||
|
||||
RUN chown -R $APP_USER_UID:$APP_USER_GID /srv
|
||||
3
services/extension/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# HotPocket by BTHLabs
|
||||
|
||||
This repository contains the _HotPocket Extension_ project.
|
||||
30
services/extension/assets/_locales/en/messages.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extension_name": {
|
||||
"message": "Save to HotPocket",
|
||||
"description": "The display name for the extension."
|
||||
},
|
||||
"extension_description": {
|
||||
"message": "Save to HotPocket. Straight from the toolbar!",
|
||||
"description": "Description of what the extension does."
|
||||
},
|
||||
"extension_description_Safari": {
|
||||
"message": "Save to HotPocket. Straight from Safari's toolbar!",
|
||||
"description": "Description of what the extension does."
|
||||
},
|
||||
"content_popup_content_success_title": {
|
||||
"message": "Saved!",
|
||||
"description": "Title of the success content popup."
|
||||
},
|
||||
"content_popup_content_success_message": {
|
||||
"message": "Your link has been saved!",
|
||||
"description": "Message of the success content popup."
|
||||
},
|
||||
"content_popup_content_error_title": {
|
||||
"message": "Oops!",
|
||||
"description": "Title of the error content popup."
|
||||
},
|
||||
"content_popup_content_error_message": {
|
||||
"message": "HotPocket couldn't complete this operation.",
|
||||
"description": "Title of the error content popup."
|
||||
}
|
||||
}
|
||||
BIN
services/extension/assets/images/icon-128.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
services/extension/assets/images/icon-256.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
services/extension/assets/images/icon-48.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
services/extension/assets/images/icon-512.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
services/extension/assets/images/icon-64.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
services/extension/assets/images/icon-96.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
29
services/extension/assets/images/toolbar-icon.svg
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
|
||||
<title>toolbar-icon</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="toolbar-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="HotPocket-Unstyled" fill="#000000">
|
||||
<g id="Group">
|
||||
<g id="patch-fashion-svgrepo-com" fill-rule="nonzero">
|
||||
<path d="M15.7333437,0 L0.26665625,0 C0.10665625,0 0,0.10665625 0,0.26665625 L0,11.4666562 C0,11.5733125 0.05334375,11.6533125 0.13334375,11.7066562 L7.8666875,15.9733125 C7.92,16 7.94665625,16 8,16 C8.05334375,16 8.08,16 8.13334375,15.9733437 L15.8666875,11.7066875 C15.9466875,11.6533438 16.0000312,11.5733438 16.0000312,11.4666875 L16.0000312,0.26665625 C16.0000312,0.10665625 15.8933437,0 15.7333437,0 Z M15.4666875,11.3066562 L8,15.4133437 L0.53334375,11.3066562 L0.53334375,0.53334375 L15.4666875,0.53334375 L15.4666875,11.3066562 L15.4666875,11.3066562 Z" id="Shape"></path>
|
||||
<path d="M1.73334375,10.7466562 L2.77334375,11.3333125 C2.8266875,11.3599687 2.85334375,11.3599687 2.9066875,11.3599687 C3.01334375,11.3599687 3.09334375,11.306625 3.1466875,11.2533125 C3.2266875,11.1199687 3.17334375,10.9599687 3.04003125,10.8799687 L2.133375,10.3733125 L2.133375,9.3333125 C2.133375,9.1733125 2.02671875,9.06665625 1.86671875,9.06665625 C1.70671875,9.06665625 1.60006245,9.1733125 1.60006245,9.3333125 L1.60006245,10.5333125 C1.6,10.6133437 1.65334375,10.72 1.73334375,10.7466562 Z" id="Path"></path>
|
||||
<path d="M1.86665625,7.81334375 C2.02665625,7.81334375 2.1333125,7.68 2.1333125,7.5466875 L2.1333125,4.85334375 C2.1333125,4.69334375 2.02665625,4.5866875 1.86665625,4.5866875 C1.70665625,4.5866875 1.6,4.69334375 1.6,4.85334375 L1.6,7.5466875 C1.6,7.70665625 1.70665625,7.81334375 1.86665625,7.81334375 Z" id="Path"></path>
|
||||
<path d="M1.86665625,3.33334375 C2.02665625,3.33334375 2.1333125,3.2266875 2.1333125,3.0666875 L2.1333125,2.13334375 L3.06665625,2.13334375 C3.22665625,2.13334375 3.3333125,2.0266875 3.3333125,1.8666875 C3.3333125,1.7066875 3.22665625,1.60003125 3.06665625,1.60003125 L1.86665625,1.60003125 C1.70665625,1.60003125 1.6,1.7066875 1.6,1.8666875 L1.6,3.0666875 C1.6,3.22665625 1.70665625,3.33334375 1.86665625,3.33334375 Z" id="Path"></path>
|
||||
<path d="M4.72,2.13334375 L7.17334375,2.13334375 C7.33334375,2.13334375 7.44,2.0266875 7.44,1.8666875 C7.44,1.7066875 7.33334375,1.60003125 7.17334375,1.60003125 L4.72,1.60003125 C4.56,1.60003125 4.45334375,1.7066875 4.45334375,1.8666875 C4.45334375,2.0266875 4.56,2.13334375 4.72,2.13334375 Z" id="Path"></path>
|
||||
<path d="M8.82665625,2.13334375 L11.28,2.13334375 C11.44,2.13334375 11.5466562,2.0266875 11.5466562,1.8666875 C11.5466562,1.7066875 11.44,1.6 11.28,1.6 L8.82665625,1.6 C8.66665625,1.6 8.56,1.70665625 8.56,1.86665625 C8.56,2.02665625 8.66665625,2.13334375 8.82665625,2.13334375 Z" id="Path"></path>
|
||||
<path d="M12.9333438,2.13334375 L13.8666875,2.13334375 L13.8666875,3.0666875 C13.8666875,3.2266875 13.9733438,3.33334375 14.1333438,3.33334375 C14.2933438,3.33334375 14.4,3.2266875 14.4,3.0666875 L14.4,1.8666875 C14.4,1.7066875 14.2933438,1.60003125 14.1333438,1.60003125 L12.9333438,1.60003125 C12.7733438,1.60003125 12.6666875,1.7066875 12.6666875,1.8666875 C12.6666875,2.0266875 12.7733438,2.13334375 12.9333438,2.13334375 Z" id="Path"></path>
|
||||
<path d="M14.1333438,4.58665625 C13.9733438,4.58665625 13.8666875,4.6933125 13.8666875,4.8533125 L13.8666875,7.54665625 C13.8666875,7.70665625 13.9733438,7.8133125 14.1333438,7.8133125 C14.2933438,7.8133125 14.4,7.67996875 14.4,7.54665625 L14.4,4.8533125 C14.4,4.69334375 14.2933438,4.58665625 14.1333438,4.58665625 Z" id="Path"></path>
|
||||
<path d="M14.1333437,9.06665625 C13.9733437,9.06665625 13.8666875,9.1733125 13.8666875,9.3333125 L13.8666875,10.3466563 L12.9600313,10.8533125 C12.8266875,10.9333125 12.773375,11.0933125 12.853375,11.2266563 C12.9067187,11.3066563 12.9867187,11.36 13.093375,11.36 C13.1200313,11.36 13.173375,11.36 13.2267187,11.36 L14.2667187,10.7733438 C14.3467187,10.72 14.4000625,10.64 14.4000625,10.5333438 L14.4000625,9.33334375 C14.4,9.17334375 14.2933437,9.06665625 14.1333437,9.06665625 Z" id="Path"></path>
|
||||
<path d="M9.97334375,12.8266562 C10.0266875,12.9066562 10.1066875,12.96 10.2133437,12.96 C10.24,12.96 10.2933437,12.9333437 10.3466875,12.9333437 L12.0800312,11.9733437 C12.213375,11.8933437 12.2666875,11.7333437 12.1866875,11.6 C12.1066875,11.4666562 11.9466875,11.4133437 11.8133437,11.4933437 L10.08,12.4533437 C9.94665625,12.5333437 9.89334375,12.6933437 9.97334375,12.8266562 Z" id="Path"></path>
|
||||
<path d="M6.72,13.1733438 C6.64,13.3066875 6.69334375,13.4666875 6.82665625,13.5466875 L7.86665625,14.1333438 C7.92,14.16 7.94665625,14.16 8,14.16 C8.05334375,14.16 8.08,14.16 8.13334375,14.1333438 L9.2,13.5733438 C9.33334375,13.4933438 9.38665625,13.3333438 9.30665625,13.2 C9.22665625,13.0666563 9.06665625,13.0133438 8.9333125,13.0933438 L8,13.6 L7.09334375,13.0666563 C6.96,12.9866563 6.8,13.04 6.72,13.1733438 Z" id="Path"></path>
|
||||
<path d="M3.92,11.9733437 L5.65334375,12.9333438 C5.7066875,12.96 5.73334375,12.96 5.7866875,12.96 C5.89334375,12.96 5.97334375,12.9066563 6.0266875,12.8266563 C6.1066875,12.6933125 6.05334375,12.5333125 5.92003125,12.4533125 L4.1866875,11.4933125 C4.05334375,11.4133125 3.89334375,11.4666562 3.81334375,11.5999687 C3.73334375,11.7332812 3.78665625,11.8933437 3.92,11.9733437 Z" id="Path"></path>
|
||||
</g>
|
||||
<g id="pepper-hot-solid-svgrepo-com" transform="translate(3.480000, 1.560000)" fill-rule="nonzero">
|
||||
<path d="M9.04,0.750430988 L9.04,0 C8.29241927,0 7.7093357,0.208127532 7.3083736,0.61558792 C7.07631531,0.851563196 6.95147381,1.113921 6.87950602,1.33963676 C6.68857201,1.20772487 6.4667945,1.12564648 6.22004877,1.12564648 C5.27125287,1.12564648 4.91288407,1.87900903 4.90407153,2.39199878 C4.90407153,2.47407716 4.90113427,2.5605527 4.89525937,2.64995943 C4.64263874,3.39599327 4.15208453,2.90205734 4.15208453,1.87607747 C2.94038683,3.16001799 4.24608291,3.63343451 3.50143944,4.37653716 C3.34281719,4.5348312 2.96388643,4.5963898 2.72742186,4.30618426 C2.41458324,3.93536554 3.05054127,3.32564036 2.448364,2.86981244 C2.55851844,3.55575327 2.02830895,3.41358149 1.84912436,3.26701294 C1.61559706,3.07647401 1.19113574,2.65728776 1.89612355,1.50086198 C1.17497968,1.82917553 0.740237198,2.49606254 0.768143059,3.00172395 C0.843048108,4.32816926 2.89779392,5.48019827 2.06796424,6.25408023 C1.64937782,6.64541835 0.826892043,6.1163056 1.14413656,5.25301692 C0.749049733,5.45381574 0.28493276,5.93162941 0.433273851,6.67473207 C0.536084761,7.1891874 1.28513412,8.34854475 0.0161560646,9.00517186 C0.0161560646,9.00517186 0.022030963,9.00663745 0.0323121292,9.00956901 L0,9.75560285 C0.0249686002,9.75706844 0.126310503,9.76 0.289339028,9.76 C1.34682074,9.76 5.00688244,9.59730881 7.25990579,7.44421687 C8.44076036,6.31563883 9.04,4.8206396 9.04,3.00172395 C9.04,2.48287147 8.80353543,1.50086198 7.91201951,1.50086198 C7.80186507,1.50086198 7.69905454,1.51551902 7.60211852,1.53896999 C7.64471182,1.41292085 7.71814786,1.26928385 7.84445836,1.14030353 C8.10001626,0.882342873 8.50097798,0.750430988 9.04,0.750430988 Z M7.91201951,2.25129297 C8.26745105,2.25129297 8.28801301,2.99586121 8.28801301,3.00172395 C8.28801301,4.6066497 7.76661568,5.91843833 6.7414461,6.90044745 C5.07738122,8.49218212 2.45570753,8.88645143 1.03251349,8.9802553 C1.37913249,8.9011081 1.79625028,8.73988288 2.25302372,8.43208873 C4.45317298,7.22876101 5.61052797,4.39119383 5.65312126,2.50192528 L5.65605852,2.50192528 L5.65605852,2.39932711 C5.65752715,2.31138598 5.69277655,1.87607747 6.22004877,1.87607747 C6.54169955,1.87607747 6.78403902,2.33190576 6.78403902,2.62650846 L7.53602601,2.62650846 C7.53602601,2.46528324 7.57568167,2.25129297 7.91201951,2.25129297 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
23
services/extension/docker-compose-ci.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
extension-ci:
|
||||
build:
|
||||
context: ".."
|
||||
dockerfile: "extension/Dockerfile"
|
||||
target: "development"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-local"
|
||||
command: "echo 'NOOP'"
|
||||
environment:
|
||||
PYTHONBREAKPOINT: "ipdb.set_trace"
|
||||
HOTPOCKET_PACKAGES_ENV: "${HOTPOCKET_EXTENSION_ENV:-docker}"
|
||||
# REQUESTS_CA_BUNDLE: "/srv/tls/requests_ca_bundle.pem"
|
||||
RUN_POETRY_INSTALL: "true"
|
||||
RUN_YARN_INSTALL: "true"
|
||||
SETUP_BACKEND: "true"
|
||||
SETUP_FRONTEND: "true"
|
||||
volumes:
|
||||
- "extension_venv:/srv/venv"
|
||||
- "extension_node_modules:/srv/node_modules"
|
||||
- "../tls:/srv/tls"
|
||||
restart: "no"
|
||||
stdin_open: true
|
||||
tty: true
|
||||
29
services/extension/docker-compose.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
services:
|
||||
extension-management:
|
||||
build:
|
||||
context: ".."
|
||||
dockerfile: "extension/Dockerfile"
|
||||
target: "development"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:local"
|
||||
command: "echo 'NOOP'"
|
||||
environment: &extension-env
|
||||
PYTHONBREAKPOINT: "ipdb.set_trace"
|
||||
HOTPOCKET_EXTENSION_ENV: "${HOTPOCKET_EXTENSION_ENV:-docker}"
|
||||
REQUESTS_CA_BUNDLE: "/srv/tls/requests_ca_bundle.pem"
|
||||
RUN_POETRY_INSTALL: "true"
|
||||
RUN_YARN_INSTALL: "true"
|
||||
SETUP_BACKEND: "true"
|
||||
SETUP_FRONTEND: "true"
|
||||
volumes:
|
||||
- "extension_venv:/srv/venv"
|
||||
- "extension_node_modules:/srv/node_modules"
|
||||
- ".:/srv/app"
|
||||
- "../packages:/srv/packages"
|
||||
- "../tls:/srv/tls"
|
||||
restart: "no"
|
||||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
volumes:
|
||||
extension_venv:
|
||||
extension_node_modules:
|
||||
74
services/extension/eslint.config.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// eslint.config.js
|
||||
import js from '@eslint/js';
|
||||
import {defineConfig} from 'eslint/config';
|
||||
import globals from 'globals';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
files: [
|
||||
'eslint.config.js',
|
||||
'src/**/*.js',
|
||||
],
|
||||
plugins: {
|
||||
js,
|
||||
},
|
||||
extends: ['js/recommended'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.webextensions,
|
||||
__HOTPOCKET_EXTENSION_ENV__: false,
|
||||
__HOTPOCKET_EXTENSION_VERSION__: false,
|
||||
__HOTPOCKET_EXTENSION_BASE_URL__: false,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-undef': 'error',
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{'avoidEscape': true, 'allowTemplateLiterals': true},
|
||||
],
|
||||
'no-unused-vars': ['error', {'args': 'none'}],
|
||||
'no-console': ['error', {'allow': ['warn', 'error', 'info']}],
|
||||
'no-empty': ['error', {'allowEmptyCatch': true}],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'block-spacing': ['error', 'always'],
|
||||
'brace-style': ['error', '1tbs', {'allowSingleLine': true}],
|
||||
'camelcase': ['error', {'properties': 'never'}],
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'comma-spacing': ['error', {'before': false, 'after': true}],
|
||||
'comma-style': ['error', 'last'],
|
||||
'computed-property-spacing': ['error', 'never'],
|
||||
'key-spacing': [
|
||||
'error', {'beforeColon': false, 'afterColon': true, 'mode': 'strict'},
|
||||
],
|
||||
'keyword-spacing': ['error', { 'before': true, 'after': true }],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'max-len': ['error', 120],
|
||||
'no-multiple-empty-lines': 'error',
|
||||
'no-spaced-func': 'error',
|
||||
'no-trailing-spaces': 'error',
|
||||
'no-unreachable': 'warn',
|
||||
'no-whitespace-before-property': 'error',
|
||||
'object-curly-spacing': 'off',
|
||||
'one-var-declaration-per-line': ['error', 'always'],
|
||||
'one-var': ['error', 'never'],
|
||||
'semi-spacing': ['error', {'before': false, 'after': true}],
|
||||
'semi': ['error', 'always'],
|
||||
'space-before-function-paren': ['error', 'always'],
|
||||
'space-before-blocks': ['error', 'always'],
|
||||
'space-in-parens': ['error', 'never'],
|
||||
'space-infix-ops': 'error',
|
||||
'unicode-bom': ['error', 'never'],
|
||||
'no-useless-escape': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'no-invalid-this': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['dist/**'],
|
||||
},
|
||||
]);
|
||||
3
services/extension/invoke.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
run:
|
||||
echo: true
|
||||
pty: true
|
||||
27
services/extension/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "hotpocket-extension",
|
||||
"version": "1.0.2",
|
||||
"description": "HotPocket Extension",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
||||
"author": "Tomek Wójcik <contact@bthlabs.pl>",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"eslint": "npx eslint .",
|
||||
"build:safari": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js",
|
||||
"dev:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js",
|
||||
"watch:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.33.0",
|
||||
"@rollup/plugin-replace": "6.0.2",
|
||||
"@rollup/plugin-terser": "0.4.4",
|
||||
"eslint": "9.33.0",
|
||||
"globals": "14.0.0",
|
||||
"rollup": "4.50.0",
|
||||
"rollup-plugin-copy": "3.5.0",
|
||||
"rollup-plugin-string": "3.0.0"
|
||||
}
|
||||
}
|
||||
508
services/extension/poetry.lock
generated
Normal file
@@ -0,0 +1,508 @@
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "asttokens"
|
||||
version = "3.0.0"
|
||||
description = "Annotate AST trees with source code positions"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
|
||||
{file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
astroid = ["astroid (>=2,<4)"]
|
||||
test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.2.1"
|
||||
description = "Decorators for Humans"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
|
||||
{file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.2.1"
|
||||
description = "Get the currently executing AST node of a frame, and other information"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"},
|
||||
{file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
||||
|
||||
[[package]]
|
||||
name = "factory-boy"
|
||||
version = "3.3.3"
|
||||
description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc"},
|
||||
{file = "factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Faker = ">=0.7.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "mongomock", "mypy", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"]
|
||||
doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
|
||||
|
||||
[[package]]
|
||||
name = "faker"
|
||||
version = "37.6.0"
|
||||
description = "Faker is a Python package that generates fake data for you."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "faker-37.6.0-py3-none-any.whl", hash = "sha256:3c5209b23d7049d596a51db5d76403a0ccfea6fc294ffa2ecfef6a8843b1e6a7"},
|
||||
{file = "faker-37.6.0.tar.gz", hash = "sha256:0f8cc34f30095184adf87c3c24c45b38b33ad81c35ef6eb0a3118f301143012c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tzdata = "*"
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "7.3.0"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"},
|
||||
{file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.14.0,<2.15.0"
|
||||
pyflakes = ">=3.4.0,<3.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-commas"
|
||||
version = "4.0.0"
|
||||
description = "Flake8 lint for trailing commas."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "flake8_commas-4.0.0-py3-none-any.whl", hash = "sha256:cad476d71ba72e8b941a8508d5b9ffb6b03e50f7102982474f085ad0d674b685"},
|
||||
{file = "flake8_commas-4.0.0.tar.gz", hash = "sha256:a68834b42a9a31c94ca790efe557a932c0eae21a3479c6b9a23c4dc077e3ea96"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
flake8 = ">=5"
|
||||
|
||||
[[package]]
|
||||
name = "hotpocket-workspace-tools"
|
||||
version = "1.0.0.dev0"
|
||||
description = "HotPocket Workspace Tools"
|
||||
optional = false
|
||||
python-versions = "^3.12"
|
||||
files = []
|
||||
develop = true
|
||||
|
||||
[package.source]
|
||||
type = "directory"
|
||||
url = "../packages/workspace_tools"
|
||||
|
||||
[[package]]
|
||||
name = "invoke"
|
||||
version = "2.2.0"
|
||||
description = "Pythonic task execution"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
|
||||
{file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdb"
|
||||
version = "0.13.13"
|
||||
description = "IPython-enabled pdb"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
{file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"},
|
||||
{file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
decorator = {version = "*", markers = "python_version >= \"3.11\""}
|
||||
ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "9.3.0"
|
||||
description = "IPython: Productive Interactive Computing"
|
||||
optional = false
|
||||
python-versions = ">=3.11"
|
||||
files = [
|
||||
{file = "ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04"},
|
||||
{file = "ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
decorator = "*"
|
||||
ipython-pygments-lexers = "*"
|
||||
jedi = ">=0.16"
|
||||
matplotlib-inline = "*"
|
||||
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
|
||||
prompt_toolkit = ">=3.0.41,<3.1.0"
|
||||
pygments = ">=2.4.0"
|
||||
stack_data = "*"
|
||||
traitlets = ">=5.13.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["ipython[doc,matplotlib,test,test-extra]"]
|
||||
black = ["black"]
|
||||
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"]
|
||||
matplotlib = ["matplotlib"]
|
||||
test = ["packaging", "pytest", "pytest-asyncio (<0.22)", "testpath"]
|
||||
test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
|
||||
|
||||
[[package]]
|
||||
name = "ipython-pygments-lexers"
|
||||
version = "1.1.1"
|
||||
description = "Defines a variety of Pygments lexers for highlighting IPython code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"},
|
||||
{file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pygments = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "6.0.1"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
optional = false
|
||||
python-versions = ">=3.9.0"
|
||||
files = [
|
||||
{file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"},
|
||||
{file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama"]
|
||||
plugins = ["setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "jedi"
|
||||
version = "0.19.2"
|
||||
description = "An autocompletion tool for Python that can be used for text editors."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
|
||||
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
parso = ">=0.8.4,<0.9.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "matplotlib-inline"
|
||||
version = "0.1.7"
|
||||
description = "Inline Matplotlib backend for Jupyter"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
||||
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
traitlets = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.16.1"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"},
|
||||
{file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"},
|
||||
{file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mypy_extensions = ">=1.0.0"
|
||||
pathspec = ">=0.9.0"
|
||||
typing_extensions = ">=4.6.0"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
faster-cache = ["orjson"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
|
||||
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.5"
|
||||
description = "A Python Parser"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"},
|
||||
{file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["docopt", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pexpect"
|
||||
version = "4.9.0"
|
||||
description = "Pexpect allows easy control of interactive console applications."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
||||
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
ptyprocess = ">=0.5"
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.52"
|
||||
description = "Library for building powerful interactive command lines in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"},
|
||||
{file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
version = "0.7.0"
|
||||
description = "Run a subprocess in a pseudo terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
||||
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-eval"
|
||||
version = "0.2.3"
|
||||
description = "Safely evaluate AST nodes without side effects"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
||||
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.14.0"
|
||||
description = "Python style guide checker"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"},
|
||||
{file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.4.0"
|
||||
description = "passive checker of Python programs"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"},
|
||||
{file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
|
||||
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "stack-data"
|
||||
version = "0.6.3"
|
||||
description = "Extract data from python stack frames and tracebacks for informative displays"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
||||
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asttokens = ">=2.1.0"
|
||||
executing = ">=1.2.0"
|
||||
pure-eval = "*"
|
||||
|
||||
[package.extras]
|
||||
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
|
||||
|
||||
[[package]]
|
||||
name = "traitlets"
|
||||
version = "5.14.3"
|
||||
description = "Traitlets Python configuration system"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
||||
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
||||
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
|
||||
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
|
||||
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
||||
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "1c74b6d5928a0b1bde41962f4233af2eba2242324c3155b21093b2f46baf5cd0"
|
||||
26
services/extension/pyproject.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[tool.poetry]
|
||||
name = "hotpocket-extension"
|
||||
version = "1.0.2"
|
||||
description = "HotPocket Extension"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
package-mode = false
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
factory-boy = "3.3.3"
|
||||
flake8 = "7.3.0"
|
||||
flake8-commas = "4.0.0"
|
||||
hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true}
|
||||
invoke = "2.2.0"
|
||||
ipdb = "0.13.13"
|
||||
ipython = "9.3.0"
|
||||
isort = "6.0.1"
|
||||
mypy = "1.16.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
125
services/extension/rollup.config.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import {string} from 'rollup-plugin-string';
|
||||
|
||||
import packageJSON from './package.json' with {type: 'json'};
|
||||
import manifestCommon from './src/manifest/common.json' with {type: 'json'};
|
||||
import manifestSafari from './src/manifest/safari.json' with {type: 'json'};
|
||||
|
||||
const BANNER = `/*!
|
||||
* HotPocket by BTHLabs (https://hotpocket.app/)
|
||||
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/`;
|
||||
|
||||
const ENV = process.env['NODE_ENV'] || 'development';
|
||||
const IS_PRODUCTION = (ENV === 'production');
|
||||
const TARGET = process.env['HOTPOCKET_EXTENSION_TARGET'] || 'safari';
|
||||
|
||||
let BASE_URL = process.env['HOTPOCKET_EXTENSION_RPC_BASE_URL'] || null;
|
||||
if (BASE_URL === null) {
|
||||
if (IS_PRODUCTION) {
|
||||
BASE_URL = 'https://my.hotpocket.app/';
|
||||
} else {
|
||||
BASE_URL = 'https://app.hotpocket.work.bthlabs.net/';
|
||||
}
|
||||
}
|
||||
|
||||
const PLUGINS = [
|
||||
string({
|
||||
include: /\.html$/,
|
||||
}),
|
||||
replace({
|
||||
include: /\.js$/,
|
||||
preventAssignment: true,
|
||||
values: {
|
||||
'__HOTPOCKET_EXTENSION_ENV__': JSON.stringify(ENV),
|
||||
'__HOTPOCKET_EXTENSION_VERSION__': JSON.stringify(packageJSON.version),
|
||||
'__HOTPOCKET_EXTENSION_BASE_URL__': JSON.stringify(BASE_URL),
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const manifestJsonOutputPlugin = () => {
|
||||
return {
|
||||
name: 'manifest-json',
|
||||
renderChunk: async (code, chunk, outputOptions) => {
|
||||
let result = {...manifestCommon};
|
||||
|
||||
if (TARGET === 'safari') {
|
||||
result = {
|
||||
...result,
|
||||
...manifestSafari,
|
||||
};
|
||||
}
|
||||
|
||||
result.version = packageJSON.version;
|
||||
|
||||
return JSON.stringify(result, null, 2);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
let OUTPUT_PATH = `dist/${TARGET}`;
|
||||
if (TARGET === 'safari') {
|
||||
OUTPUT_PATH = '../apple/Shared (Extension)/Resources';
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
input: `src/content/${TARGET}.js`,
|
||||
output: {
|
||||
file: `${OUTPUT_PATH}/content-bundle.js`,
|
||||
format: 'iife',
|
||||
banner: BANNER,
|
||||
sourcemap: false,
|
||||
},
|
||||
plugins: [
|
||||
...PLUGINS,
|
||||
copy({
|
||||
targets: [
|
||||
{
|
||||
src: 'assets/_locales',
|
||||
dest: OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
src: 'assets/images',
|
||||
dest: OUTPUT_PATH,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
input: `src/background/${TARGET}.js`,
|
||||
output: {
|
||||
file: `${OUTPUT_PATH}/background-bundle.js`,
|
||||
format: 'iife',
|
||||
banner: BANNER,
|
||||
sourcemap: false,
|
||||
},
|
||||
plugins: PLUGINS,
|
||||
},
|
||||
{
|
||||
input: `src/manifest.js`,
|
||||
output: {
|
||||
file: `${OUTPUT_PATH}/manifest.json`,
|
||||
format: 'es',
|
||||
sourcemap: false,
|
||||
plugins: [
|
||||
manifestJsonOutputPlugin(),
|
||||
],
|
||||
},
|
||||
}
|
||||
];
|
||||
16
services/extension/setup.cfg
Normal file
@@ -0,0 +1,16 @@
|
||||
[flake8]
|
||||
extend-exclude =
|
||||
node_modules/**/*.py
|
||||
ignore = E131,W503,W504
|
||||
max-line-length = 119
|
||||
hang-closing = False
|
||||
|
||||
[isort]
|
||||
known_first_party=hotpocket_backend,hotpocket_backend_testing,hotpocket_common,hotpocket_soa,hotpocket_testing,hotpocket_workspace_tools
|
||||
multi_line_output=3
|
||||
include_trailing_comma=true
|
||||
force_sort_within_sections=true
|
||||
line_length=80
|
||||
use_parentheses=true
|
||||
combine_as_imports=true
|
||||
star_first=true
|
||||
300
services/extension/src/background/main.js
Normal file
@@ -0,0 +1,300 @@
|
||||
import HotPocketExtension from '../common';
|
||||
|
||||
const AUTH_URL = (new URL('/integrations/extension/authenticate/', HotPocketExtension.base_url)).toString();
|
||||
const POST_AUTH_URL = (new URL('/integrations/extension/post-authenticate/', HotPocketExtension.base_url)).toString();
|
||||
const RPC_URL = (new URL('/rpc/', HotPocketExtension.base_url)).toString();
|
||||
|
||||
const makeJSONRPCCall = (method, params) => {
|
||||
return {
|
||||
'jsonrpc': '2.0',
|
||||
'id': (new Date().toISOString()),
|
||||
'method': method,
|
||||
'params': params,
|
||||
};
|
||||
};
|
||||
|
||||
const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.executeJSONRPCCall()', url, call, accessToken,
|
||||
);
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
if (accessToken) {
|
||||
headers['Authorization'] = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
let result = null;
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
body: JSON.stringify(call),
|
||||
credentials: 'include',
|
||||
headers: headers,
|
||||
method: 'POST',
|
||||
},
|
||||
);
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.executeJSONRPCCall()', response,
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
error = {
|
||||
code: -32000,
|
||||
message: 'Fetch error',
|
||||
data: response,
|
||||
};
|
||||
} else {
|
||||
const callResult = await response.json();
|
||||
if (callResult.error) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.executeJSONRPCCall(): RPC error',
|
||||
callResult.error.code,
|
||||
callResult.error.message,
|
||||
callResult.error.data,
|
||||
);
|
||||
|
||||
error = callResult.error;
|
||||
} else {
|
||||
result = callResult.result;
|
||||
}
|
||||
}
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.executeJSONRPCCall(): Fetch error', exception,
|
||||
);
|
||||
error = {
|
||||
code: -32000,
|
||||
message: 'Fetch error',
|
||||
data: exception,
|
||||
};
|
||||
}
|
||||
|
||||
return [result, error];
|
||||
};
|
||||
|
||||
const doSave = async (accessToken, tab) => {
|
||||
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doSave():', result, error,
|
||||
);
|
||||
|
||||
if (error !== null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const doCreateAndStoreAccessToken = async (authKey) => {
|
||||
const accessTokenCall = makeJSONRPCCall(
|
||||
'accounts.access_tokens.create',
|
||||
[
|
||||
authKey,
|
||||
{
|
||||
platform: navigator.platform,
|
||||
version: HotPocketExtension.version,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const [accessToken, error] = await executeJSONRPCCall(
|
||||
RPC_URL, accessTokenCall, {accessToken: null},
|
||||
);
|
||||
|
||||
if (error === null) {
|
||||
await HotPocketExtension.api.storage.local.set({
|
||||
accessToken: accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
return [accessToken, error];
|
||||
};
|
||||
|
||||
const doHandleAuthFlow = (authTab) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const onTabsUpdated = (tabId, changeInfo, updatedTab) => {
|
||||
if (tabId === authTab.id) {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doHandleAuthFlow.onTabsUpdated()', updatedTab, changeInfo,
|
||||
);
|
||||
|
||||
const changedURL = changeInfo.url;
|
||||
if (changedURL && changedURL.startsWith(POST_AUTH_URL)) {
|
||||
const parsedChangedURL = new URL(changedURL);
|
||||
const authKey = parsedChangedURL.searchParams.get('auth_key');
|
||||
|
||||
doCreateAndStoreAccessToken(authKey).
|
||||
then((result) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'doHandleAuthFlow.onTabsUpdated.doGetAndStoreAccessToken.then()', result,
|
||||
);
|
||||
const [accessToken, error] = result;
|
||||
|
||||
if (error !== null) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(accessToken);
|
||||
}
|
||||
}).
|
||||
catch((error) => {
|
||||
reject(error);
|
||||
}).
|
||||
finally(() => {
|
||||
HotPocketExtension.api.tabs.onUpdated.removeListener(onTabsUpdated);
|
||||
HotPocketExtension.api.tabs.remove(authTab.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HotPocketExtension.api.tabs.onUpdated.addListener(onTabsUpdated);
|
||||
});
|
||||
};
|
||||
|
||||
const doCheckAuth = async (accessToken) => {
|
||||
if (accessToken === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const call = makeJSONRPCCall('accounts.auth.check');
|
||||
|
||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
|
||||
accessToken,
|
||||
});
|
||||
|
||||
if (error !== null) {
|
||||
if (error.data instanceof Error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error.data.status === 403) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
};
|
||||
|
||||
const doGetAccessToken = async () => {
|
||||
let storageResult = await HotPocketExtension.api.storage.local.get('accessToken');
|
||||
let accessToken = await doCheckAuth(
|
||||
storageResult.accessToken || null,
|
||||
);
|
||||
|
||||
if (accessToken === null) {
|
||||
const authTab = await HotPocketExtension.api.tabs.create({
|
||||
url: AUTH_URL,
|
||||
});
|
||||
|
||||
accessToken = await doHandleAuthFlow(authTab);
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
};
|
||||
|
||||
const doSendTabMessage = (tab, message) => {
|
||||
HotPocketExtension.api.tabs.sendMessage(tab.id, message).
|
||||
then((result) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doSendTabMessage(): message sent', message, result,
|
||||
);
|
||||
}).
|
||||
catch((error) => {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.doSendTabMessage(): could not send message', error,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onTabCreated = (tab) => {
|
||||
HotPocketExtension.LOGGER.debug('HotPocketExtension.onTabCreated()', tab);
|
||||
HotPocketExtension.api.action.enable(tab.id);
|
||||
};
|
||||
|
||||
const onBrowserActionClicked = async (tab) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.onBrowserActionClicked()', tab.url,
|
||||
);
|
||||
|
||||
if (!tab.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = false;
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
let accessToken = await doGetAccessToken();
|
||||
|
||||
result = await doSave(accessToken, tab);
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.onBrowserActionClicked()', result,
|
||||
);
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'Unhandled exception when handling action click',
|
||||
exception,
|
||||
);
|
||||
error = exception;
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: 'HotPocket:Extension:save',
|
||||
result: result,
|
||||
error: error,
|
||||
};
|
||||
|
||||
doSendTabMessage(tab, message);
|
||||
};
|
||||
|
||||
const onMessage = (message, sender, sendResponse) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.onMessage()', message, sender, sendResponse,
|
||||
);
|
||||
|
||||
let response = {ok: true};
|
||||
try {
|
||||
if (message.type === 'HotPocket:Extension:ping') {
|
||||
HotPocketExtension.LOGGER.debug(sender.tab.id);
|
||||
doSendTabMessage(sender.tab, {
|
||||
type: 'HotPocket:Extension:pong',
|
||||
});
|
||||
}
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.background.onMessage(): Unhandled exception when handling content message',
|
||||
message,
|
||||
exception,
|
||||
);
|
||||
response.ok = false;
|
||||
}
|
||||
|
||||
sendResponse(response);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default ({...configuration}) => {
|
||||
HotPocketExtension.configure(configuration, {
|
||||
background: true,
|
||||
});
|
||||
|
||||
HotPocketExtension.api.tabs.onCreated.addListener(onTabCreated);
|
||||
|
||||
HotPocketExtension.api.action.onClicked.addListener(onBrowserActionClicked);
|
||||
|
||||
HotPocketExtension.api.runtime.onMessage.addListener(onMessage);
|
||||
|
||||
HotPocketExtension.LOGGER.info(`HotPocket v${HotPocketExtension.version} by BTHLabs`);
|
||||
};
|
||||
6
services/extension/src/background/safari.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Safari',
|
||||
api: browser,
|
||||
});
|
||||
34
services/extension/src/common.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const ENV = __HOTPOCKET_EXTENSION_ENV__;
|
||||
const DEBUG = (ENV === 'development') ? true : false;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const HotPocketExtension = {
|
||||
platform: null,
|
||||
version: __HOTPOCKET_EXTENSION_VERSION__,
|
||||
debug: DEBUG,
|
||||
api: null,
|
||||
base_url: __HOTPOCKET_EXTENSION_BASE_URL__,
|
||||
LOGGER: {
|
||||
// eslint-disable-next-line no-console
|
||||
debug: (DEBUG === true) ? console.log : noop,
|
||||
error: console.error,
|
||||
info: console.info,
|
||||
warning: console.warn,
|
||||
},
|
||||
configure: ({platform, debug, api}, options) => {
|
||||
options = options || {};
|
||||
|
||||
HotPocketExtension.platform = platform;
|
||||
HotPocketExtension.debug = debug;
|
||||
HotPocketExtension.api = api;
|
||||
|
||||
const background = (options.background === true) ? true : false;
|
||||
|
||||
if (background === false && HotPocketExtension.debug === true) {
|
||||
window.HotPocketExtension = HotPocketExtension;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default HotPocketExtension;
|
||||
118
services/extension/src/content/main.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import HotPocketExtension from '../common';
|
||||
|
||||
import POPUP from './templates/popup.html';
|
||||
import POPUP_CONTENT_SUCCESS from './templates/popup_content_success.html';
|
||||
import POPUP_CONTENT_ERROR from './templates/popup_content_error.html';
|
||||
|
||||
class Popup {
|
||||
constructor () {
|
||||
this.container = null;
|
||||
this.timeout = null;
|
||||
}
|
||||
setCloseTimeout = () => {
|
||||
this.timeout = window.setTimeout(this.close, 5000);
|
||||
};
|
||||
clearCloseTimeout = () => {
|
||||
if (this.timeout !== null) {
|
||||
window.clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
};
|
||||
close = () => {
|
||||
this.clearCloseTimeout();
|
||||
|
||||
if (this.container !== null) {
|
||||
this.container.remove();
|
||||
this.container = null;
|
||||
}
|
||||
};
|
||||
show = (content) => {
|
||||
this.close();
|
||||
|
||||
this.container = document.createElement('div');
|
||||
this.container.id = 'hotpocket-extension-popup';
|
||||
this.container.hotPocketExtensionPopup = this;
|
||||
|
||||
const shadow = this.container.attachShadow({mode: 'open'});
|
||||
shadow.innerHTML = POPUP;
|
||||
|
||||
const body = shadow.querySelector('.hotpocket-extension-popup-body');
|
||||
body.innerHTML = content;
|
||||
|
||||
const i18nElements = shadow.querySelectorAll('[data-message]');
|
||||
for (let i18nElement of i18nElements) {
|
||||
i18nElement.innerHTML = HotPocketExtension.api.i18n.getMessage(
|
||||
i18nElement.dataset.message,
|
||||
);
|
||||
}
|
||||
|
||||
const closeElements = shadow.querySelectorAll('.hotpocket-extension-popup-close');
|
||||
for (const closeElement of closeElements) {
|
||||
closeElement.addEventListener('click', this.onCloseClick);
|
||||
}
|
||||
|
||||
document.body.appendChild(this.container);
|
||||
};
|
||||
onCloseClick = (event) => {
|
||||
this.close();
|
||||
};
|
||||
}
|
||||
|
||||
let currentPopup = null;
|
||||
|
||||
const doHandleSaveMessage = (message) => {
|
||||
if (currentPopup !== null) {
|
||||
currentPopup.close();
|
||||
}
|
||||
|
||||
currentPopup = new Popup();
|
||||
currentPopup.show(
|
||||
(message.result === true) ? POPUP_CONTENT_SUCCESS : POPUP_CONTENT_ERROR,
|
||||
);
|
||||
};
|
||||
|
||||
const doSendMessage = (message) => {
|
||||
HotPocketExtension.api.runtime.sendMessage(message).
|
||||
then((result) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doSendMessage(): message sent', message, result,
|
||||
);
|
||||
}).
|
||||
catch((error) => {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.doSendMessage(): could not send message', error,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default ({...configuration}) => {
|
||||
HotPocketExtension.configure(configuration);
|
||||
|
||||
HotPocketExtension.api.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
HotPocketExtension.LOGGER.debug('HotPocketExtension.content.onMessage()', message, sender, sendResponse);
|
||||
|
||||
let response = {ok: true};
|
||||
try {
|
||||
if (message.type === 'HotPocket:Extension:save') {
|
||||
doHandleSaveMessage(message);
|
||||
}
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.onMessage(): Unhandled exception when handling service worker message',
|
||||
message,
|
||||
exception,
|
||||
);
|
||||
response.ok = false;
|
||||
}
|
||||
|
||||
sendResponse(response);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
doSendMessage({
|
||||
type: 'HotPocket:Extension:ping',
|
||||
});
|
||||
|
||||
HotPocketExtension.LOGGER.info(`HotPocket v${HotPocketExtension.version} by BTHLabs`);
|
||||
};
|
||||
6
services/extension/src/content/safari.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Safari',
|
||||
api: window.browser,
|
||||
});
|
||||
64
services/extension/src/content/templates/popup.html
Normal file
@@ -0,0 +1,64 @@
|
||||
<style type="text/css">
|
||||
:host {
|
||||
all: initial;
|
||||
font-family: sans-serif !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 1.5 !important;
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 999999;
|
||||
}
|
||||
.hotpocket-extension-popup {
|
||||
background: #212529;
|
||||
border: 1px solid #495057;
|
||||
border-radius: 0.375rem;
|
||||
color: white;
|
||||
width: 300px;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-close {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-header {
|
||||
background: rgba(222, 226, 230, 0.3);
|
||||
border-bottom: 1px solid #495057;
|
||||
padding: 0.5rem 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-header strong {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-header .hotpocket-extension-popup-close {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
text-align: center;
|
||||
top: 0.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-body > * {
|
||||
margin: 0px;
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-message-success {
|
||||
color: rgba(25, 135, 84, 1);
|
||||
}
|
||||
.hotpocket-extension-popup .hotpocket-extension-popup-message-error {
|
||||
color: rgba(220, 53, 69, 1);
|
||||
}
|
||||
</style>
|
||||
<div class="hotpocket-extension-popup">
|
||||
<div class="hotpocket-extension-popup-header">
|
||||
<strong>HotPocket by BTHLabs</strong>
|
||||
<a class="hotpocket-extension-popup-close">×</a>
|
||||
</div>
|
||||
<div class="hotpocket-extension-popup-body">
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<p class="hotpocket-extension-popup-message hotpocket-extension-popup-message-error">
|
||||
<strong data-message="content_popup_content_error_title"></strong>
|
||||
<br>
|
||||
<span data-message="content_popup_content_error_message"></span>
|
||||
</p>
|
||||
@@ -0,0 +1,5 @@
|
||||
<p class="hotpocket-extension-popup-message hotpocket-extension-popup-message-success">
|
||||
<strong data-message="content_popup_content_success_title"></strong>
|
||||
<br>
|
||||
<span data-message="content_popup_content_success_message"></span>
|
||||
</p>
|
||||
1
services/extension/src/manifest.js
Normal file
@@ -0,0 +1 @@
|
||||
export default 'This file intentionally left blank';
|
||||
34
services/extension/src/manifest/common.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_extension_name__",
|
||||
"description": "__MSG_extension_description__",
|
||||
"version": "1.0",
|
||||
"icons": {
|
||||
"48": "images/icon-48.png",
|
||||
"64": "images/icon-64.png",
|
||||
"96": "images/icon-96.png",
|
||||
"128": "images/icon-128.png",
|
||||
"256": "images/icon-256.png",
|
||||
"512": "images/icon-512.png"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"js": [
|
||||
"content-bundle.js"
|
||||
],
|
||||
"matches": [
|
||||
"<all_urls>"
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"default_title": "__MSG_extension_name__",
|
||||
"default_icon": "images/toolbar-icon.svg"
|
||||
},
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab",
|
||||
"tabs"
|
||||
]
|
||||
}
|
||||
9
services/extension/src/manifest/safari.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"description": "__MSG_extension_description_Safari__",
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background-bundle.js"
|
||||
],
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
112
services/extension/tasks.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from invoke import task
|
||||
from invoke.exceptions import UnexpectedExit
|
||||
|
||||
from hotpocket_workspace_tools import get_workspace_mode
|
||||
|
||||
WORKSPACE_MODE = get_workspace_mode()
|
||||
|
||||
|
||||
@task
|
||||
def clean(ctx):
|
||||
ctx.run('rm -rf dist/')
|
||||
|
||||
|
||||
@task
|
||||
def test(ctx):
|
||||
# ctx.run('pytest -v --disable-warnings')
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def flake8(ctx):
|
||||
ctx.run('flake8')
|
||||
|
||||
|
||||
@task
|
||||
def isort(ctx, check=False, diff=False):
|
||||
command_parts = [
|
||||
'isort',
|
||||
]
|
||||
|
||||
if check is True:
|
||||
command_parts.append('--check')
|
||||
|
||||
if diff is True:
|
||||
command_parts.append('--diff')
|
||||
|
||||
command_parts.append('.')
|
||||
|
||||
ctx.run(' '.join(command_parts))
|
||||
|
||||
|
||||
@task
|
||||
def eslint(ctx):
|
||||
ctx.run('yarn run eslint')
|
||||
|
||||
|
||||
@task
|
||||
def lint(ctx):
|
||||
ihazsuccess = True
|
||||
|
||||
try:
|
||||
flake8(ctx)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
try:
|
||||
isort(ctx, check=True)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
try:
|
||||
eslint(ctx)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
if ihazsuccess is False:
|
||||
raise RuntimeError('FIAL')
|
||||
|
||||
|
||||
@task
|
||||
def typecheck(ctx):
|
||||
ctx.run('mypy .')
|
||||
|
||||
|
||||
@task
|
||||
def django_shell(ctx):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def ci(ctx):
|
||||
ihazsuccess = True
|
||||
|
||||
ci_tasks = [test, lint, typecheck]
|
||||
for ci_task in ci_tasks:
|
||||
try:
|
||||
ci_task(ctx)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
if ihazsuccess is False:
|
||||
raise RuntimeError('FIAL')
|
||||
|
||||
|
||||
@task
|
||||
def setup(ctx):
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def start_web(ctx):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def start_safari(ctx):
|
||||
ctx.run('yarn watch:safari')
|
||||