Compare commits

..
Author SHA1 Message Date
113a3cbabe docs: bring README.md in line with Standard Readme 2026-09-22 09:58:30 -04:00
27 changed files with 320 additions and 2456 deletions

7
.gitignore vendored
View file

@ -1,7 +0,0 @@
node_modules/
build/
.env
.env.*
.zapierapprc
*.log
.DS_Store

11
NATS.md
View file

@ -5,13 +5,6 @@ This Zapier Platform app **does not use NATS**.
| Direction | Address | Peer | Body |
|-----------|---------|------|------|
| IN | Zapier runtime | user Zap | create/search/trigger fields |
| OUT HTTPS | middleware `/zapier/v1/auth/me` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/timestamp`, `/timestamp/wait`, `/timestamp/batch` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/verify`, `/verify/batch` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/status/{id}`, `/status/{id}/verification`, `/status/batch` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/hashes/{sha256}`, `/hashes/{sha256}?includeAttached=true&includeTree=true` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/receipts/{id}` | verae-middleware | Bearer `zmw_` |
| OUT HTTPS | `/zapier/v1/webhooks/subscribe`, `/webhooks/unsubscribe` | verae-middleware | Bearer `zmw_` |
| IN HTTPS | Zapier hook URL | webhook-deliver / in-process poller | `{ event, jobId, tenantId, status }` |
| OUT HTTPS | middleware `/zapier/v1/timestamp`, `/wait`, `/verify`, `/status/{id}`, `/hashes/{sha256}`, `/hashes/{sha256}?includeTree=true`, REST Hook subscribe | verae-middleware | Bearer `zmw_` |
Job completion is a REST Hook HTTP POST from the middleware's webhook path, not a NATS client in Zapier.
Job completion is a REST Hook HTTP POST from webhook-deliver, not a NATS client in Zapier.

205
README.md
View file

@ -1,109 +1,36 @@
# verae-zapier
Zapier Platform CLI app for Verae. Runs on **Zapier's servers**. Calls only middleware HTTPS (`MIDDLEWARE_BASE_URL`, `/zapier/v1/*`), never NATS and never `api.veraetime.net`.
[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
Zapier Platform CLI app for Verae. Runs on **Zapier’s servers**. Calls only middleware HTTPS...
**Forgejo:** https://git.georgelambert.org/marchon/verae-zapier-app
**Middleware:** https://git.georgelambert.org/marchon/verae-middleware
**Catalog:** https://zapier.georgelambert.org/packages/verae-zapier/README.pdf
**Sphinx API sheets:** https://zapier.georgelambert.org/sphinx/index.html
## Role
Runs on **Zapier's servers**. Calls only the middleware HTTPS API (`MIDDLEWARE_BASE_URL`), never
NATS and never `api.veraetime.net` directly. The middleware owns customer API keys (`zmw_…`),
plans and quotas, job watching, REST Hook delivery, and the upstream Verae chain client
(`POST /auth/login`, `/api/timestamp`, `/api/status`, `/api/verify`, … on api.veraetime.net).
Runs on **Zapier’s servers**. Calls only the middleware HTTPS API (`MIDDLEWARE_BASE_URL`), never NATS and never `api.veraetime.net` directly.
## Modules
## Planned modules
| File | Zapier type | Middleware call |
|------|-------------|-----------------|
| `authentication.js` | custom API key | `GET /zapier/v1/auth/me` → connection label `tenantId (plan)` |
| `index.js` | app definition | `beforeRequest` sends the key as `Authorization: Bearer` **and** `x-api-key`; `afterResponse` maps `{ error, code, details }` (402/403 billing → upgrade URL, 429 → ThrottledError) |
| `lib/middleware.js` | helpers | base URL, error parsing, 404-as-no-match search helper, shared samples |
| `lib/hookTrigger.js` | helpers | REST Hook factory (`/webhooks/subscribe`, `/webhooks/unsubscribe`) |
| `creates/timestamp_and_wait.js` | action | `POST /timestamp/wait` (primary) |
| `creates/create_timestamp.js` | action | `POST /timestamp` → 202 `{ jobId }` |
| `creates/batch_timestamp.js` | action | `POST /timestamp/batch` (paid plans) |
| `creates/verify_timestamp.js` | action | `POST /verify` |
| `creates/batch_verify.js` | action | `POST /verify/batch` |
| `creates/batch_status.js` | action | `POST /status/batch` |
| `creates/get_receipt.js` | action | `GET /receipts/{jobId}` (JSON; optional PDF via `z.stashFile`) |
| `searches/job_status.js` | search | `GET /status/{jobId}` (404 → no match) |
| `searches/job_verification.js` | search | `GET /status/{jobId}/verification` (404 → no match) |
| `searches/hash_lookup.js` | search | `GET /hashes/{sha256}` (404 → no match) |
| `searches/tree_lookup.js` | search | `GET /hashes/{sha256}?includeAttached=true&includeTree=true` |
| `searches/usage.js` | search | `GET /auth/me` → plan + month-to-date usage vs limits (Find Usage) |
| `triggers/timestamp_completed.js` | REST Hook | event `timestamp.completed` |
| `triggers/timestamp_failed.js` | REST Hook | event `timestamp.failed` |
Hook payload delivered by the middleware: `{ event, jobId, tenantId, status }`; the trigger
adds `id = jobId` so Zapier can dedupe.
Create Timestamp / Create Timestamp and Wait take **Data or SHA256** (64 hex; hash large files
outside Zapier and send only the digest, per the user guide), plus optional public / private
metadata dictionaries. A digest that was sealed before returns the original job with
`existing: true` and no second chain write.
| File | Purpose |
|------|---------|
| `authentication.js` | Custom API key auth → `GET /zapier/v1/auth/me` |
| `index.js` | App definition, beforeRequest, afterResponse error mapping |
| `creates/timestamp_and_wait.js` | Primary action |
| `creates/create_timestamp.js` | Async jobId action |
| `creates/verify_timestamp.js` | Verify certificate |
| `creates/batch_timestamp.js` | Batch create |
| `searches/job_status.js` | Lookup by jobId |
| `triggers/timestamp_completed.js` | REST Hook |
I/O contracts: https://zapier.georgelambert.org/docs/developer/modules/function-reference.pdf
### Known gaps upstream (middleware / chain), not in this app
- `GET /hashes/{sha256}` returns **501 NOT_IMPLEMENTED** when the middleware runs against the
live chain (`MOCK_VERAE=false`); the Verae OpenAPI has no hash lookup. The two hash searches
surface that message verbatim.
- `publicMetadata` / `privateMetadata` and `sha256`-only creates are accepted by the middleware
but only honoured by its mock chain, so this app does not expose them.
- The middleware has no "list recent events" endpoint, so `performList` on the hook triggers
returns nothing and Zapier uses the static `sample`.
## Env
| Variable | Default | Purpose |
|----------|---------|---------|
| `MIDDLEWARE_BASE_URL` | `http://127.0.0.1:3100` | Origin the app calls. Set per Zapier version: `zapier env:set 1.1.0 MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org` |
| `MIDDLEWARE_PATH_PREFIX` | `/zapier/v1` | Route prefix at that origin. Leave the default for the middleware or the access planes; change it only if a fronting edge mounts the same routes elsewhere. |
## Where to point it (lab, 2026-09-14)
The catalog at https://zapier.georgelambert.org/ documents the live lab on NS1:
| Origin | What it is | Usable by this app today? |
|--------|------------|---------------------------|
| `https://mw.zapier.georgelambert.org` | verae-middleware (`MOCK_VERAE=true`, NATS on) | **Yes** — full `/zapier/v1/*`; accepts `Bearer` or `x-api-key`. Use this until the Zap plane proxies every route. |
| `https://zap.zapier.georgelambert.org` | verae-access-zapier, the intended Zapier door (STATUS.md / dev-overview) | **Not yet** — it only proxies `POST /v1/timestamp` and does not forward the API key, so `auth/me`, wait, verify, status, hooks all 404/401. |
| `https://api.zapier.georgelambert.org` | verae-access-api (direct customer API plane) | Forwards `x-api-key` for `POST /v1/timestamp` only; not for Zapier. |
| zappier-edge `/v1/*` | billing edge (composition target; loopback :13000, tunnel only) | Not yet — covers timestamp/hashes/receipts/usage only, no `/zapier/v1` prefix. |
Keys: connect with a middleware **`zmw_…`** key (portal / `POST /zapier/v1/signup` /
admin provisioning). Never a Verae username, password, or JWT. After the zappier
composition lands, the same field takes the portal `x-api-key`; the app already sends that
header.
## Develop
```bash
npm install
npm test # unit tests, no network
npx zapier-platform-cli validate --without-style
```
End-to-end against a running middleware (mock chain shown; a live-chain middleware works the same).
Against the lab middleware add `E2E_SKIP_HOOK_DELIVERY=1` (it cannot POST back to your laptop):
```bash
# in verae-middleware
PORT=3100 MOCK_VERAE=true NATS_ENABLED=false ADMIN_SECRET=dev-admin npm start
curl -s -X POST http://127.0.0.1:3100/zapier/v1/admin/tenants \
-H 'x-admin-secret: dev-admin' -H 'content-type: application/json' \
-d '{"name":"E2E","plan":"pro","veraeUsername":"e2e","veraePassword":"x"}' # → apiKey
# in verae-zapier-app
MIDDLEWARE_BASE_URL=http://127.0.0.1:3100 MIDDLEWARE_API_KEY=zmw_... npm run test:e2e
# against the live lab (key from dev-overview/05-developer-credentials or /zapier/v1/signup)
MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org MIDDLEWARE_API_KEY=zmw_... \
E2E_SKIP_HOOK_DELIVERY=1 npm run test:e2e
export MIDDLEWARE_BASE_URL=https://your-middleware.example.com
```
## Gate
@ -112,96 +39,24 @@ MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org MIDDLEWARE_API_KEY=zmw_.
npm run gate:11 # from monorepo root, after Phase 11 implementation
```
## Status of the API integration (2026-09-15)
## Install
Where the Zapier ↔ Verae path stands, and what is still open. Sources: this repo,
`verae-middleware`, and the catalog (`zapier-decisions/STATUS`, `dev-overview/03…05`).
```sh
git clone ssh://git@git.georgelambert.org:2223/marchon/verae-zapier-app.git
cd verae-zapier-app
```
### Done
## Usage
- This app covers the whole middleware `/zapier/v1` surface that Zapier needs: async / wait /
batch create, single and batch verify, job status / verification / batch status, hash and
tree-node lookup, usage, retrieval receipt, and REST Hooks for `timestamp.completed` and
`timestamp.failed`.
- The middleware already has the live chain client (`src/clients/veraeClient.js`) for
`POST /api/timestamp`, `/api/batch/timestamp`, `/api/verify`, `/api/batch/verify`,
`GET /api/status/{jobId}`, `GET /api/verify/{jobId}`, `POST /api/batch/status`, and
`POST /auth/login` on https://api.veraetime.net. No Zapier-app change is needed to go live.
- Verified end to end through `zapier-platform-core` against a mock-chain middleware
(`npm run test:e2e`), on both a pro tenant (batch, hook delivery) and a free tenant
(402/403 upgrade-URL mapping).
```sh
# No single entrypoint is declared in this tree.
ls
```
### Open — blocked on people, not code
## Contributing
| Item | Owner | What is needed |
|------|-------|----------------|
| Zapier Platform push | operator | `zapier login`, `zapier register` (or link), `zapier env:set <version> MIDDLEWARE_BASE_URL=…`, `zapier push` as a private app, then test Create Timestamp and Wait, Create Timestamp, Find Job Status, Timestamp Completed. |
| Live chain (`MOCK_VERAE=false`) | Verae / operator | A Verae username + password for the middleware (`VERAE_API_BASE_URL=https://api.veraetime.net`). Then prove one real seal, confirm `202 { jobId }` → status → verify, and map the live status JSON into the receipt shape this app samples (`certificate`, `timestamp`, `blockIndex`, `sha256`). |
| Live-lab e2e run | anyone with the lab key | `MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org MIDDLEWARE_API_KEY=zmw_… E2E_SKIP_HOOK_DELIVERY=1 npm run test:e2e`. Exercises the NATS-enabled wait / poller path that the local run does not. |
Questions: open an issue at https://git.georgelambert.org/marchon/verae-zapier-app/issues. Pull requests are accepted.
### Open — code in other repos
## License
| Item | Repo | Notes |
|------|------|-------|
| Zapier plane does not proxy the app | `verae-access-zapier` | `zap.zapier.georgelambert.org` handles only `POST /v1/timestamp` and drops the API key. STATUS says to point the app there; until it proxies all `/zapier/v1/*` with the key intact, use `mw.zapier.georgelambert.org`. |
| Hash lookup on live chain | Verae API or middleware store | `GET /hashes/{sha256}` is mock-only; live returns `501 NOT_IMPLEMENTED`. Needs a Verae API gap fill or a middleware-side index. |
| `sha256`-only create and metadata on live chain | `verae-middleware` | Accepted by the middleware and honoured by the mock; the live client sends only `data` + `hashAlg` (adapter rule). Decide how the live path handles digest-only registration and public/private metadata. |
| Tree-leaf receipts on wait/status | `verae-middleware` / `verae-archive-aggregator` | The user guide describes `receipts[]`, `itemizedOnMainChain`, `merkleRoot`, `proofOk`; the live wait path does not call the aggregator yet, so those fields are absent. This app's `tree_lookup` sample already names them. |
| Billing-edge composition | `zappier-edge` | Target is Zapier → zappier `/v1/*` (`x-api-key`, metered) → middleware. The edge is loopback-only today and covers only timestamp / hashes / receipts / usage without the `/zapier/v1` prefix. This app already sends `x-api-key` and has `MIDDLEWARE_PATH_PREFIX` for when it lands. |
| Webhook `performList` | `verae-middleware` | No "recent events" endpoint, so the hook triggers return `[]` from `performList` and Zapier uses the static sample. A small list route would give real sample data in the editor. |
### Not planned in this app
- Verae admin HTML, user CRUD, and get-block-by-hash (`/admin/*`, `/auth/users*`).
- Encrypted object storage and shares (`/objects*`, `/shares/*`) — mock-only today; add creates
here once the middleware contract is final.
- Anything that talks to NATS or api.veraetime.net directly.
## Proposed changes to the Verae chain API
Context: a developer asked how to query an original hash, get a receipt back from both the
original submission and later lookups, see every timestamp field and its metadata, and store
metadata (or links to it) in a blockchain timestamp. Against the live OpenAPI
(`docs/00-sources/veraetime-openapi.yaml`) none of that is possible today: lookups are by job ID
only, `POST /api/timestamp` returns just `{ jobId }`, and the request has no metadata field.
The middleware mocks all of it (`MOCK_VERAE=true`) but its live client may only call paths in
the snapshot spec.
### Required on api.veraetime.net
| Ask | Change to the Verae API | Why the chain, not the middleware |
|-----|-------------------------|-----------------------------------|
| Query by original hash | New `GET /api/hashes/{sha256}` → `{ exists, jobId, sha256, timestamp, blockIndex, certificate }`. The chain already records `dataHash` per transaction; it needs an index on it. | Only the chain can find hashes sealed by any submitter. A middleware index only knows what passed through it. |
| Receipt on original submission | Make `POST /api/timestamp` idempotent on the digest: if the hash already exists return `200 { jobId, existing: true, certificate, timestamp, blockIndex }` instead of `202`. Accept a pre-hashed input `{ sha256 }` alongside `{ data }` so clients never ship the document. | Idempotency has to live where the seal lives. Accepting a digest also stops raw data landing in blocks. |
| Receipt on lookup | Have the hash lookup and `GET /api/verify/{jobId}` return the full `StatusResponse` (certificate + block metadata). The shape exists; the lookup just needs to reuse it. | No new data, a consistent response. |
| Metadata in the timestamp | Optional `metadata` object on `TimestampRequest` with a documented size cap, stored with the transaction and returned on status, verify, and hash lookup. Add `metadataHash` as an alternative for callers who want to commit to metadata without publishing it. | Anything the seal must prove existed at that time has to be inside what the chain hashes. Everything else stays off chain. |
### Strongly recommended
- **Certified retrieval receipt** — `GET /api/receipts/{jobId}?format=json|pdf`, where the chain
seals a `document.retrieved` event referencing the original job and returns both. The
middleware mocks this; a chain-signed receipt is what makes it certified.
- **JSON error bodies everywhere** — the spec promises `{ error, code }` but the live service
returns plain text on 401 (`Unauthorized: token is missing`). Every client special-cases it.
- **Completion callback or job list** — an optional `callbackUrl` on create, or
`GET /api/jobs?since=…`. Today every consumer polls `GET /api/status/{jobId}` per job.
### Rules to write into the spec
- Public metadata on chain is immutable and readable by anyone. Private metadata never goes on
chain; only a hash commitment does.
- Define `data` as "bytes to hash" and state whether blocks retain it. The `/admin/*` examples
show blocks carrying a base64 `data` field that decodes to the sample input, which suggests
they do — that matters for privacy and should be confirmed on a live seal.
### What can be done without touching the API
The middleware can index SHA-256 → job ID at submission, store public/private metadata, and
merge both back into `/status/{jobId}` and `/hashes/{sha256}` on the live path (it already does
so in mock). That covers the developer's flow for anything submitted through Verae's own
middleware. It cannot deliver chain-wide lookup, chain-level idempotency, or a chain-signed
receipt — those are the real API asks in the table above.
This Zapier app already sends `sha256`, `publicMetadata`, and `privateMetadata` to the
middleware and exposes Find Timestamp by SHA256, Find Job Verification Details, and Get
Retrieval Receipt, so no app change is needed once either side lands.
UNLICENSED © George Lambert

View file

@ -1,9 +1,9 @@
# verae-zapier-app
**Job:** Full Zapier CLI app: timestamp (async, wait, batch), verify (single, batch), job status (single, batch, verification details), hash / tree-node lookup, usage, retrieval receipt, REST Hooks for `timestamp.completed` and `timestamp.failed`.
**Job:** Full Zapier CLI app: timestamp, wait, verify, batch, job status, hash lookup, REST Hook.
**Expects:** `zmw_` middleware key (today) or zappier portal key after composition; sent as `Authorization: Bearer` and `x-api-key`.
**Expects:** `zmw_` middleware key (today) or zappier key after composition.
**Sends:** HTTPS to middleware `/zapier/v1/*` only.
**Sends:** HTTPS to middleware `/zapier/v1/*`.
**Does not** speak NATS or call `api.veraetime.net`.
**Does not** speak NATS.

View file

@ -1,25 +1,21 @@
/**
* Custom API key auth against the Verae middleware.
*
* Customers get a `zmw_…` key from signup / provisioning. The middleware
* resolves it to a tenant (plan, quotas) and logs in to Verae on their behalf.
* Custom API key auth against Verae middleware.
* @module authentication
*/
const { url } = require('./lib/middleware');
const middlewareBase = () =>
process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
/**
* Zapier connection test — `GET /zapier/v1/auth/me`. Its fields feed
* `connectionLabel`.
* Zapier connection test — validates API key.
* @param {object} z
* @param {object} bundle
*/
const testAuth = async (z, bundle) => {
const response = await z.request({
url: url('/auth/me'),
url: `${middlewareBase()}/zapier/v1/auth/me`,
headers: {
Authorization: `Bearer ${bundle.authData.api_key}`,
'x-api-key': bundle.authData.api_key,
},
});
return response.data;
@ -33,7 +29,8 @@ module.exports = {
type: 'string',
required: true,
label: 'API Key',
helpText: 'Your Verae API key (zmw_…) from portal signup or admin provisioning. Never a Verae username, password, or JWT.',
helpText:
'Your Verae API key (zmw_…) from signup or admin provisioning.',
},
],
test: testAuth,

