feat(pg-node): add Node.js example using @e4a/pg-js#46
Conversation
Mirrors the pg-sveltekit "Informatierijk notificeren" flow (citizen exact-email recipient + organisation email-domain recipient) but as a plain Node CLI script — drop-in starting point for backend integrations that need to encrypt and upload from a server runtime. Two modes: npm run send # encrypt + upload + ask Cryptify to mail recipients npm run upload # encrypt + upload silently, return only the UUID Configuration via .env (loaded with Node's --env-file-if-exists). Defaults to staging; flip PG_CRYPTIFY_URL to production for real mail delivery. The package depends on @e4a/pg-js via "file:../../postguard-js" so local SDK changes are picked up immediately after a rebuild — useful while the non-browser-runtime support work is unreleased. Swap to a "^X.Y.Z" version once the SDK ships to npm.
There was a problem hiding this comment.
Code review
Stage 1 code-review findings were not available to this reviewer, so this review is rule-compliance focused. Spot-check of the diff itself: clean small change. New pg-node/ sub-project, ~380 lines, mirrors the pg-sveltekit "Informatierijk notificeren" flow as a CLI. Code is straightforward: env-driven config, two modes (send / upload-only), in-memory demo files fallback, abort handling on SIGINT, identical staging-detection heuristic to pg-sveltekit. PR body documents real end-to-end verification (3 staging UUIDs). Conventional-commit title, repo-root README updated to list the new sub-project plus a Development section pointing at the example.
Rule compliance
Checked the diff against the dobby-memory rule set and the postguard-examples repo notes. No blocking violations. A few small stylistic notes from writing-rules flagged in inline comments below — none are merge-blockers, and one is arguably load-bearing (the staging-doesn't-send-email callout has to read as a warning).
standardized-readmes: not applicable — sub-project READMEs aren't held to the repo-README template (matches pg-sveltekit / pg-dotnet / pg-manual conventions).staging-cryptify-detection: matches the documented pattern (new URL(url).hostname.toLowerCase().includes('staging')inside try/catch). The download-URL branching (staging.postguard.euvspostguard.eu) is the "if a separate staging download viewer is ever stood up" follow-up the memory note anticipated — good.conventional-commit-pr-titles:feat(pg-node): ...✓cross-repo-link-format:encryption4all/postguard-js#76auto-links via the org-prefixed form ✓pr-close-issue-keywords: no issue to close (this is a new feature referencing an upstream SDK PR, not resolving a tracker issue).tests-required-on-fixes: not applicable — new example, not a fix; repo has no test suite by design (example/reference code only).typo-sweep-template: no hits.check-dep-is-used-before-bumping: not a bump; the one new dep (@e4a/pg-js) is used insrc/encryption.mjs.never-self-merge-prs: respected (this review does not merge).- Memory note refresh:
repos/postguard-examples/notes.mdsays "No .github/workflows/" butpr-title.ymlis now present in the repo. That memory note is stale; will update separately, not a PR concern.
Approving.
|
|
||
| 1. **Send** (`npm run send`) — encrypts the input files for a citizen (exact email) and an organisation (email domain), uploads to Cryptify, and asks Cryptify to email each recipient a download link. | ||
| 2. **Upload-only** (`npm run upload`) — same encryption + upload, but silent. Cryptify returns a UUID you can distribute through some other channel. | ||
|
|
There was a problem hiding this comment.
[Rule: writing-rules] Minor: the **Send** (...) — encrypts ... / **Upload-only** (...) — same encryption ... pattern is the "Inline header + colon/dash + sentence" list (writing-rules #48) plus the em-dash-overuse tell (#40). Same idea would read cleaner as plain sentences or a small table. Not a blocker — calling it out so it doesn't get copy-pasted into future example READMEs.
| The `package.json` depends on the SDK via `file:../../postguard-js`, so any local changes you make to the SDK take effect after `cd ../../postguard-js && npm run build` followed by `cd ../postguard-examples/pg-node && npm install`. Swap that to `"^X.Y.Z"` once a real release is published. | ||
|
|
||
| ## Run | ||
|
|
There was a problem hiding this comment.
[Rule: writing-rules] Em-dash count is on the high side for a short README (~10 across 75 lines). A few replace cleanly with a period or parentheses (e.g. Mirrors the pg-sveltekit example's flow — drop-in starting point... → just two sentences). Stylistic only.
| import { PostGuard } from '@e4a/pg-js'; | ||
| import { PKG_URL, CRYPTIFY_URL } from './config.mjs'; | ||
|
|
||
| const pg = new PostGuard({ pkgUrl: PKG_URL, cryptifyUrl: CRYPTIFY_URL }); |
There was a problem hiding this comment.
Observation, not a rule violation: new PostGuard({ pkgUrl, cryptifyUrl }) runs at module import time, which means import from this file in a context where PKG_URL/CRYPTIFY_URL aren't yet set would silently bind to defaults. Fine for the CLI-only usage here; worth keeping in mind if this ever gets refactored into a library.
| try { | ||
| return new URL(url).hostname.toLowerCase().includes('staging'); | ||
| } catch { | ||
| return false; |
There was a problem hiding this comment.
Matches the documented staging-cryptify-detection pattern from the pg-sveltekit example — same new URL(url).hostname.toLowerCase().includes('staging') inside try/catch with false fallback. Good consistency.
Swaps the temporary file:../../postguard-js link for the published ^1.9.0 release on npm. Bumps the example's engines.node to >=22 to match the SDK. README updated accordingly. Verified end-to-end against staging.postguard.eu — real UUID returned (bb90cb02-b27c-4b0d-a245-4acdb16056e1).
|
/dobby review this pr |
|
On it, @rubenhensen! This agent is most eager to review the new pg-node example PR — checking it over now. |
PlanRe-reviewing on top of the new chore commit (2bf7463) — swaps Checks done so far on Node v22.22.2:
Two stale references the bump missed (non-blocking, doc-only):
Will flag both as inline review comments and approve (the prior bot review already approved the feat commit; the chore commit is mechanically sound, the two refs are nits, not blockers). |
There was a problem hiding this comment.
Re-reviewed after the chore commit that swapped the file:link for @e4a/pg-js ^1.9.0 and bumped engines.node to >=22.
Verification on Node v22.22.2:
@e4a/pg-js@1.9.0published with matchingengines.node: ">=22"npm installresolves clean; live import returns the expected SDK surface (PostGuard,recipient.email/emailDomain,sign.apiKey,encrypt)node --checkpasses on all .mjs files- Missing
PG_API_KEYboundary still exits with code 2 and a clear message - CI green, conventional title (prior workflow run passed)
Two stale 20.6+ references the bump missed — flagged inline. Not blockers; both are doc-only and the example still runs correctly without fixing them. If you'd prefer to fold them into a tiny follow-up commit before merging, the two lines are noted.
Prior inline threads (em-dash/inline-header style, module-level singleton observation, staging-detection consistency) are unchanged by this commit. None are blockers either; the style nits stand for future copy-paste hygiene but are not worth blocking a working example.
Approving.
|
|
||
| ### Node.js example | ||
|
|
||
| Requires Node.js 20.6+ and a PostGuard API key. |
There was a problem hiding this comment.
Stale after the engines bump. pg-node/package.json now requires node >= 22, but the root README still advertises 20.6+. Two consistent options: bump this to Node.js 22+ to match the sub-project's engines, or leave it at 20.6 with a sentence clarifying that pg-node specifically needs 22+. Recommend the former — simpler and matches pg-node/README.md:16.
| @@ -0,0 +1,35 @@ | |||
| // Env-driven config. Run with `node --env-file=.env index.mjs` to load | |||
| // from .env (Node 20.6+). Mirrors pg-sveltekit's config.ts. | |||
There was a problem hiding this comment.
Also stale: the comment still says (Node 20.6+) and references --env-file=, but package.json scripts actually use --env-file-if-exists (added in Node 21.7) and engines.node is now >=22. Suggest: // from .env (Node 22+). Mirrors pg-sveltekit's config.ts. and drop the node --env-file=... snippet (scripts already wire this up).
Follow-up to dobby's review on the (already-merged) #46: - Root README and pg-node/src/config.mjs still referenced "Node 20.6+" after the engines bump to >=22 in #46's chore commit. - pg-node/src/config.mjs comment referenced `--env-file=` but the npm scripts use `--env-file-if-exists`. - pg-node/README "Two modes" list used the inline-header + em-dash pattern flagged by writing-rules; converted to a small table. - Trimmed a few stylistic em-dashes (lead paragraph, prerequisites, staging note, SDK-mapping paragraph). Kept the bold "does not actually deliver notification emails" callout — that one is load-bearing. No behaviour change; verified with `node --check` on all .mjs files and a live `npm run upload` to staging (UUID 6e22edaf-…).
Summary
Adds a new sub-project
pg-node/— a plain Node.js CLI example showing how to use@e4a/pg-jsfrom a server runtime. Mirrors the pg-sveltekit "Informatierijk notificeren" flow (citizen exact-email recipient + organisation email-domain recipient).Two modes:
npm run send— encrypt + upload + ask Cryptify to mail recipientsnpm run upload— encrypt + upload silently, return only the UUIDConfiguration via
.env(Node's built-in--env-file-if-exists), defaulting to staging. No bundler, no TypeScript build step — plain ESM (.mjs), runnable as soon asnpm installfinishes.Why now
A partner integration (Bereken je Recht) is wiring
@e4a/pg-jsinto a Node backend and hit several non-browser-runtime issues — see encryption4all/postguard-js#76 for the SDK-side fixes. This example serves as the known-good baseline for those use cases.SDK dependency
package.jsonpins"@e4a/pg-js": "file:../../postguard-js"so local SDK changes are picked up immediately. Once encryption4all/postguard-js#76 merges and a release ships to npm, I'll swap this to"^X.Y.Z"in a follow-up commit. The README already explains the temporary file: link.Verification
Ran end-to-end against
pkg.staging.postguard.eu+storage.staging.postguard.euin both modes, three real Cryptify UUIDs returned (last:babbfce1-2642-42c7-bccf-a10a92f8881a). Verified.envloading and explicit env-var loading both work.Test plan
npm installresolves the localfile:linknpm run sendreturns a real Cryptify UUIDnpm run uploadreturns a real Cryptify UUID.envfile is loaded correctly via--env-file-if-existsfile:link to^X.Y.Z