From 1fc5d7f4432815a75ed19b2fede849673d4f2615 Mon Sep 17 00:00:00 2001 From: aorlov Date: Wed, 17 Dec 2025 17:54:53 +0100 Subject: [PATCH 01/14] feat: enhance demo with signature functionality and API integration - Added a custom signature component using `signature_pad` for drawing signatures. - Updated the demo to include document field definitions and improved event logging. - Integrated API calls for document annotation and signing, utilizing the provided API key. - Enhanced the Vite configuration for better development experience and added a README for setup instructions. - Included new dependencies in `package.json` and updated the lock file accordingly. --- .github/workflows/deploy-demo.yml | 2 + demo/README.md | 26 ++ demo/package.json | 1 + demo/pnpm-lock.yaml | 8 + demo/src/App.tsx | 620 +++++++++++++++++++++++++----- demo/src/vite-env.d.ts | 1 + demo/vite.config.ts | 19 +- src/index.tsx | 38 +- src/types.ts | 1 + 9 files changed, 613 insertions(+), 103 deletions(-) create mode 100644 demo/README.md create mode 100644 demo/src/vite-env.d.ts diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 3311a37..536f11a 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -42,6 +42,8 @@ jobs: cache: "pnpm" - name: Install and Build + env: + VITE_SUPERDOC_SERVICES_API_KEY: ${{ secrets.VITE_SUPERDOC_SERVICES_API_KEY }} run: | pnpm install rm -rf dist diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..e0fe744 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,26 @@ +# Demo + +## Setup + +1. Build the main package first (from root): + ```bash + pnpm build + ``` + +2. Install demo dependencies: + ```bash + cd demo + pnpm install + ``` + +3. Create `.env` file with your API key: + ``` + VITE_SUPERDOC_SERVICES_API_KEY=your_key_here + ``` + +4. Run the demo: + ```bash + pnpm run dev + ``` + + diff --git a/demo/package.json b/demo/package.json index a09ce3c..a244d2c 100644 --- a/demo/package.json +++ b/demo/package.json @@ -11,6 +11,7 @@ "@superdoc-dev/esign": "link:../.", "react": "^18.3.1", "react-dom": "^18.3.1", + "signature_pad": "^5.1.1", "superdoc": "^0.35.3" }, "devDependencies": { diff --git a/demo/pnpm-lock.yaml b/demo/pnpm-lock.yaml index 4caf216..08dd888 100644 --- a/demo/pnpm-lock.yaml +++ b/demo/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + signature_pad: + specifier: ^5.1.1 + version: 5.1.3 superdoc: specifier: ^0.35.3 version: 0.35.3(@hocuspocus/provider@2.15.3(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))(canvas@2.11.2)(pdfjs-dist@4.6.82)(typescript@5.9.3)(y-prosemirror@1.3.7(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.41.2)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))(yjs@13.6.19) @@ -1218,6 +1221,9 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signature_pad@5.1.3: + resolution: {integrity: sha512-zyxW5vuJVnQdGcU+kAj9FYl7WaAunY3kA5S7mPg0xJiujL9+sPAWfSQHS5tXaJXDUa4FuZeKhfdCDQ6K3wfkpQ==} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -2681,6 +2687,8 @@ snapshots: signal-exit@3.0.7: optional: true + signature_pad@5.1.3: {} + simple-concat@1.0.1: optional: true diff --git a/demo/src/App.tsx b/demo/src/App.tsx index cd6d844..1162582 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -1,14 +1,214 @@ -import React, { useState } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import SuperDocESign from '@superdoc-dev/esign'; -import type { SubmitData, SigningState, FieldChange, DownloadData } from '@superdoc-dev/esign'; +import type { + SubmitData, + SigningState, + FieldChange, + DownloadData, + SuperDocESignHandle, + FieldComponentProps, +} from '@superdoc-dev/esign'; +import SignaturePad from 'signature_pad'; import 'superdoc/style.css'; import './App.css'; +// Document field definitions with labels +const documentFieldsConfig = [ + { + id: '123456', + label: 'Date', + defaultValue: new Date().toLocaleDateString(), + readOnly: true, + type: 'text' as const, + }, + { + id: '234567', + label: 'Full Name', + defaultValue: 'John Doe', + readOnly: false, + type: 'text' as const, + }, + { + id: '345678', + label: 'Company', + defaultValue: 'SuperDoc', + readOnly: false, + type: 'text' as const, + }, + { id: '456789', label: 'Plan', defaultValue: 'Premium', readOnly: false, type: 'text' as const }, + { id: '567890', label: 'State', defaultValue: 'CA', readOnly: false, type: 'text' as const }, + { + id: '678901', + label: 'Address', + defaultValue: '123 Main St, Anytown, USA', + readOnly: false, + type: 'text' as const, + }, +]; + +// Custom signature component with type/draw modes using signature_pad +const CustomSignature: React.FC = ({ value, onChange, isDisabled, label }) => { + const [mode, setMode] = useState<'type' | 'draw'>('type'); + const canvasRef = useRef(null); + const signaturePadRef = useRef(null); + + const switchMode = (newMode: 'type' | 'draw') => { + setMode(newMode); + onChange(''); + if (newMode === 'draw' && signaturePadRef.current) { + signaturePadRef.current.clear(); + } + }; + + const clearCanvas = () => { + if (signaturePadRef.current) { + signaturePadRef.current.clear(); + onChange(''); + } + }; + + useEffect(() => { + if (!canvasRef.current || mode !== 'draw') return; + + signaturePadRef.current = new SignaturePad(canvasRef.current, { + backgroundColor: 'rgb(255, 255, 255)', + penColor: 'rgb(0, 0, 0)', + }); + + if (isDisabled) { + signaturePadRef.current.off(); + } + + signaturePadRef.current.addEventListener('endStroke', () => { + if (signaturePadRef.current) { + onChange(signaturePadRef.current.toDataURL()); + } + }); + + return () => { + if (signaturePadRef.current) { + signaturePadRef.current.off(); + } + }; + }, [mode, isDisabled, onChange]); + + return ( +
+ {label && ( + + )} +
+ + +
+ {mode === 'type' ? ( + onChange(e.target.value)} + disabled={isDisabled} + placeholder="Type your full name" + style={{ + fontFamily: 'cursive', + fontSize: '20px', + padding: '14px', + border: '1px solid #d1d5db', + borderRadius: '8px', + outline: 'none', + transition: 'border-color 0.2s', + }} + onFocus={(e) => (e.target.style.borderColor = '#14b8a6')} + onBlur={(e) => (e.target.style.borderColor = '#d1d5db')} + /> + ) : ( +
+ + +
+ )} +
+ ); +}; + export function App() { const [submitted, setSubmitted] = useState(false); const [submitData, setSubmitData] = useState(null); const [events, setEvents] = useState([]); + // Stable eventId that persists across renders + const [eventId] = useState(() => `demo-${Date.now()}`); + + // Ref to the esign component + const esignRef = useRef(null); + + // State for document field values + const [documentFields, setDocumentFields] = useState>(() => + Object.fromEntries(documentFieldsConfig.map((f) => [f.id, f.defaultValue])), + ); + + const updateDocumentField = (id: string, value: string) => { + setDocumentFields((prev) => ({ ...prev, [id]: value })); + esignRef.current?.updateFieldInDocument({ id, value }); + }; + const log = (msg: string) => { const time = new Date().toLocaleTimeString(); console.log(`[${time}] ${msg}`); @@ -16,27 +216,209 @@ export function App() { }; const handleSubmit = async (data: SubmitData) => { - log('✓ Agreement signed'); + log('⏳ Signing document...'); console.log('Submit data:', data); - setSubmitted(true); - setSubmitData(data); + + const headers: Record = { + 'Content-Type': 'application/json', + }; + const bearerToken = import.meta.env.VITE_SUPERDOC_SERVICES_API_KEY; + if (bearerToken) { + headers.Authorization = `Bearer ${bearerToken}`; + } + + try { + // Step 1: Prepare fields for annotation + const fields = [...data.documentFields, ...data.signerFields] + .filter( + (field) => + field.id !== 'consent_agreement' && field.id !== 'terms' && field.id !== 'email', + ) + .map((field) => { + // Signature field: add options with IP label + if (field.id === '789012') { + const isDrawnSignature = + typeof field.value === 'string' && field.value?.startsWith('data:image/'); + return { + id: field.id, + value: field.value, + type: isDrawnSignature ? 'signature' : 'text', + options: { + bottomLabel: { + text: `ip: ${field.value}`, + color: '#666', + }, + }, + }; + } + // Document fields + const docField = documentFieldsConfig.find((f) => f.id === field.id); + return { + id: field.id, + value: field.value, + type: docField?.type || 'text', + }; + }); + + // Step 2: Annotate the document + log('⏳ Annotating document...'); + const annotateResponse = await fetch('/v1/annotate', { + method: 'POST', + headers, + body: JSON.stringify({ + document: { + url: 'https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement_updated.docx', + }, + fields, + }), + }); + + if (!annotateResponse.ok) { + const error = await annotateResponse.text(); + throw new Error(error || 'Failed to annotate document'); + } + + const annotateResult = await annotateResponse.json(); + const annotatedBase64 = annotateResult.document.base64; + + // Step 3: Sign the annotated document + log('⏳ Applying digital signature...'); + const signPayload = { + eventId: data.eventId, + document: { + base64: annotatedBase64, + }, + auditTrail: data.auditTrail, + signer: { + name: documentFields['234567'] || 'Signer', // Full Name field + email: 'andrii@superdoc.dev', + ip: '127.0.0.1', + userAgent: navigator.userAgent, + }, + certificate: { + enable: true, + }, + metadata: { + company: documentFields['345678'], + plan: documentFields['456789'], + }, + }; + + const signResponse = await fetch('/v1/sign', { + method: 'POST', + headers, + body: JSON.stringify(signPayload), + }); + + if (!signResponse.ok) { + const error = await signResponse.text(); + throw new Error(error || 'Failed to sign document'); + } + + const signResult = await signResponse.json(); + const { base64: signedBase64 } = signResult.document; + + // Step 4: Download the signed PDF + const byteCharacters = atob(signedBase64); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + const blob = new Blob([byteArray], { type: 'application/pdf' }); + + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `signed_agreement_${data.eventId}.pdf`; + a.click(); + URL.revokeObjectURL(url); + + log('✓ Document signed and downloaded!'); + setSubmitted(true); + setSubmitData(data); + } catch (error) { + console.error('Error signing document:', error); + log(`✗ Signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`); + } }; const handleDownload = async (data: DownloadData) => { - // Send to your backend for DOCX to PDF conversion - const response = await fetch('/v1/convert-to-pdf', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data), - }); + if (typeof data.documentSource !== 'string') { + log('Download requires a document URL.'); + return; + } + + const fields = [...data.fields.document, ...data.fields.signer] + .filter((field) => field.id !== 'consent_agreement' && field.id !== '406948812') + .map((field) => { + // Signature field: determine type from value (data:image = signature, else text) + if (field.id === '789012') { + const isDrawnSignature = + typeof field.value === 'string' && field.value.startsWith('data:image/'); + return { + id: field.id, + value: field.value, + type: isDrawnSignature ? 'signature' : 'text', + }; + } + // Document fields have type from config + const docField = documentFieldsConfig.find((f) => f.id === field.id); + return { + id: field.id, + value: field.value, + type: docField?.type || 'text', + }; + }); + + const headers: Record = { + 'Content-Type': 'application/json', + }; + const bearerToken = import.meta.env.VITE_SUPERDOC_SERVICES_API_KEY; + if (bearerToken) { + headers.Authorization = `Bearer ${bearerToken}`; + } + + console.log('Annotating document with fields:', fields); - const blob = await response.blob(); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = data.fileName; - a.click(); - URL.revokeObjectURL(url); + try { + const response = await fetch('/v1/annotate?to=pdf', { + method: 'POST', + headers, + body: JSON.stringify({ + document: { url: data.documentSource }, + fields, + }), + }); + + if (!response.ok) { + const error = await response.text(); + throw new Error(error || 'Failed to annotate document'); + } + + const result = await response.json(); + const { base64, contentType } = result.document; + + // Convert base64 to blob + const byteCharacters = atob(base64); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + const blob = new Blob([byteArray], { type: contentType }); + + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = data.fileName || 'document.pdf'; + a.click(); + URL.revokeObjectURL(url); + log('✓ Downloaded PDF'); + } catch (error) { + console.error('Error processing document:', error); + log('Download failed'); + } }; const handleStateChange = (state: SigningState) => { @@ -50,12 +432,16 @@ export function App() { }; const handleFieldChange = (field: FieldChange) => { - log(`Field "${field.id}": ${field.value}`); + const displayValue = + typeof field.value === 'string' && field.value.startsWith('data:image/') + ? `${field.value.slice(0, 30)}... (base64 image)` + : field.value; + log(`Field "${field.id}": ${displayValue}`); console.log('Field change:', field); }; return ( -
+

Use the document toolbar to download the current agreement at any time.

- - {/* Event Log */} - {events.length > 0 && ( +
+ {/* Main content */} +
+ ({ + id: f.id, + value: documentFields[f.id], + type: f.type, + })), + signer: [ + { + id: '789012', + type: 'signature', + label: 'Your Signature', + validation: { required: true }, + component: CustomSignature, + }, + { + id: 'terms', + type: 'checkbox', + label: 'I accept the terms and conditions', + validation: { required: true }, + }, + { + id: 'email', + type: 'checkbox', + label: 'Send me a copy of the agreement', + validation: { required: false }, + }, + ], + }} + download={{ label: 'Download PDF' }} + onSubmit={handleSubmit} + onDownload={handleDownload} + onStateChange={handleStateChange} + onFieldChange={handleFieldChange} + documentHeight="500px" + /> + + {/* Event Log */} + {events.length > 0 && ( +
+
+ EVENT LOG +
+ {events.map((evt, i) => ( +
+ {evt} +
+ ))} +
+ )} +
+ + {/* Right Sidebar - Document Fields */}
-
- EVENT LOG + Document Fields +