2
creates/add_numbers.js Normal file
View file

@ -0,0 +1,2 @@
/** Re-export activate-now Add Numbers so this package stays self-contained. */
module.exports = require('../../verae-activate/creates/add_numbers');

View file

@ -1,73 +0,0 @@
/**
* `POST /zapier/v1/status/batch` — status of several jobs at once.
* Modelled as an action because it returns one aggregate result.
* @module creates/batch_status
*/
const { url, statusSample, toStringList } = require('../lib/middleware');
const perform = async (z, bundle) => {
const jobIds = toStringList(bundle.inputData.jobIds);
if (jobIds.length === 0) {
throw new z.errors.Error('Provide at least one job ID.', 'VALIDATION_ERROR', 400);
}
const response = await z.request({
method: 'POST',
url: url('/status/batch'),
body: { jobIds },
});
const map = (response.data && response.data.results) || {};
const results = jobIds.map((id) => ({ id, ...(map[id] || { status: 'unknown' }) }));
const count = (s) => results.filter((r) => r.status === s).length;
return {
jobIds,
results,
total: results.length,
completedCount: count('completed'),
pendingCount: count('pending'),
failedCount: count('failed'),
allCompleted: results.length > 0 && count('completed') === results.length,
};
};
module.exports = {
key: 'batch_status',
noun: 'Job',
display: {
label: 'Get Batch Job Status',
description: 'Retrieves the current status of multiple timestamp jobs in one request.',
},
operation: {
inputFields: [
{
key: 'jobIds',
label: 'Job IDs',
type: 'string',
required: true,
list: true,
helpText: 'One job ID per entry (or one per line), e.g. the Job IDs from "Create Batch Timestamps".',
},
],
perform,
sample: {
jobIds: [statusSample.id, '650e8400-e29b-41d4-a716-446655440001'],
results: [statusSample, { id: '650e8400-e29b-41d4-a716-446655440001', status: 'pending' }],
total: 2,
completedCount: 1,
pendingCount: 1,
failedCount: 0,
allCompleted: false,
},
outputFields: [
{ key: 'jobIds', label: 'Job IDs' },
{ key: 'results', label: 'Results' },
{ key: 'total', label: 'Total', type: 'integer' },
{ key: 'completedCount', label: 'Completed Count', type: 'integer' },
{ key: 'pendingCount', label: 'Pending Count', type: 'integer' },
{ key: 'failedCount', label: 'Failed Count', type: 'integer' },
{ key: 'allCompleted', label: 'All Completed', type: 'boolean' },
],
},
};

View file

@ -1,24 +1,18 @@
/**
* `POST /zapier/v1/timestamp/batch` — several items in one request (paid plans).
* @module creates/batch_timestamp
*/
const { url, hashAlgField, toStringList } = require('../lib/middleware');
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const perform = async (z, bundle) => {
const hashAlg = bundle.inputData.hashAlg || undefined;
const items = toStringList(bundle.inputData.items).map((data) => ({ data, hashAlg }));
if (items.length === 0) {
throw new z.errors.Error('Provide at least one data item to timestamp.', 'VALIDATION_ERROR', 400);
}
const items = (bundle.inputData.items || '')
.split('\n')
.map((s) => s.trim())
.filter(Boolean)
.map((data) => ({ data }));
const response = await z.request({
method: 'POST',
url: url('/timestamp/batch'),
url: `${base()}/zapier/v1/timestamp/batch`,
body: { items },
});
const jobIds = (response.data && response.data.jobIds) || [];
return { ...response.data, jobIds, count: jobIds.length };
return response.data;
};
module.exports = {
@ -26,28 +20,20 @@ module.exports = {
noun: 'Timestamp',
display: {
label: 'Create Batch Timestamps',
description: 'Submits multiple data items for timestamping in one request (paid plans).',
description: 'Submit multiple data lines for timestamping (paid plans).',
},
operation: {
inputFields: [
{
key: 'items',
label: 'Data Items',
label: 'Data Lines',
type: 'text',
required: true,
list: true,
helpText: 'One item per entry (or one per line). Each item becomes its own timestamp job.',
helpText: 'One payload per line.',
},
hashAlgField,
],
perform,
sample: {
jobIds: ['550e8400-e29b-41d4-a716-446655440000', '650e8400-e29b-41d4-a716-446655440001'],
count: 2,
},
outputFields: [
{ key: 'jobIds', label: 'Job IDs' },
{ key: 'count', label: 'Job Count', type: 'integer' },
],
sample: { jobIds: ['id-1', 'id-2'] },
outputFields: [{ key: 'jobIds', label: 'Job IDs' }],
},
};

View file

@ -1,67 +0,0 @@
/**
* `POST /zapier/v1/verify/batch` — verify several certificates at once.
* @module creates/batch_verify
*/
const { url, toStringList } = require('../lib/middleware');
const perform = async (z, bundle) => {
const certificates = toStringList(bundle.inputData.certificates);
if (certificates.length === 0) {
throw new z.errors.Error('Provide at least one certificate to verify.', 'VALIDATION_ERROR', 400);
}
const response = await z.request({
method: 'POST',
url: url('/verify/batch'),
body: { certificates },
});
const results = (response.data && response.data.results) || [];
const validCount = results.filter((r) => r && r.valid === true).length;
return {
results,
total: results.length,
validCount,
invalidCount: results.length - validCount,
allValid: results.length > 0 && validCount === results.length,
};
};
module.exports = {
key: 'batch_verify',
noun: 'Verification',
display: {
label: 'Verify Batch Timestamps',
description: 'Verifies multiple timestamp certificates in one request.',
},
operation: {
inputFields: [
{
key: 'certificates',
label: 'Certificates',
type: 'text',
required: true,
list: true,
helpText: 'One certificate per entry (or one per line). Results come back in the same order.',
},
],
perform,
sample: {
results: [
{ valid: true, timestamp: '2023-01-01T12:00:00Z', blockIndex: 42 },
{ valid: false },
],
total: 2,
validCount: 1,
invalidCount: 1,
allValid: false,
},
outputFields: [
{ key: 'results', label: 'Results' },
{ key: 'total', label: 'Total', type: 'integer' },
{ key: 'validCount', label: 'Valid Count', type: 'integer' },
{ key: 'invalidCount', label: 'Invalid Count', type: 'integer' },
{ key: 'allValid', label: 'All Valid', type: 'boolean' },
],
},
};

View file

@ -1,16 +1,13 @@
/**
* `POST /zapier/v1/timestamp` — submit data or a SHA-256, get a job ID (202).
* A digest that was sealed before returns the original job with `existing: true`.
* @module creates/create_timestamp
*/
const { url, timestampBody, timestampInputFields } = require('../lib/middleware');
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const perform = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: url('/timestamp'),
body: timestampBody(z, bundle.inputData),
url: `${base()}/zapier/v1/timestamp`,
body: {
data: bundle.inputData.data,
hashAlg: bundle.inputData.hashAlg || undefined,
},
});
return response.data;
};
@ -20,21 +17,21 @@ module.exports = {
noun: 'Timestamp',
display: {
label: 'Create Timestamp (Async)',
description:
'Registers data or a SHA-256 digest for blockchain timestamping and returns a job ID right away. Pair with "Timestamp Completed" or "Find Job Status" to get the certificate.',
description: 'Submits data for timestamping and returns a job ID.',
},
operation: {
inputFields: timestampInputFields(),
perform,
sample: {
jobId: '550e8400-e29b-41d4-a716-446655440000',
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
existing: false,
},
outputFields: [
{ key: 'jobId', label: 'Job ID' },
{ key: 'sha256', label: 'SHA256' },
{ key: 'existing', label: 'Already Timestamped', type: 'boolean' },
inputFields: [
{ key: 'data', label: 'Data', type: 'string', required: true },
{
key: 'hashAlg',
label: 'Hash Algorithm',
type: 'string',
required: false,
default: 'SHA256',
},
],
perform,
sample: { jobId: '550e8400-e29b-41d4-a716-446655440000' },
outputFields: [{ key: 'jobId', label: 'Job ID' }],
},
};

View file

@ -1,76 +0,0 @@
/**
* `GET /zapier/v1/receipts/{jobId}` — certified retrieval receipt for a
* completed job (JSON, optionally the PDF as a Zapier file). The middleware
* seals a `document.retrieved` event on each call, so this is an action.
* @module creates/get_receipt
*/
const { url } = require('../lib/middleware');
const perform = async (z, bundle) => {
const jobId = bundle.inputData.jobId;
const receiptUrl = url(`/receipts/${encodeURIComponent(jobId)}`);
const response = await z.request({ url: receiptUrl, params: { format: 'json' } });
const receipt = { id: jobId, ...(response.data || {}) };
if (bundle.inputData.includePdf) {
const pdfRequest = z.request({ url: receiptUrl, params: { format: 'pdf' }, raw: true });
receipt.pdf = await z.stashFile(
pdfRequest,
undefined,
`verae-receipt-${jobId}.pdf`,
'application/pdf',
);
}
return receipt;
};
module.exports = {
key: 'get_receipt',
noun: 'Receipt',
display: {
label: 'Get Retrieval Receipt',
description:
'Fetches the certified retrieval receipt for a completed timestamp job. Fails with NOT_COMPLETE while the job is still pending.',
},
operation: {
inputFields: [
{ key: 'jobId', label: 'Job ID', type: 'string', required: true },
{
key: 'includePdf',
label: 'Include PDF',
type: 'boolean',
required: false,
default: 'false',
helpText: 'Also attach the receipt as a PDF file.',
},
],
perform,
sample: {
id: '550e8400-e29b-41d4-a716-446655440000',
type: 'verae.retrieval-receipt',
format: 'json',
jobId: '550e8400-e29b-41d4-a716-446655440000',
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
timestamp: '2023-01-01T12:05:00Z',
certificate: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
extraSeal: {
event: 'document.retrieved',
retrievedAt: '2023-01-02T09:00:00Z',
tenantId: 'tenant-example',
},
},
outputFields: [
{ key: 'jobId', label: 'Job ID' },
{ key: 'type', label: 'Receipt Type' },
{ key: 'sha256', label: 'SHA256' },
{ key: 'timestamp', label: 'Timestamp', type: 'datetime' },
{ key: 'certificate', label: 'Certificate' },
{ key: 'extraSeal__event', label: 'Seal Event' },
{ key: 'extraSeal__retrievedAt', label: 'Retrieved At', type: 'datetime' },
{ key: 'extraSeal__tenantId', label: 'Tenant ID' },
{ key: 'pdf', label: 'Receipt PDF', type: 'file' },
],
},
};

View file

@ -1,27 +1,15 @@
/**
* `POST /zapier/v1/timestamp/wait` — submit data or a SHA-256 and hold until
* the job is terminal or the middleware's WAIT_TIMEOUT_MS elapses
* (then `status: pending` with the job ID).
* @module creates/timestamp_and_wait
*/
const {
url,
statusOutputFields,
statusSample,
timestampBody,
timestampInputFields,
} = require('../lib/middleware');
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const perform = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: url('/timestamp/wait'),
body: timestampBody(z, bundle.inputData),
url: `${base()}/zapier/v1/timestamp/wait`,
body: {
data: bundle.inputData.data,
hashAlg: bundle.inputData.hashAlg || undefined,
},
});
const status = response.data || {};
const id = status.id || status.jobId;
return { ...status, id, jobId: status.jobId || id };
return response.data;
};
module.exports = {
@ -30,12 +18,37 @@ module.exports = {
display: {
label: 'Create Timestamp and Wait',
description:
'Registers data or a SHA-256 digest and holds the step until the certificate exists. If the chain is slow the step returns status "pending" with the job ID so a later step (or the Timestamp Completed trigger) can finish the Zap.',
'Submits data for blockchain timestamping and waits for the certificate.',
},
operation: {
inputFields: timestampInputFields(),
inputFields: [
{
key: 'data',
label: 'Data',
type: 'string',
required: true,
helpText: 'Content to timestamp on the blockchain.',
},
{
key: 'hashAlg',
label: 'Hash Algorithm',
type: 'string',
required: false,
default: 'SHA256',
},
],
perform,
sample: statusSample,
outputFields: [{ key: 'jobId', label: 'Job ID' }, ...statusOutputFields.slice(1)],
sample: {
id: '550e8400-e29b-41d4-a716-446655440000',
status: 'completed',
result: 'mock-cert-example',
completedAt: '2023-01-01T12:05:00Z',
},
outputFields: [
{ key: 'id', label: 'Job ID' },
{ key: 'status', label: 'Status' },
{ key: 'result', label: 'Certificate' },
{ key: 'completedAt', label: 'Completed At', type: 'datetime' },
],
},
};

View file

@ -1,14 +1,9 @@
/**
* `POST /zapier/v1/verify` — verify one certificate.
* @module creates/verify_timestamp
*/
const { url, verifyOutputFields, verifySample } = require('../lib/middleware');
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const perform = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: url('/verify'),
url: `${base()}/zapier/v1/verify`,
body: { certificate: bundle.inputData.certificate },
});
return response.data;
@ -19,7 +14,7 @@ module.exports = {
noun: 'Verification',
display: {
label: 'Verify Timestamp',
description: 'Verifies the authenticity of a timestamp certificate.',
description: 'Verify a timestamp certificate.',
},
operation: {
inputFields: [
@ -28,11 +23,14 @@ module.exports = {
label: 'Certificate',
type: 'text',
required: true,
helpText: 'The certificate returned when a timestamp job completed.',
},
],
perform,
sample: verifySample,
outputFields: verifyOutputFields,
sample: { valid: true, timestamp: '2023-01-01T12:00:00Z', blockIndex: 42 },
outputFields: [
{ key: 'valid', label: 'Valid', type: 'boolean' },
{ key: 'timestamp', label: 'Timestamp', type: 'datetime' },
{ key: 'blockIndex', label: 'Block Index', type: 'integer' },
],
},
};