+
+ {documentFieldsConfig.map((field) => ( +
+ + updateDocumentField(field.id, e.target.value)} + readOnly={field.readOnly} + style={{ + width: '100%', + padding: '8px 10px', + fontSize: '14px', + border: '1px solid #d1d5db', + borderRadius: '6px', + background: field.readOnly ? '#f3f4f6' : 'white', + color: field.readOnly ? '#6b7280' : '#111827', + cursor: field.readOnly ? 'not-allowed' : 'text', + boxSizing: 'border-box', + }} + /> +
+ ))}
- {events.map((evt, i) => ( -
- {evt} -
- ))}
- )} +
)} diff --git a/demo/src/vite-env.d.ts b/demo/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/demo/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 597a3ac..5545f88 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -1,10 +1,25 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +import path from 'path'; -export default defineConfig({ +export default defineConfig(({ mode }) => ({ plugins: [react()], base: '/', resolve: { dedupe: ['react', 'react-dom'], + ...(mode === 'development' && { + alias: { + '@superdoc-dev/esign': path.resolve(__dirname, '../src/index.tsx'), + }, + }), }, -}); + server: { + proxy: { + '/v1': { + target: 'https://api.superdoc.dev', + changeOrigin: true, + secure: true, + }, + }, + }, +})); diff --git a/src/index.tsx b/src/index.tsx index b15e8f9..c9971c0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -404,22 +404,34 @@ const SuperDocESign = forwardRef ({ - getState: () => ({ + useImperativeHandle( + ref, + () => ({ + getState: () => ({ + scrolled, + fields: fieldValues, + isValid, + isSubmitting, + }), + getAuditTrail: () => auditTrailRef.current, + reset: () => { + setScrolled(!document.validation?.scroll?.required); + setFieldValues(new Map()); + setIsValid(false); + auditTrailRef.current = []; + setAuditTrail([]); + }, + updateFieldInDocument, + }), + [ scrolled, - fields: fieldValues, + fieldValues, isValid, isSubmitting, - }), - getAuditTrail: () => auditTrailRef.current, - reset: () => { - setScrolled(!document.validation?.scroll?.required); - setFieldValues(new Map()); - setIsValid(false); - auditTrailRef.current = []; - setAuditTrail([]); - }, - })); + document.validation?.scroll?.required, + updateFieldInDocument, + ], + ); return (
diff --git a/src/types.ts b/src/types.ts index a20a00b..0b8b0bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -100,6 +100,7 @@ export interface SuperDocESignHandle { getState: () => SigningState; getAuditTrail: () => AuditEvent[]; reset: () => void; + updateFieldInDocument: (field: FieldUpdate) => void; } export interface DownloadData { From 7ca80bdd3b5d44a1404d6485433c0bf354cebfc3 Mon Sep 17 00:00:00 2001 From: aorlov Date: Wed, 17 Dec 2025 19:06:20 +0100 Subject: [PATCH 02/14] fix: fixed payload --- demo/src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/src/App.tsx b/demo/src/App.tsx index 1162582..534cd23 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -245,7 +245,7 @@ export function App() { type: isDrawnSignature ? 'signature' : 'text', options: { bottomLabel: { - text: `ip: ${field.value}`, + text: 'ip: 127.0.0.1', color: '#666', }, }, From 0ba470732f04f50786a7a13cd3e104839115e523 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 18:21:08 +0100 Subject: [PATCH 03/14] feat: added proxy-server for the demo and basic workflow to test deployment - Changed the proxy target in Vite config to point to localhost for local testing. - Removed the custom signature component and replaced it with a simpler implementation. - Introduced a helper function to download response blobs as files. - Refactored API calls in the App component to streamline document signing and downloading processes. --- .github/workflows/deploy-proxy.yml | 72 +++ demo/server/.dockerignore | 6 + demo/server/.env.example | 3 + demo/server/Dockerfile | 13 + demo/server/package-lock.json | 861 +++++++++++++++++++++++++++++ demo/server/package.json | 14 + demo/server/server.js | 197 +++++++ demo/src/App.tsx | 357 ++---------- demo/src/CustomSignature.tsx | 146 +++++ demo/vite.config.ts | 4 +- 10 files changed, 1362 insertions(+), 311 deletions(-) create mode 100644 .github/workflows/deploy-proxy.yml create mode 100644 demo/server/.dockerignore create mode 100644 demo/server/.env.example create mode 100644 demo/server/Dockerfile create mode 100644 demo/server/package-lock.json create mode 100644 demo/server/package.json create mode 100644 demo/server/server.js create mode 100644 demo/src/CustomSignature.tsx diff --git a/.github/workflows/deploy-proxy.yml b/.github/workflows/deploy-proxy.yml new file mode 100644 index 0000000..38e2b3e --- /dev/null +++ b/.github/workflows/deploy-proxy.yml @@ -0,0 +1,72 @@ +name: Deploy Proxy Server to GCP + +on: + workflow_dispatch: + inputs: + deploy_env: + description: 'Environment label (optional)' + required: false + +env: + PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + REGION: ${{ secrets.GCP_REGION }} + SERVICE_NAME: ${{ secrets.GCP_SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ secrets.SUPERDOC_SERVICES_BASE_URL }} + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Auth to Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_KEY }} + + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Build and push container with Cloud Build + run: | + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:${GITHUB_SHA}" + + echo "REGION=${REGION}" >> $GITHUB_ENV + echo "SERVICE_NAME=${SERVICE_NAME}" >> $GITHUB_ENV + echo "IMAGE=${IMAGE}" >> $GITHUB_ENV + + gcloud builds submit demo/server --tag "${IMAGE}" + env: + PROJECT_ID: ${{ env.PROJECT_ID }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} + + - name: Deploy container to Cloud Run + run: | + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="${IMAGE}" + SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL:-https://api.superdoc.dev}" + + gcloud run deploy "${SERVICE_NAME}" \ + --image "${IMAGE}" \ + --region "${REGION}" \ + --platform managed \ + --allow-unauthenticated \ + --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" + env: + IMAGE: ${{ env.IMAGE }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ env.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ env.SUPERDOC_SERVICES_BASE_URL }} diff --git a/demo/server/.dockerignore b/demo/server/.dockerignore new file mode 100644 index 0000000..dbb23e6 --- /dev/null +++ b/demo/server/.dockerignore @@ -0,0 +1,6 @@ +node_modules +npm-debug.log +.env +.env.* +Dockerfile +.dockerignore diff --git a/demo/server/.env.example b/demo/server/.env.example new file mode 100644 index 0000000..a65e3b7 --- /dev/null +++ b/demo/server/.env.example @@ -0,0 +1,3 @@ +PORT=3003 +SUPERDOC_SERVICES_API_KEY=replace-with-your-superdoc-api-key +SUPERDOC_SERVICES_BASE_URL=https://api.superdoc.dev diff --git a/demo/server/Dockerfile b/demo/server/Dockerfile new file mode 100644 index 0000000..4d53525 --- /dev/null +++ b/demo/server/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install --omit=dev + +COPY . . + +# Cloud Run/Functions set PORT; default to 8080 +ENV PORT=8080 + +CMD ["npm", "start"] diff --git a/demo/server/package-lock.json b/demo/server/package-lock.json new file mode 100644 index 0000000..c74f124 --- /dev/null +++ b/demo/server/package-lock.json @@ -0,0 +1,861 @@ +{ + "name": "esign-proxy-server", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "esign-proxy-server", + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/demo/server/package.json b/demo/server/package.json new file mode 100644 index 0000000..dbd0f13 --- /dev/null +++ b/demo/server/package.json @@ -0,0 +1,14 @@ +{ + "name": "esign-proxy-server", + "private": true, + "type": "module", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2" + } +} diff --git a/demo/server/server.js b/demo/server/server.js new file mode 100644 index 0000000..5f76664 --- /dev/null +++ b/demo/server/server.js @@ -0,0 +1,197 @@ +import express from 'express'; +import cors from 'cors'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const app = express(); +const PORT = process.env.PORT || 3001; +const SUPERDOC_SERVICES_API_KEY = process.env.SUPERDOC_SERVICES_API_KEY; +const SUPERDOC_SERVICES_BASE_URL = process.env.SUPERDOC_SERVICES_BASE_URL || 'https://api.superdoc.dev'; +const CONSENT_FIELD_IDS = new Set(['consent_agreement', 'terms', 'email', '406948812']); +const SIGNATURE_FIELD_ID = '789012'; + +app.use(cors()); +app.use(express.json({ limit: '50mb' })); + +app.get('/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +const normalizeFields = (fieldsPayload = {}) => { + const documentFields = Array.isArray(fieldsPayload.document) ? fieldsPayload.document : []; + const signerFields = Array.isArray(fieldsPayload.signer) ? fieldsPayload.signer : []; + + return [...documentFields, ...signerFields] + .filter((field) => field?.id && !CONSENT_FIELD_IDS.has(field.id)) + .map((field) => { + const isSignatureField = field.id === SIGNATURE_FIELD_ID; + const value = field.value ?? ''; + const isDrawnSignature = typeof value === 'string' && value.startsWith('data:image/'); + const type = isSignatureField && isDrawnSignature ? 'signature' : 'text'; + + const normalized = { id: field.id, value, type }; + if (type === 'signature') { + normalized.options = { + bottomLabel: { text: 'ip: 127.0.0.1', color: '#666' }, + }; + } + return normalized; + }); +}; + +const annotateDocument = async ({ documentUrl, fields }) => { + const response = await fetch(`${SUPERDOC_SERVICES_BASE_URL}/v1/annotate?to=pdf`, { + method: 'POST', + headers: { + Authorization: `Bearer ${SUPERDOC_SERVICES_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + document: { url: documentUrl }, + fields: fields || [], + }), + }); + + if (!response.ok) { + const error = await response.text(); + throw new Error(error || 'Failed to annotate document'); + } + + const data = await response.json(); + return { + base64: data?.document?.base64, + contentType: data?.document?.contentType || 'application/pdf', + }; +}; + +const sendPdfBuffer = (res, base64, fileName, contentType = 'application/pdf') => { + const buffer = Buffer.from(base64, 'base64'); + res.setHeader('Content-Type', contentType); + res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); + res.send(buffer); +}; + +app.post('/v1/download', async (req, res) => { + try { + const { document, fields = {}, fileName = 'document.pdf' } = req.body || {}; + + if (!SUPERDOC_SERVICES_API_KEY) { + return res.status(500).json({ error: 'Missing SUPERDOC_SERVICES_API_KEY on the server' }); + } + + if (!document?.url) { + return res.status(400).json({ error: 'document.url is required' }); + } + + const annotatedFields = normalizeFields(fields); + + const { base64, contentType } = await annotateDocument({ + documentUrl: document.url, + fields: annotatedFields, + }); + + if (!base64) { + return res.status(502).json({ + error: 'Annotate response missing PDF content', + }); + } + + sendPdfBuffer(res, base64, fileName || 'document.pdf', contentType); + } catch (error) { + console.error('Error processing download:', error); + res.status(500).json({ + error: 'Internal server error', + message: error.message, + }); + } +}); + +app.post('/v1/sign', async (req, res) => { + try { + const { + document, + documentFields = [], + signerFields = [], + auditTrail = [], + eventId, + signer, + certificate, + metadata, + fileName = 'signed-document.pdf', + } = req.body || {}; + + if (!SUPERDOC_SERVICES_API_KEY) { + return res.status(500).json({ error: 'Missing SUPERDOC_SERVICES_API_KEY on the server' }); + } + + if (!document?.url) { + return res.status(400).json({ error: 'document.url is required' }); + } + + const annotatedFields = normalizeFields({ + document: documentFields, + signer: signerFields, + }); + + const { base64: annotatedBase64 } = await annotateDocument({ + documentUrl: document.url, + fields: annotatedFields, + }); + + if (!annotatedBase64) { + return res.status(502).json({ + error: 'Annotate response missing document content', + }); + } + + const signPayload = { + eventId, + document: { base64: annotatedBase64 }, + auditTrail, + signer, + certificate, + metadata, + }; + + const signResponse = await fetch(`${SUPERDOC_SERVICES_BASE_URL}/v1/sign`, { + method: 'POST', + headers: { + Authorization: `Bearer ${SUPERDOC_SERVICES_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(signPayload), + }); + + if (!signResponse.ok) { + const error = await signResponse.text(); + console.error('SuperDoc sign error:', error); + return res.status(signResponse.status).json({ + error: 'Failed to sign document', + details: error, + }); + } + + const signData = await signResponse.json(); + const signedBase64 = signData?.document?.base64; + const contentType = signData?.document?.contentType || 'application/pdf'; + + if (!signedBase64) { + return res.status(502).json({ + error: 'Sign response missing document content', + }); + } + + sendPdfBuffer(res, signedBase64, fileName, contentType); + } catch (error) { + console.error('Error signing document:', error); + res.status(500).json({ + error: 'Internal server error', + message: error.message, + }); + } +}); + +app.listen(PORT, () => { + console.log(`Proxy server running on http://localhost:${PORT}`); +}); diff --git a/demo/src/App.tsx b/demo/src/App.tsx index 534cd23..df00e7f 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef } from 'react'; import SuperDocESign from '@superdoc-dev/esign'; import type { SubmitData, @@ -6,12 +6,25 @@ import type { FieldChange, DownloadData, SuperDocESignHandle, - FieldComponentProps, } from '@superdoc-dev/esign'; -import SignaturePad from 'signature_pad'; +import CustomSignature from './CustomSignature'; import 'superdoc/style.css'; import './App.css'; +const documentSource = + 'https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement_updated.docx'; + +// Helper to download a response blob as a file +const downloadBlob = async (response: Response, fileName: string) => { + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + a.click(); + URL.revokeObjectURL(url); +}; + // Document field definitions with labels const documentFieldsConfig = [ { @@ -46,148 +59,6 @@ const documentFieldsConfig = [ }, ]; -// Custom signature component with type/draw modes using signature_pad -const CustomSignature: React.FC = ({ value, onChange, isDisabled, label }) => { - const [mode, setMode] = useState<'type' | 'draw'>('type'); - const canvasRef = useRef(null); - const signaturePadRef = useRef(null); - - const switchMode = (newMode: 'type' | 'draw') => { - setMode(newMode); - onChange(''); - if (newMode === 'draw' && signaturePadRef.current) { - signaturePadRef.current.clear(); - } - }; - - const clearCanvas = () => { - if (signaturePadRef.current) { - signaturePadRef.current.clear(); - onChange(''); - } - }; - - useEffect(() => { - if (!canvasRef.current || mode !== 'draw') return; - - signaturePadRef.current = new SignaturePad(canvasRef.current, { - backgroundColor: 'rgb(255, 255, 255)', - penColor: 'rgb(0, 0, 0)', - }); - - if (isDisabled) { - signaturePadRef.current.off(); - } - - signaturePadRef.current.addEventListener('endStroke', () => { - if (signaturePadRef.current) { - onChange(signaturePadRef.current.toDataURL()); - } - }); - - return () => { - if (signaturePadRef.current) { - signaturePadRef.current.off(); - } - }; - }, [mode, isDisabled, onChange]); - - return ( -
- {label && ( - - )} -
- - -
- {mode === 'type' ? ( - onChange(e.target.value)} - disabled={isDisabled} - placeholder="Type your full name" - style={{ - fontFamily: 'cursive', - fontSize: '20px', - padding: '14px', - border: '1px solid #d1d5db', - borderRadius: '8px', - outline: 'none', - transition: 'border-color 0.2s', - }} - onFocus={(e) => (e.target.style.borderColor = '#14b8a6')} - onBlur={(e) => (e.target.style.borderColor = '#d1d5db')} - /> - ) : ( -
- - -
- )} -
- ); -}; - export function App() { const [submitted, setSubmitted] = useState(false); const [submitData, setSubmitData] = useState(null); @@ -219,120 +90,37 @@ export function App() { log('⏳ Signing document...'); console.log('Submit data:', data); - const headers: Record = { - 'Content-Type': 'application/json', - }; - const bearerToken = import.meta.env.VITE_SUPERDOC_SERVICES_API_KEY; - if (bearerToken) { - headers.Authorization = `Bearer ${bearerToken}`; - } - try { - // Step 1: Prepare fields for annotation - const fields = [...data.documentFields, ...data.signerFields] - .filter( - (field) => - field.id !== 'consent_agreement' && field.id !== 'terms' && field.id !== 'email', - ) - .map((field) => { - // Signature field: add options with IP label - if (field.id === '789012') { - const isDrawnSignature = - typeof field.value === 'string' && field.value?.startsWith('data:image/'); - return { - id: field.id, - value: field.value, - type: isDrawnSignature ? 'signature' : 'text', - options: { - bottomLabel: { - text: 'ip: 127.0.0.1', - color: '#666', - }, - }, - }; - } - // Document fields - const docField = documentFieldsConfig.find((f) => f.id === field.id); - return { - id: field.id, - value: field.value, - type: docField?.type || 'text', - }; - }); - - // Step 2: Annotate the document - log('⏳ Annotating document...'); - const annotateResponse = await fetch('/v1/annotate', { + const response = await fetch('/v1/sign', { method: 'POST', - headers, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - document: { - url: 'https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement_updated.docx', + document: { url: documentSource }, + documentFields: data.documentFields, + signerFields: data.signerFields, + auditTrail: data.auditTrail, + eventId: data.eventId, + signer: { + name: documentFields['234567'] || 'Signer', + email: 'andrii@superdoc.dev', + ip: '127.0.0.1', + userAgent: navigator.userAgent, + }, + certificate: { enable: true }, + metadata: { + company: documentFields['345678'], + plan: documentFields['456789'], }, - fields, + fileName: `signed_agreement_${data.eventId}.pdf`, }), }); - if (!annotateResponse.ok) { - const error = await annotateResponse.text(); - throw new Error(error || 'Failed to annotate document'); - } - - const annotateResult = await annotateResponse.json(); - const annotatedBase64 = annotateResult.document.base64; - - // Step 3: Sign the annotated document - log('⏳ Applying digital signature...'); - const signPayload = { - eventId: data.eventId, - document: { - base64: annotatedBase64, - }, - auditTrail: data.auditTrail, - signer: { - name: documentFields['234567'] || 'Signer', // Full Name field - email: 'andrii@superdoc.dev', - ip: '127.0.0.1', - userAgent: navigator.userAgent, - }, - certificate: { - enable: true, - }, - metadata: { - company: documentFields['345678'], - plan: documentFields['456789'], - }, - }; - - const signResponse = await fetch('/v1/sign', { - method: 'POST', - headers, - body: JSON.stringify(signPayload), - }); - - if (!signResponse.ok) { - const error = await signResponse.text(); + if (!response.ok) { + const error = await response.text(); throw new Error(error || 'Failed to sign document'); } - const signResult = await signResponse.json(); - const { base64: signedBase64 } = signResult.document; - - // Step 4: Download the signed PDF - const byteCharacters = atob(signedBase64); - const byteNumbers = new Array(byteCharacters.length); - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - const byteArray = new Uint8Array(byteNumbers); - const blob = new Blob([byteArray], { type: 'application/pdf' }); - - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `signed_agreement_${data.eventId}.pdf`; - a.click(); - URL.revokeObjectURL(url); + await downloadBlob(response, `signed_agreement_${data.eventId}.pdf`); log('✓ Document signed and downloaded!'); setSubmitted(true); @@ -344,50 +132,19 @@ export function App() { }; const handleDownload = async (data: DownloadData) => { - if (typeof data.documentSource !== 'string') { - log('Download requires a document URL.'); - return; - } - - const fields = [...data.fields.document, ...data.fields.signer] - .filter((field) => field.id !== 'consent_agreement' && field.id !== '406948812') - .map((field) => { - // Signature field: determine type from value (data:image = signature, else text) - if (field.id === '789012') { - const isDrawnSignature = - typeof field.value === 'string' && field.value.startsWith('data:image/'); - return { - id: field.id, - value: field.value, - type: isDrawnSignature ? 'signature' : 'text', - }; - } - // Document fields have type from config - const docField = documentFieldsConfig.find((f) => f.id === field.id); - return { - id: field.id, - value: field.value, - type: docField?.type || 'text', - }; - }); - - const headers: Record = { - 'Content-Type': 'application/json', - }; - const bearerToken = import.meta.env.VITE_SUPERDOC_SERVICES_API_KEY; - if (bearerToken) { - headers.Authorization = `Bearer ${bearerToken}`; - } - - console.log('Annotating document with fields:', fields); - try { - const response = await fetch('/v1/annotate?to=pdf', { + if (typeof data.documentSource !== 'string') { + log('Download requires a document URL.'); + return; + } + + const response = await fetch('/v1/download', { method: 'POST', - headers, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ document: { url: data.documentSource }, - fields, + fields: data.fields, + fileName: data.fileName, }), }); @@ -396,24 +153,7 @@ export function App() { throw new Error(error || 'Failed to annotate document'); } - const result = await response.json(); - const { base64, contentType } = result.document; - - // Convert base64 to blob - const byteCharacters = atob(base64); - const byteNumbers = new Array(byteCharacters.length); - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - const byteArray = new Uint8Array(byteNumbers); - const blob = new Blob([byteArray], { type: contentType }); - - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = data.fileName || 'document.pdf'; - a.click(); - URL.revokeObjectURL(url); + await downloadBlob(response, data.fileName || 'document.pdf'); log('✓ Downloaded PDF'); } catch (error) { console.error('Error processing document:', error); @@ -522,8 +262,7 @@ export function App() { ref={esignRef} eventId={eventId} document={{ - source: - 'https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement_updated.docx', + source: documentSource, mode: 'full', validation: { scroll: { required: true }, diff --git a/demo/src/CustomSignature.tsx b/demo/src/CustomSignature.tsx new file mode 100644 index 0000000..584a155 --- /dev/null +++ b/demo/src/CustomSignature.tsx @@ -0,0 +1,146 @@ +import React, { useEffect, useRef, useState } from 'react'; +import SignaturePad from 'signature_pad'; +import type { FieldComponentProps } from '@superdoc-dev/esign'; + +const CustomSignature: React.FC = ({ value, onChange, isDisabled, label }) => { + const [mode, setMode] = useState<'type' | 'draw'>('type'); + const canvasRef = useRef(null); + const signaturePadRef = useRef(null); + + const switchMode = (newMode: 'type' | 'draw') => { + setMode(newMode); + onChange(''); + if (newMode === 'draw' && signaturePadRef.current) { + signaturePadRef.current.clear(); + } + }; + + const clearCanvas = () => { + if (signaturePadRef.current) { + signaturePadRef.current.clear(); + onChange(''); + } + }; + + useEffect(() => { + if (!canvasRef.current || mode !== 'draw') return; + + signaturePadRef.current = new SignaturePad(canvasRef.current, { + backgroundColor: 'rgb(255, 255, 255)', + penColor: 'rgb(0, 0, 0)', + }); + + if (isDisabled) { + signaturePadRef.current.off(); + } + + signaturePadRef.current.addEventListener('endStroke', () => { + if (signaturePadRef.current) { + onChange(signaturePadRef.current.toDataURL()); + } + }); + + return () => { + if (signaturePadRef.current) { + signaturePadRef.current.off(); + } + }; + }, [mode, isDisabled, onChange]); + + return ( +
+ {label && ( + + )} +
+ + +
+ {mode === 'type' ? ( + onChange(e.target.value)} + disabled={isDisabled} + placeholder="Type your full name" + style={{ + fontFamily: 'cursive', + fontSize: '20px', + padding: '14px', + border: '1px solid #d1d5db', + borderRadius: '8px', + outline: 'none', + transition: 'border-color 0.2s', + }} + onFocus={(e) => (e.target.style.borderColor = '#14b8a6')} + onBlur={(e) => (e.target.style.borderColor = '#d1d5db')} + /> + ) : ( +
+ + +
+ )} +
+ ); +}; + +export default CustomSignature; diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 5545f88..1f997ff 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -16,9 +16,9 @@ export default defineConfig(({ mode }) => ({ server: { proxy: { '/v1': { - target: 'https://api.superdoc.dev', + target: 'http://localhost:3003', changeOrigin: true, - secure: true, + secure: false, }, }, }, From f51161231c3cef70cddaa3453047686fd51e1e36 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 21:33:32 +0100 Subject: [PATCH 04/14] test: temporary changing existing workflow for testing --- .github/workflows/pr-validation.yml | 87 +++++++++++++++++++---------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 0963925..103e32c 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -1,46 +1,73 @@ -name: PR Validation +name: Deploy Proxy Server to GCP on: - pull_request: - branches: [main, stable, "*.x"] + workflow_dispatch: + inputs: + deploy_env: + description: 'Environment label (optional)' + required: false -concurrency: - group: pr-${{ github.event.pull_request.number }} - cancel-in-progress: true +env: + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + REGION: ${{ vars.GCP_REGION }} + SERVICE_NAME: ${{ vars.GCP_SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ vars.SUPERDOC_SERVICES_BASE_URL }} jobs: - validate: + deploy: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: - - name: Generate token - id: generate_token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout + uses: actions/checkout@v5 - - uses: actions/checkout@v5 + - name: Auth to Google Cloud + uses: google-github-actions/auth@v2 with: - fetch-depth: 0 - token: ${{ steps.generate_token.outputs.token }} + credentials_json: ${{ secrets.GCP_SA_KEY }} - - uses: pnpm/action-setup@v4 + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 with: - version: 9 + project_id: ${{ env.PROJECT_ID }} - - uses: actions/setup-node@v5 - with: - node-version: 22 - cache: "pnpm" + - name: Build and push container with Cloud Build + run: | + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:${GITHUB_SHA}" + + echo "REGION=${REGION}" >> $GITHUB_ENV + echo "SERVICE_NAME=${SERVICE_NAME}" >> $GITHUB_ENV + echo "IMAGE=${IMAGE}" >> $GITHUB_ENV - - run: pnpm install --frozen-lockfile + gcloud builds submit demo/server --tag "${IMAGE}" + env: + PROJECT_ID: ${{ env.PROJECT_ID }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} - - name: Validate commits + - name: Deploy container to Cloud Run run: | - pnpm exec commitlint \ - --from ${{ github.event.pull_request.base.sha }} \ - --to ${{ github.event.pull_request.head.sha }} + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="${IMAGE}" + SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL:-https://api.superdoc.dev}" - - run: pnpm lint - - run: pnpm build - - run: pnpm test + gcloud run deploy "${SERVICE_NAME}" \ + --image "${IMAGE}" \ + --region "${REGION}" \ + --memory=1Gi \ + --cpu=1 \ + --allow-unauthenticated \ + --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" + env: + IMAGE: ${{ env.IMAGE }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ env.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ env.SUPERDOC_SERVICES_BASE_URL }} From e575784ac5a4372ef30b11cc4166210eedd8c62e Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 21:44:15 +0100 Subject: [PATCH 05/14] fix: used wrong workflow --- .github/workflows/deploy-demo.yml | 100 +++++++++++++++------------- .github/workflows/pr-validation.yml | 87 +++++++++--------------- 2 files changed, 85 insertions(+), 102 deletions(-) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 536f11a..103e32c 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -1,63 +1,73 @@ -name: Deploy Demo +name: Deploy Proxy Server to GCP on: - push: - branches: [main] - paths: - - "demo/**" - - "!**.md" workflow_dispatch: + inputs: + deploy_env: + description: 'Environment label (optional)' + required: false -permissions: - contents: read - pages: write - id-token: write +env: + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + REGION: ${{ vars.GCP_REGION }} + SERVICE_NAME: ${{ vars.GCP_SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ vars.SUPERDOC_SERVICES_BASE_URL }} jobs: deploy: runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + permissions: + contents: read + id-token: write + steps: - - name: Generate token - id: generate_token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout + uses: actions/checkout@v5 - - uses: actions/checkout@v5 + - name: Auth to Google Cloud + uses: google-github-actions/auth@v2 with: - fetch-depth: 0 - token: ${{ steps.generate_token.outputs.token }} + credentials_json: ${{ secrets.GCP_SA_KEY }} - - uses: pnpm/action-setup@v2 + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v2 with: - version: 9 + project_id: ${{ env.PROJECT_ID }} - - uses: actions/setup-node@v3 - with: - node-version: 20 - cache: "pnpm" + - name: Build and push container with Cloud Build + run: | + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:${GITHUB_SHA}" - - name: Install and Build + echo "REGION=${REGION}" >> $GITHUB_ENV + echo "SERVICE_NAME=${SERVICE_NAME}" >> $GITHUB_ENV + echo "IMAGE=${IMAGE}" >> $GITHUB_ENV + + gcloud builds submit demo/server --tag "${IMAGE}" env: - VITE_SUPERDOC_SERVICES_API_KEY: ${{ secrets.VITE_SUPERDOC_SERVICES_API_KEY }} + PROJECT_ID: ${{ env.PROJECT_ID }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} + + - name: Deploy container to Cloud Run run: | - pnpm install - rm -rf dist - pnpm build - cd demo - rm -rf node_modules dist - pnpm install - pnpm build - - - name: Upload artifact - uses: actions/upload-pages-artifact@v4 - with: - path: ./demo/dist + REGION="${REGION:-us-central1}" + SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" + IMAGE="${IMAGE}" + SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL:-https://api.superdoc.dev}" - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + gcloud run deploy "${SERVICE_NAME}" \ + --image "${IMAGE}" \ + --region "${REGION}" \ + --memory=1Gi \ + --cpu=1 \ + --allow-unauthenticated \ + --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" + env: + IMAGE: ${{ env.IMAGE }} + REGION: ${{ env.REGION }} + SERVICE_NAME: ${{ env.SERVICE_NAME }} + SUPERDOC_SERVICES_API_KEY: ${{ env.SUPERDOC_SERVICES_API_KEY }} + SUPERDOC_SERVICES_BASE_URL: ${{ env.SUPERDOC_SERVICES_BASE_URL }} diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 103e32c..0963925 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -1,73 +1,46 @@ -name: Deploy Proxy Server to GCP +name: PR Validation on: - workflow_dispatch: - inputs: - deploy_env: - description: 'Environment label (optional)' - required: false + pull_request: + branches: [main, stable, "*.x"] -env: - PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} - REGION: ${{ vars.GCP_REGION }} - SERVICE_NAME: ${{ vars.GCP_SERVICE_NAME }} - SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} - SUPERDOC_SERVICES_BASE_URL: ${{ vars.SUPERDOC_SERVICES_BASE_URL }} +concurrency: + group: pr-${{ github.event.pull_request.number }} + cancel-in-progress: true jobs: - deploy: + validate: runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Auth to Google Cloud - uses: google-github-actions/auth@v2 + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@v2 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Setup gcloud - uses: google-github-actions/setup-gcloud@v2 + - uses: actions/checkout@v5 with: - project_id: ${{ env.PROJECT_ID }} + fetch-depth: 0 + token: ${{ steps.generate_token.outputs.token }} - - name: Build and push container with Cloud Build - run: | - REGION="${REGION:-us-central1}" - SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" - IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:${GITHUB_SHA}" + - uses: pnpm/action-setup@v4 + with: + version: 9 - echo "REGION=${REGION}" >> $GITHUB_ENV - echo "SERVICE_NAME=${SERVICE_NAME}" >> $GITHUB_ENV - echo "IMAGE=${IMAGE}" >> $GITHUB_ENV + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: "pnpm" - gcloud builds submit demo/server --tag "${IMAGE}" - env: - PROJECT_ID: ${{ env.PROJECT_ID }} - REGION: ${{ env.REGION }} - SERVICE_NAME: ${{ env.SERVICE_NAME }} + - run: pnpm install --frozen-lockfile - - name: Deploy container to Cloud Run + - name: Validate commits run: | - REGION="${REGION:-us-central1}" - SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" - IMAGE="${IMAGE}" - SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL:-https://api.superdoc.dev}" + pnpm exec commitlint \ + --from ${{ github.event.pull_request.base.sha }} \ + --to ${{ github.event.pull_request.head.sha }} - gcloud run deploy "${SERVICE_NAME}" \ - --image "${IMAGE}" \ - --region "${REGION}" \ - --memory=1Gi \ - --cpu=1 \ - --allow-unauthenticated \ - --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" - env: - IMAGE: ${{ env.IMAGE }} - REGION: ${{ env.REGION }} - SERVICE_NAME: ${{ env.SERVICE_NAME }} - SUPERDOC_SERVICES_API_KEY: ${{ env.SUPERDOC_SERVICES_API_KEY }} - SUPERDOC_SERVICES_BASE_URL: ${{ env.SUPERDOC_SERVICES_BASE_URL }} + - run: pnpm lint + - run: pnpm build + - run: pnpm test From 171c33777894ffa79a697d5e723355d1d53089af Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:03:52 +0100 Subject: [PATCH 06/14] fix: replaced key var with secret --- .github/workflows/deploy-demo.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 103e32c..e94ee8a 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -64,7 +64,8 @@ jobs: --memory=1Gi \ --cpu=1 \ --allow-unauthenticated \ - --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" + --set-env-vars SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" \ + --set-secrets="SUPERDOC_SERVICES_API_KEY=esign-demo-sd-services-api-key:latest" env: IMAGE: ${{ env.IMAGE }} REGION: ${{ env.REGION }} From ef7b9a4b67adcc5b12014550c79e92d3dc519f54 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:34:01 +0100 Subject: [PATCH 07/14] fix: added cors, fixed ci/cd process --- .github/workflows/deploy-proxy.yml | 14 ++++++++------ demo/server/server.js | 10 ++++++++-- demo/vite.config.ts | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-proxy.yml b/.github/workflows/deploy-proxy.yml index 38e2b3e..e94ee8a 100644 --- a/.github/workflows/deploy-proxy.yml +++ b/.github/workflows/deploy-proxy.yml @@ -8,11 +8,11 @@ on: required: false env: - PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} - REGION: ${{ secrets.GCP_REGION }} - SERVICE_NAME: ${{ secrets.GCP_SERVICE_NAME }} + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + REGION: ${{ vars.GCP_REGION }} + SERVICE_NAME: ${{ vars.GCP_SERVICE_NAME }} SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} - SUPERDOC_SERVICES_BASE_URL: ${{ secrets.SUPERDOC_SERVICES_BASE_URL }} + SUPERDOC_SERVICES_BASE_URL: ${{ vars.SUPERDOC_SERVICES_BASE_URL }} jobs: deploy: @@ -61,9 +61,11 @@ jobs: gcloud run deploy "${SERVICE_NAME}" \ --image "${IMAGE}" \ --region "${REGION}" \ - --platform managed \ + --memory=1Gi \ + --cpu=1 \ --allow-unauthenticated \ - --set-env-vars SUPERDOC_SERVICES_API_KEY="${SUPERDOC_SERVICES_API_KEY}",SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" + --set-env-vars SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" \ + --set-secrets="SUPERDOC_SERVICES_API_KEY=esign-demo-sd-services-api-key:latest" env: IMAGE: ${{ env.IMAGE }} REGION: ${{ env.REGION }} diff --git a/demo/server/server.js b/demo/server/server.js index 5f76664..f3b95d0 100644 --- a/demo/server/server.js +++ b/demo/server/server.js @@ -7,11 +7,17 @@ dotenv.config(); const app = express(); const PORT = process.env.PORT || 3001; const SUPERDOC_SERVICES_API_KEY = process.env.SUPERDOC_SERVICES_API_KEY; -const SUPERDOC_SERVICES_BASE_URL = process.env.SUPERDOC_SERVICES_BASE_URL || 'https://api.superdoc.dev'; +const SUPERDOC_SERVICES_BASE_URL = + process.env.SUPERDOC_SERVICES_BASE_URL || 'https://api.superdoc.dev'; const CONSENT_FIELD_IDS = new Set(['consent_agreement', 'terms', 'email', '406948812']); const SIGNATURE_FIELD_ID = '789012'; -app.use(cors()); +// TODO: tighten origin allowlist when the frontend URL is known. +app.use( + cors({ + origin: 'https://esign.superdoc.dev', + }), +); app.use(express.json({ limit: '50mb' })); app.get('/health', (req, res) => { diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 1f997ff..884133b 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -16,7 +16,7 @@ export default defineConfig(({ mode }) => ({ server: { proxy: { '/v1': { - target: 'http://localhost:3003', + target: 'https://esign-demo-proxy-server-191591660773.us-central1.run.app', changeOrigin: true, secure: false, }, From a0df722a9a40c56ed03e34952f0fa0ea81e348ee Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:41:20 +0100 Subject: [PATCH 08/14] fix: fixed linter config for server --- eslint.config.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/eslint.config.js b/eslint.config.js index cfbe3e5..4934d91 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -24,5 +24,18 @@ export default [ 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn' } + }, + { + files: ['demo/server/**/*.js'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + fetch: 'readonly' + } + }, + env: { + node: true + } } ]; From 0b72b26dfeac9f086bc4dc7f85a2d73e8ca88d49 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:50:54 +0100 Subject: [PATCH 09/14] fix: fixed workflows for github --- .github/workflows/deploy-demo.yml | 101 ++++++++---------- ...ploy-proxy.yml => deploy-proxy-server.yml} | 6 +- demo/server/server.js | 1 - 3 files changed, 46 insertions(+), 62 deletions(-) rename .github/workflows/{deploy-proxy.yml => deploy-proxy-server.yml} (94%) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index e94ee8a..536f11a 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -1,74 +1,63 @@ -name: Deploy Proxy Server to GCP +name: Deploy Demo on: + push: + branches: [main] + paths: + - "demo/**" + - "!**.md" workflow_dispatch: - inputs: - deploy_env: - description: 'Environment label (optional)' - required: false -env: - PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} - REGION: ${{ vars.GCP_REGION }} - SERVICE_NAME: ${{ vars.GCP_SERVICE_NAME }} - SUPERDOC_SERVICES_API_KEY: ${{ secrets.SUPERDOC_SERVICES_API_KEY }} - SUPERDOC_SERVICES_BASE_URL: ${{ vars.SUPERDOC_SERVICES_BASE_URL }} +permissions: + contents: read + pages: write + id-token: write jobs: deploy: runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Auth to Google Cloud - uses: google-github-actions/auth@v2 + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@v2 with: - credentials_json: ${{ secrets.GCP_SA_KEY }} + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Setup gcloud - uses: google-github-actions/setup-gcloud@v2 + - uses: actions/checkout@v5 with: - project_id: ${{ env.PROJECT_ID }} + fetch-depth: 0 + token: ${{ steps.generate_token.outputs.token }} - - name: Build and push container with Cloud Build - run: | - REGION="${REGION:-us-central1}" - SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" - IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:${GITHUB_SHA}" + - uses: pnpm/action-setup@v2 + with: + version: 9 - echo "REGION=${REGION}" >> $GITHUB_ENV - echo "SERVICE_NAME=${SERVICE_NAME}" >> $GITHUB_ENV - echo "IMAGE=${IMAGE}" >> $GITHUB_ENV + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: "pnpm" - gcloud builds submit demo/server --tag "${IMAGE}" + - name: Install and Build env: - PROJECT_ID: ${{ env.PROJECT_ID }} - REGION: ${{ env.REGION }} - SERVICE_NAME: ${{ env.SERVICE_NAME }} - - - name: Deploy container to Cloud Run + VITE_SUPERDOC_SERVICES_API_KEY: ${{ secrets.VITE_SUPERDOC_SERVICES_API_KEY }} run: | - REGION="${REGION:-us-central1}" - SERVICE_NAME="${SERVICE_NAME:-esign-demo-proxy-server}" - IMAGE="${IMAGE}" - SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL:-https://api.superdoc.dev}" + pnpm install + rm -rf dist + pnpm build + cd demo + rm -rf node_modules dist + pnpm install + pnpm build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: ./demo/dist - gcloud run deploy "${SERVICE_NAME}" \ - --image "${IMAGE}" \ - --region "${REGION}" \ - --memory=1Gi \ - --cpu=1 \ - --allow-unauthenticated \ - --set-env-vars SUPERDOC_SERVICES_BASE_URL="${SUPERDOC_SERVICES_BASE_URL}" \ - --set-secrets="SUPERDOC_SERVICES_API_KEY=esign-demo-sd-services-api-key:latest" - env: - IMAGE: ${{ env.IMAGE }} - REGION: ${{ env.REGION }} - SERVICE_NAME: ${{ env.SERVICE_NAME }} - SUPERDOC_SERVICES_API_KEY: ${{ env.SUPERDOC_SERVICES_API_KEY }} - SUPERDOC_SERVICES_BASE_URL: ${{ env.SUPERDOC_SERVICES_BASE_URL }} + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/deploy-proxy.yml b/.github/workflows/deploy-proxy-server.yml similarity index 94% rename from .github/workflows/deploy-proxy.yml rename to .github/workflows/deploy-proxy-server.yml index e94ee8a..da8c5ca 100644 --- a/.github/workflows/deploy-proxy.yml +++ b/.github/workflows/deploy-proxy-server.yml @@ -1,11 +1,7 @@ -name: Deploy Proxy Server to GCP +name: Deploy Proxy Server on: workflow_dispatch: - inputs: - deploy_env: - description: 'Environment label (optional)' - required: false env: PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} diff --git a/demo/server/server.js b/demo/server/server.js index f3b95d0..72b987c 100644 --- a/demo/server/server.js +++ b/demo/server/server.js @@ -12,7 +12,6 @@ const SUPERDOC_SERVICES_BASE_URL = const CONSENT_FIELD_IDS = new Set(['consent_agreement', 'terms', 'email', '406948812']); const SIGNATURE_FIELD_ID = '789012'; -// TODO: tighten origin allowlist when the frontend URL is known. app.use( cors({ origin: 'https://esign.superdoc.dev', From 5d07b75a0ffd2884202aae494f2f8c69dc0a8224 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:53:28 +0100 Subject: [PATCH 10/14] fix: fixed linter for node server --- eslint.config.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 4934d91..965db51 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -31,11 +31,17 @@ export default [ ecmaVersion: 'latest', sourceType: 'module', globals: { - fetch: 'readonly' + fetch: 'readonly', + process: 'readonly', + Buffer: 'readonly', + console: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + setImmediate: 'readonly', + global: 'readonly' } - }, - env: { - node: true } } ]; From 43f68a512d326119f0ef24f2c823e2676254c7ec Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 22:56:07 +0100 Subject: [PATCH 11/14] chore: cleaning --- .github/workflows/deploy-demo.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 536f11a..3311a37 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -42,8 +42,6 @@ jobs: cache: "pnpm" - name: Install and Build - env: - VITE_SUPERDOC_SERVICES_API_KEY: ${{ secrets.VITE_SUPERDOC_SERVICES_API_KEY }} run: | pnpm install rm -rf dist From 73610f4bc2767eedab2a166e34c05e9d3adb8902 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 18 Dec 2025 23:15:16 +0100 Subject: [PATCH 12/14] chore: server using pnpm instead of npm --- demo/server/Dockerfile | 6 +- demo/server/package-lock.json | 861 ---------------------------------- demo/server/package.json | 3 +- demo/server/pnpm-lock.yaml | 606 ++++++++++++++++++++++++ 4 files changed, 611 insertions(+), 865 deletions(-) delete mode 100644 demo/server/package-lock.json create mode 100644 demo/server/pnpm-lock.yaml diff --git a/demo/server/Dockerfile b/demo/server/Dockerfile index 4d53525..aa7d575 100644 --- a/demo/server/Dockerfile +++ b/demo/server/Dockerfile @@ -2,12 +2,12 @@ FROM node:20-alpine WORKDIR /app -COPY package*.json ./ -RUN npm install --omit=dev +COPY package.json ./ +RUN corepack enable && pnpm install --prod --no-lockfile COPY . . # Cloud Run/Functions set PORT; default to 8080 ENV PORT=8080 -CMD ["npm", "start"] +CMD ["pnpm", "start"] diff --git a/demo/server/package-lock.json b/demo/server/package-lock.json deleted file mode 100644 index c74f124..0000000 --- a/demo/server/package-lock.json +++ /dev/null @@ -1,861 +0,0 @@ -{ - "name": "esign-proxy-server", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "esign-proxy-server", - "dependencies": { - "cors": "^2.8.5", - "dotenv": "^16.4.5", - "express": "^4.19.2" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/demo/server/package.json b/demo/server/package.json index dbd0f13..962a673 100644 --- a/demo/server/package.json +++ b/demo/server/package.json @@ -4,7 +4,8 @@ "type": "module", "main": "server.js", "scripts": { - "start": "node server.js" + "start": "node server.js", + "dev": "node server.js" }, "dependencies": { "cors": "^2.8.5", diff --git a/demo/server/pnpm-lock.yaml b/demo/server/pnpm-lock.yaml new file mode 100644 index 0000000..961b1be --- /dev/null +++ b/demo/server/pnpm-lock.yaml @@ -0,0 +1,606 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + cors: + specifier: ^2.8.5 + version: 2.8.5 + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + express: + specifier: ^4.19.2 + version: 4.22.1 + +packages: + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + +snapshots: + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + array-flatten@1.1.1: {} + + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.7: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + depd@2.0.0: {} + + destroy@1.2.0: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + negotiator@0.6.3: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + parseurl@1.3.3: {} + + path-to-regexp@0.1.12: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + statuses@2.0.2: {} + + toidentifier@1.0.1: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + unpipe@1.0.0: {} + + utils-merge@1.0.1: {} + + vary@1.1.2: {} From afb066f7a9f4f5e33d7612dafd70e3aac88367e4 Mon Sep 17 00:00:00 2001 From: aorlov Date: Tue, 23 Dec 2025 01:27:28 +0100 Subject: [PATCH 13/14] docs: update README with demo integration details and setup instructions --- demo/README.md | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/demo/README.md b/demo/README.md index e0fe744..bf1d474 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,26 +1,51 @@ # Demo +This demo shows how to integrate `@superdoc-dev/esign` into a React application. The frontend sends signing and download requests to a proxy server, which securely communicates with the SuperDoc Services API. + +## Prerequisites + +You'll need a SuperDoc Services API key. [Get your API key here](https://docs.superdoc.dev/api-reference/authentication/register). + ## Setup -1. Build the main package first (from root): +1. Build the main package (from repo root): ```bash pnpm build ``` -2. Install demo dependencies: +2. Install dependencies: ```bash cd demo pnpm install + cd server + pnpm install ``` -3. Create `.env` file with your API key: +3. Create `.env` file in `demo/server/`: ``` - VITE_SUPERDOC_SERVICES_API_KEY=your_key_here + SUPERDOC_SERVICES_API_KEY=your_key_here ``` -4. Run the demo: - ```bash - pnpm run dev +4. Update `demo/vite.config.ts` to proxy to localhost: + ```ts + proxy: { + '/v1': { + target: 'http://localhost:3001', + changeOrigin: true, + }, + }, ``` +## Running + +Start the proxy server: +```bash +cd demo/server +pnpm start +``` +In a separate terminal, start the frontend: +```bash +cd demo +pnpm dev +``` From c602ecd4d63e037640bc64fa8267aa2e4c8a9221 Mon Sep 17 00:00:00 2001 From: aorlov Date: Tue, 23 Dec 2025 02:09:48 +0100 Subject: [PATCH 14/14] feat: add demo user and IP address handling in server and update signer information --- demo/server/server.js | 16 +++++++++++++--- demo/src/App.tsx | 6 ------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/demo/server/server.js b/demo/server/server.js index 72b987c..36d5b3c 100644 --- a/demo/server/server.js +++ b/demo/server/server.js @@ -11,6 +11,12 @@ const SUPERDOC_SERVICES_BASE_URL = process.env.SUPERDOC_SERVICES_BASE_URL || 'https://api.superdoc.dev'; const CONSENT_FIELD_IDS = new Set(['consent_agreement', 'terms', 'email', '406948812']); const SIGNATURE_FIELD_ID = '789012'; +const IP_ADDRESS = '127.0.0.1'; // Replace with real client IP once available +const DEMO_USER = { + name: 'Demo User', + email: 'demo@superdoc.dev', + userAgent: 'demo-user-agent', +}; app.use( cors({ @@ -38,7 +44,7 @@ const normalizeFields = (fieldsPayload = {}) => { const normalized = { id: field.id, value, type }; if (type === 'signature') { normalized.options = { - bottomLabel: { text: 'ip: 127.0.0.1', color: '#666' }, + bottomLabel: { text: `ip: ${IP_ADDRESS}`, color: '#666' }, }; } return normalized; @@ -120,7 +126,6 @@ app.post('/v1/sign', async (req, res) => { signerFields = [], auditTrail = [], eventId, - signer, certificate, metadata, fileName = 'signed-document.pdf', @@ -154,7 +159,12 @@ app.post('/v1/sign', async (req, res) => { eventId, document: { base64: annotatedBase64 }, auditTrail, - signer, + signer: { + name: DEMO_USER.name, + email: DEMO_USER.email, + ip: IP_ADDRESS, + userAgent: DEMO_USER.userAgent, + }, certificate, metadata, }; diff --git a/demo/src/App.tsx b/demo/src/App.tsx index df00e7f..219d08f 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -100,12 +100,6 @@ export function App() { signerFields: data.signerFields, auditTrail: data.auditTrail, eventId: data.eventId, - signer: { - name: documentFields['234567'] || 'Signer', - email: 'andrii@superdoc.dev', - ip: '127.0.0.1', - userAgent: navigator.userAgent, - }, certificate: { enable: true }, metadata: { company: documentFields['345678'],