View file

@ -1,100 +1,82 @@
/**
* Verae Zapier Platform app definition.
*
* Every operation calls the Verae middleware (`MIDDLEWARE_BASE_URL`,
* `/zapier/v1/*`). The middleware talks to api.veraetime.net; this app never does.
* @module index
*/
const authentication = require('./authentication');
const { describeError } = require('./lib/middleware');
const timestampAndWait = require('./creates/timestamp_and_wait');
const createTimestamp = require('./creates/create_timestamp');
const batchTimestamp = require('./creates/batch_timestamp');
const verifyTimestamp = require('./creates/verify_timestamp');
const batchVerify = require('./creates/batch_verify');
const batchStatus = require('./creates/batch_status');
const getReceipt = require('./creates/get_receipt');
const batchTimestamp = require('./creates/batch_timestamp');
const addNumbers = require('./creates/add_numbers');
const jobStatus = require('./searches/job_status');
const jobVerification = require('./searches/job_verification');
const hashLookup = require('./searches/hash_lookup');
const treeLookup = require('./searches/tree_lookup');
const findUsage = require('./searches/usage');
const timestampCompleted = require('./triggers/timestamp_completed');
const timestampFailed = require('./triggers/timestamp_failed');
/**
* Attach the API key to every outbound request, both ways the stack accepts
* it: the middleware reads `Authorization: Bearer` or `x-api-key`; the
* access planes and the zappier billing edge forward only `x-api-key`.
* Attach middleware API key to every outbound request.
* @param {object} request
* @param {object} _z
* @param {object} bundle
*/
const addApiKey = (request, _z, bundle) => {
request.headers = request.headers || {};
const key = bundle && bundle.authData && bundle.authData.api_key;
if (key) {
if (!request.headers.Authorization) request.headers.Authorization = `Bearer ${key}`;
if (!request.headers['x-api-key']) request.headers['x-api-key'] = key;
}
request.headers.Authorization = `Bearer ${bundle.authData.api_key}`;
return request;
};
/**
* Map middleware errors (`{ error, code, details }`) to Zapier errors.
* Billing errors carry the upgrade URL; 429 becomes a ThrottledError so
* Zapier retries later. Requests made with `skipThrowForStatus` are left alone.
* Map middleware billing errors to Zapier errors.
* @param {object} response
* @param {object} z
*/
const mapMiddlewareErrors = (response, z) => {
if (response.skipThrowForStatus || response.status < 400) {
return response;
if (response.status === 402) {
throw new z.errors.Error(
`${response.data?.error ?? 'Quota exceeded'}. Upgrade at ${response.data?.details?.upgradeUrl ?? response.data?.upgradeUrl ?? 'your billing portal'}.`,
'QuotaExceeded',
402,
);
}
const { message, code, upgradeUrl } = describeError(response);
if (response.status === 429) {
throw new z.errors.ThrottledError(message);
if (response.status === 403 && response.data?.code === 'PLAN_UPGRADE_REQUIRED') {
throw new z.errors.Error(
response.data?.error ?? 'This action requires a paid plan.',
'PlanUpgradeRequired',
403,
);
}
const billingCodes = new Set(['QUOTA_EXCEEDED', 'PLAN_UPGRADE_REQUIRED', 'BATCH_LIMIT_EXCEEDED']);
if (response.status === 402 || billingCodes.has(code)) {
const where = upgradeUrl || 'your billing portal';
throw new z.errors.Error(`${message}. Upgrade at ${where}.`, code, response.status);
}
throw new z.errors.Error(message, code, response.status);
return response;
};
let platformVersion = '15.19.0';
try {
platformVersion = require('zapier-platform-core').version;
} catch {
// optional for unit tests without full install
}
module.exports = {
version: require('./package.json').version,
platformVersion: require('zapier-platform-core').version,
platformVersion,
authentication,
beforeRequest: [addApiKey],
afterResponse: [mapMiddlewareErrors],
triggers: {
[timestampCompleted.key]: timestampCompleted,
[timestampFailed.key]: timestampFailed,
},
creates: {
[addNumbers.key]: addNumbers,
[timestampAndWait.key]: timestampAndWait,
[createTimestamp.key]: createTimestamp,
[batchTimestamp.key]: batchTimestamp,
[verifyTimestamp.key]: verifyTimestamp,
[batchVerify.key]: batchVerify,
[batchStatus.key]: batchStatus,
[getReceipt.key]: getReceipt,
[batchTimestamp.key]: batchTimestamp,
},
searches: {
[jobStatus.key]: jobStatus,
[jobVerification.key]: jobVerification,
[hashLookup.key]: hashLookup,
[treeLookup.key]: treeLookup,
[findUsage.key]: findUsage,
},
};

View file

@ -1,87 +0,0 @@
/**
* Factory for REST Hook triggers backed by the middleware's
* `/webhooks/subscribe` and `/webhooks/unsubscribe` endpoints.
*
* Delivered payload (see verae-middleware inProcessJobPoller / webhookWorker):
* `{ event, jobId, tenantId, status: StatusResponse }`.
* @module lib/hookTrigger
*/
const { url, statusSample } = require('./middleware');
/**
* @param {object} spec
* @param {string} spec.key Zapier trigger key
* @param {string} spec.event middleware event name (timestamp.completed | timestamp.failed)
* @param {string} spec.label
* @param {string} spec.description
* @param {object} [spec.sampleStatus] StatusResponse used in the sample payload
* @returns {object} Zapier trigger definition
*/
const hookTrigger = ({ key, event, label, description, sampleStatus = statusSample }) => {
const performSubscribe = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: url('/webhooks/subscribe'),
body: { targetUrl: bundle.targetUrl, event },
});
return { id: response.data.id, event };
};
const performUnsubscribe = async (z, bundle) => {
const hookId =
bundle.subscribeData && typeof bundle.subscribeData === 'object'
? bundle.subscribeData.id
: bundle.subscribeData;
await z.request({
method: 'DELETE',
url: url('/webhooks/unsubscribe'),
body: { hookId, targetUrl: bundle.targetUrl },
});
return {};
};
/** Zapier dedupes hook items on `id`; the payload only carries `jobId`. */
const perform = async (_z, bundle) => {
const payload = bundle.cleanedRequest || {};
const id = payload.jobId || (payload.status && payload.status.id);
return [{ id, ...payload }];
};
/** The middleware has no "list recent events" endpoint; Zapier falls back to `sample`. */
const performList = async () => [];
return {
key,
noun: 'Timestamp',
display: { label, description },
operation: {
type: 'hook',
perform,
performList,
performSubscribe,
performUnsubscribe,
sample: {
id: sampleStatus.id,
event,
jobId: sampleStatus.id,
tenantId: 'tenant-example',
status: sampleStatus,
},
outputFields: [
{ key: 'id', label: 'Job ID' },
{ key: 'event', label: 'Event' },
{ key: 'jobId', label: 'Job ID' },
{ key: 'tenantId', label: 'Tenant ID' },
{ key: 'status__status', label: 'Status' },
{ key: 'status__result', label: 'Certificate' },
{ key: 'status__error', label: 'Error' },
{ key: 'status__completedAt', label: 'Completed At', type: 'datetime' },
{ key: 'status__sha256', label: 'SHA256' },
{ key: 'status__metadata__blockIndex', label: 'Block Index', type: 'integer' },
],
},
};
};
module.exports = hookTrigger;

View file

@ -1,233 +0,0 @@
/**
* Shared helpers for calling the Verae middleware (`/zapier/v1/*`).
*
* The Zapier app never talks to api.veraetime.net or NATS; the middleware
* (verae-middleware) owns API keys, plans/quotas, job watching, webhooks,
* and the upstream Verae chain client.
* @module lib/middleware
*/
const DEFAULT_BASE_URL = 'http://127.0.0.1:3100';
const DEFAULT_PATH_PREFIX = '/zapier/v1';
/**
* Middleware origin, from MIDDLEWARE_BASE_URL (set per Zapier version with
* `zapier env:set <version> MIDDLEWARE_BASE_URL=https://...`).
* @returns {string} origin without a trailing slash
*/
const baseUrl = () =>
(process.env.MIDDLEWARE_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, '');
/**
* Path prefix in front of every route. The middleware serves `/zapier/v1`;
* a fronting edge may mount the same routes elsewhere, so it is overridable
* with MIDDLEWARE_PATH_PREFIX.
* @returns {string} prefix starting with "/" and without a trailing slash
*/
const pathPrefix = () => {
const raw = process.env.MIDDLEWARE_PATH_PREFIX;
if (raw === undefined || raw === null) return DEFAULT_PATH_PREFIX;
const trimmed = String(raw).trim().replace(/\/+$/, '');
if (!trimmed) return '';
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
};
/**
* Build a middleware URL.
* @param {string} path path starting with "/", e.g. "/timestamp/wait"
* @returns {string}
*/
const url = (path) => `${baseUrl()}${pathPrefix()}${path}`;
/** 64 lowercase/uppercase hex characters. */
const SHA256_RE = /^[A-Fa-f0-9]{64}$/;
/**
* Build the body for `/timestamp` and `/timestamp/wait` from Zapier input.
* Either `data` or a 64-hex `sha256` is required (the middleware hashes data
* itself; sending only the digest is recommended for large files).
* Optional public/private metadata dictionaries are passed through.
* @param {object} z
* @param {object} inputData
* @returns {{ data?: string, sha256?: string, hashAlg?: string, publicMetadata?: object, privateMetadata?: object }}
*/
const timestampBody = (z, inputData) => {
const data = inputData.data === undefined || inputData.data === null ? '' : String(inputData.data);
const sha256 = String(inputData.sha256 || '').trim().toLowerCase();
if (!data && !sha256) {
throw new z.errors.Error('Provide either Data or a SHA256 digest.', 'VALIDATION_ERROR', 400);
}
if (sha256 && !SHA256_RE.test(sha256)) {
throw new z.errors.Error('SHA256 must be 64 hexadecimal characters.', 'VALIDATION_ERROR', 400);
}
const body = { hashAlg: inputData.hashAlg || undefined };
if (data) body.data = data;
if (sha256) body.sha256 = sha256;
const isDict = (v) => v && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0;
if (isDict(inputData.publicMetadata)) body.publicMetadata = inputData.publicMetadata;
if (isDict(inputData.privateMetadata)) body.privateMetadata = inputData.privateMetadata;
return body;
};
/** Input fields shared by Create Timestamp and Create Timestamp and Wait. */
const timestampInputFields = () => [
{
key: 'data',
label: 'Data',
type: 'text',
required: false,
helpText: 'Content to timestamp. The middleware hashes it with SHA-256; only the digest is sealed on chain. Leave blank if you supply a SHA256 below.',
},
{
key: 'sha256',
label: 'SHA256',
type: 'string',
required: false,
helpText: '64 hex characters if you already hashed the file outside Zapier (recommended for large files). Either Data or SHA256 is required.',
},
hashAlgField,
{
key: 'publicMetadata',
label: 'Public Metadata',
dict: true,
required: false,
helpText: 'Key/value pairs stored with the seal and returned on lookups.',
},
{
key: 'privateMetadata',
label: 'Private Metadata',
dict: true,
required: false,
helpText: 'Key/value pairs stored privately with the job (only visible to your account).',
},
];
/**
* Read the middleware's structured error body (`{ error, code, details }`),
* falling back to plain text or a generic message.
* @param {object} response zapier-platform-core response
* @returns {{ message: string, code: string, upgradeUrl?: string, details?: object }}
*/
const describeError = (response) => {
const data = response.data;
if (data && typeof data === 'object' && typeof data.error === 'string') {
const details = data.details && typeof data.details === 'object' ? data.details : undefined;
return {
message: data.error,
code: typeof data.code === 'string' ? data.code : `HTTP_${response.status}`,
upgradeUrl: details?.upgradeUrl ?? data.upgradeUrl,
details,
};
}
let text = '';
try {
text = typeof response.content === 'string' ? response.content.trim() : '';
} catch {
text = '';
}
if (text && text.length <= 500 && !text.startsWith('<')) {
return { message: text, code: `HTTP_${response.status}` };
}
return {
message: `Verae middleware request failed with HTTP ${response.status}`,
code: `HTTP_${response.status}`,
};
};
/**
* Run a lookup request for a Zapier search: 404 means "no match" (empty
* array), other errors are raised with the middleware's message, and a hit
* is returned as a one-element array.
* @param {object} z
* @param {object} requestOptions passed to z.request
* @returns {Promise<object[]>}
*/
const searchOne = async (z, requestOptions) => {
const response = await z.request({ ...requestOptions, skipThrowForStatus: true });
if (response.status === 404) return [];
if (response.status >= 400) {
const { message, code } = describeError(response);
throw new z.errors.Error(message, code, response.status);
}
return [response.data];
};
/**
* Normalise a Zapier `list: true` field or newline-separated text into an
* array of non-empty trimmed strings.
* @param {string[]|string|undefined} value
* @returns {string[]}
*/
const toStringList = (value) => {
const raw = Array.isArray(value) ? value : String(value ?? '').split('\n');
return raw.map((s) => String(s).trim()).filter(Boolean);
};
/** Sample job status as returned by `/status/{jobId}` and `/timestamp/wait`. */
const statusSample = {
id: '550e8400-e29b-41d4-a716-446655440000',
jobId: '550e8400-e29b-41d4-a716-446655440000',
status: 'completed',
result: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
completedAt: '2023-01-01T12:05:00Z',
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
metadata: {
blockIndex: 42,
timestamp: '2023-01-01T12:00:00Z',
certificate: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
},
};
/** Output fields for a job status (nested keys use Zapier's `__` notation). */
const statusOutputFields = [
{ key: 'id', label: 'Job ID' },
{ key: 'status', label: 'Status' },
{ key: 'error', label: 'Error' },
{ key: 'result', label: 'Certificate' },
{ key: 'completedAt', label: 'Completed At', type: 'datetime' },
{ key: 'sha256', label: 'SHA256' },
{ key: 'metadata__blockIndex', label: 'Block Index', type: 'integer' },
{ key: 'metadata__timestamp', label: 'Block Timestamp', type: 'datetime' },
{ key: 'metadata__certificate', label: 'Certificate (metadata)' },
];
/** Sample verification result from `/verify`. */
const verifySample = { valid: true, timestamp: '2023-01-01T12:00:00Z', blockIndex: 42 };
/** Output fields for a verification result. */
const verifyOutputFields = [
{ key: 'valid', label: 'Valid', type: 'boolean' },
{ key: 'timestamp', label: 'Timestamp', type: 'datetime' },
{ key: 'blockIndex', label: 'Block Index', type: 'integer' },
];
/** Hash-algorithm input shared by the timestamp actions. */
const hashAlgField = {
key: 'hashAlg',
label: 'Hash Algorithm',
type: 'string',
required: false,
default: 'SHA256',
helpText: 'Hash algorithm for the chain. Defaults to SHA256.',
};
module.exports = {
DEFAULT_BASE_URL,
DEFAULT_PATH_PREFIX,
SHA256_RE,
baseUrl,
describeError,
hashAlgField,
pathPrefix,
searchOne,
statusOutputFields,
statusSample,
timestampBody,
timestampInputFields,
toStringList,
url,
verifyOutputFields,
verifySample,
};

819
package-lock.json generated
View file

@ -1,819 +0,0 @@
{
"name": "verae-zapier",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "verae-zapier",
"version": "1.0.0",
"dependencies": {
"zapier-platform-core": "15.19.0"
},
"engines": {
"node": ">=18",
"npm": ">=5.6.0"
}
},
"node_modules/@types/node": {
"version": "20.19.43",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@zapier/secret-scrubber": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@zapier/secret-scrubber/-/secret-scrubber-1.1.6.tgz",
"integrity": "sha512-ETblIxh6lnkVHjVqQ8XVB2Ymeo7RudR7kIiZylXtYXAfpmGLXIzbk1h9fjF/SuO21k7jdxaMBaBdqUCBHv8+og==",
"license": "ISC",
"dependencies": {
"create-hash": "1.2.0",
"lodash.isplainobject": "4.0.6"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"license": "MIT",
"dependencies": {
"possible-typed-array-names": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"license": "MIT"
},
"node_modules/call-bind": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
"integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"get-intrinsic": "^1.3.0",
"set-function-length": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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/cipher-base": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz",
"integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.4",
"safe-buffer": "^5.2.1",
"to-buffer": "^1.2.2"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"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/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"license": "MIT",
"dependencies": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
"md5.js": "^1.3.4",
"ripemd160": "^2.0.1",
"sha.js": "^2.4.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dotenv": {
"version": "12.0.4",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-12.0.4.tgz",
"integrity": "sha512-oWdqbSywffzH1l4WXKPHWA0TWYpqp7IyLfqjipT4upoIFS0HPMqtNotykQpD4iIg0BqtNmdgPCh2WMvMt7yTiw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"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/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.2",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"license": "MIT",
"dependencies": {
"is-callable": "^1.2.7"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 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-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"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/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hash-base": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
"integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "^2.3.8",
"safe-buffer": "^5.2.1",
"to-buffer": "^1.2.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/hasown": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"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/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"license": "MIT",
"dependencies": {
"which-typed-array": "^1.1.16"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/jsonschema": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.2.tgz",
"integrity": "sha512-iX5OFQ6yx9NgbHCwse51ohhKgLuLL7Z5cNOeZOPIlDUtAMrxlruHLzVZxbltdHE5mEDXN+75oFOwq6Gn0MZwsA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=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/md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"license": "MIT",
"dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
"safe-buffer": "^5.1.2"
}
},
"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/node-abort-controller": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
"license": "MIT"
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"license": "Apache-2.0",
"engines": {
"node": "*"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/ripemd160": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
"integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
"license": "MIT",
"dependencies": {
"hash-base": "^3.1.2",
"inherits": "^2.0.4"
},
"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/semver": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz",
"integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==",
"license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/sha.js": {
"version": "2.4.12",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
"integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
"license": "(MIT AND BSD-3-Clause)",
"dependencies": {
"inherits": "^2.0.4",
"safe-buffer": "^5.2.1",
"to-buffer": "^1.2.0"
},
"bin": {
"sha.js": "bin.js"
},
"engines": {
"node": ">= 0.10"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/to-buffer": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
"integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
"license": "MIT",
"dependencies": {
"isarray": "^2.0.5",
"safe-buffer": "^5.2.1",
"typed-array-buffer": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/to-buffer/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"license": "MIT"
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.3",
"es-errors": "^1.3.0",
"is-typed-array": "^1.1.14"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT",
"optional": true
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/which-typed-array": {
"version": "1.1.22",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz",
"integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.9",
"call-bound": "^1.0.4",
"for-each": "^0.3.5",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"license": "ISC"
},
"node_modules/zapier-platform-core": {
"version": "15.19.0",
"resolved": "https://registry.npmjs.org/zapier-platform-core/-/zapier-platform-core-15.19.0.tgz",
"integrity": "sha512-KWyvuZ+QnooOknFl3AW6HObbXdz1FZYclN46EwUeQEd1xIjHwr+WdQwOYPJJM6jwSBkh0Fb7MMVhZQQ0VmPlCg==",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@zapier/secret-scrubber": "^1.1.1",
"bluebird": "3.7.2",
"content-disposition": "0.5.4",
"dotenv": "12.0.4 ",
"form-data": "4.0.0",
"lodash": "4.17.21",
"mime-types": "2.1.35",
"node-abort-controller": "3.1.1",
"node-fetch": "2.6.7",
"oauth-sign": "0.9.0",
"semver": "7.5.2",
"zapier-platform-schema": "15.19.0"
},
"engines": {
"node": ">=16",
"npm": ">=5.6.0"
},
"optionalDependencies": {
"@types/node": "^20.3.1"
}
},
"node_modules/zapier-platform-schema": {
"version": "15.19.0",
"resolved": "https://registry.npmjs.org/zapier-platform-schema/-/zapier-platform-schema-15.19.0.tgz",
"integrity": "sha512-c3lkrIm3/KaAOXGgayhQtXiH7ODtn1lxBTK5marU30yuyO+7WMCpxW+H79yVrfeiDyR7Dhy7LyKtoKw7pEWq+Q==",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"jsonschema": "1.2.2",
"lodash": "4.17.21"
}
}
}
}

View file

@ -1,11 +1,10 @@
{
"name": "verae-zapier",
"version": "1.1.0",
"version": "1.0.0",
"description": "Zapier CLI app for Verae Timestamping via middleware",
"main": "index.js",
"scripts": {
"test": "node --test test/*.test.js",
"test:e2e": "node --test test/e2e.test.js"
"test": "node --test test/**/*.test.js"
},
"engines": {
"node": ">=18",

View file

@ -1,45 +1,34 @@
/**
* `GET /zapier/v1/hashes/{sha256}` — find an existing timestamp by the
* SHA-256 of its data. 404 means no match.
* @module searches/hash_lookup
*/
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const { url, searchOne } = require('../lib/middleware');
const perform = (z, bundle) =>
searchOne(z, { url: url(`/hashes/${encodeURIComponent(bundle.inputData.sha256)}`) });
const perform = async (z, bundle) => {
try {
const response = await z.request({
method: 'GET',
url: `${base()}/zapier/v1/hashes/${encodeURIComponent(bundle.inputData.sha256)}`,
});
return [response.data];
} catch (err) {
if (err.status === 404) return [];
throw err;
}
};
module.exports = {
key: 'hash_lookup',
noun: 'Timestamp',
display: {
label: 'Find Timestamp by SHA256',
description: 'Looks up an existing timestamp on the main Verae chain by the SHA256 hex digest of its data.',
description: 'Looks up an existing timestamp for a SHA256 hex digest (mock/middleware).',
},
operation: {
inputFields: [
{
key: 'sha256',
label: 'SHA256',
type: 'string',
required: true,
helpText: '64 hex characters.',
},
{ key: 'sha256', label: 'SHA256', type: 'string', required: true },
],
perform,
sample: {
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
exists: true,
jobId: '550e8400-e29b-41d4-a716-446655440000',
timestamp: '2023-01-01T12:05:00Z',
status: 'completed',
},
outputFields: [
{ key: 'sha256', label: 'SHA256' },
{ key: 'exists', label: 'Exists', type: 'boolean' },
{ key: 'jobId', label: 'Job ID' },
{ key: 'timestamp', label: 'Timestamp', type: 'datetime' },
{ key: 'status', label: 'Status' },
],
},
};

View file

@ -1,26 +1,33 @@
/**
* `GET /zapier/v1/status/{jobId}` — 404 means no such job (empty result).
* @module searches/job_status
*/
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
const { url, searchOne, statusOutputFields, statusSample } = require('../lib/middleware');
const perform = (z, bundle) =>
searchOne(z, { url: url(`/status/${encodeURIComponent(bundle.inputData.jobId)}`) });
const perform = async (z, bundle) => {
const response = await z.request({
url: `${base()}/zapier/v1/status/${encodeURIComponent(bundle.inputData.jobId)}`,
});
return [response.data];
};
module.exports = {
key: 'job_status',
noun: 'Job',
display: {
label: 'Find Job Status',
description: 'Looks up a timestamp job by its job ID.',
description: 'Look up a timestamp job by ID.',
},
operation: {
inputFields: [
{ key: 'jobId', label: 'Job ID', type: 'string', required: true },
],
perform,
sample: statusSample,
outputFields: statusOutputFields,
sample: {
id: '550e8400-e29b-41d4-a716-446655440000',
status: 'completed',
result: 'mock-cert',
},
outputFields: [
{ key: 'id', label: 'Job ID' },
{ key: 'status', label: 'Status' },
{ key: 'result', label: 'Result' },
],
},
};

Some files were not shown because too many files have changed in this diff Show more