Commit graph

16 commits

Author SHA1 Message Date
4c4d52e3de postprocess-openapi: strip empty required: [] arrays
oatpp 1.3 emits `"required": []` on schemas with no required fields.
OpenAPI 3.0.x rejects this with "must NOT have fewer than 1 items",
which silently breaks strict consumers. Orval's typed fetch client
swallowed it (the violation surfaces only as a non-fatal warning), but
Orval's Zod generator (added downstream in fewo-webapp's #469 increment 2)
fails hard:

  must NOT have fewer than 1 items at /components/schemas/<DTO>/required
  must have required property '$ref'                  at /components/schemas/<DTO>
  must match exactly one schema in oneOf              at /components/schemas/<DTO>

83 of fewo-webapp's 198 DTOs trip this. Stripping the empty array (absence
of the keyword has the same semantics) is the spec-compliant fix.

Bumps to v0.4.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 21:22:30 +02:00
5ee6894916 #7: Add tests/e2e/ for the initial password-setup flow (Option A1)
Closes the integration gap that let two prior regressions ship:
  1. oatpp-authkit query-string 401 (v0.3.3 / commit 46971ac)
  2. VITE_BASE blank page (v0.3.6 / commit b1a13b8)

A1 scope: skips the host-provisioning side of new-project.sh (root,
systemd, Apache, Forgejo). Instead clones webapp-template into a tmp
dir, builds with VITE_BASE pinned to /projects/tmp-foo/, boots the
binary on an ephemeral port, fronts it with an in-process
PrefixStrippingProxy that mirrors the production Apache vhost. Tests
then drive the same flow a real user would.

Files:
- tests/e2e/proxy.py — stdlib-only reverse proxy (~100 LOC,
  ThreadingHTTPServer + urllib). Strips the /projects/<name>/ prefix
  and sets X-Forwarded-Prefix exactly like Apache's ProxyPass.
- tests/e2e/conftest.py — webapp_template_src / built_webapp /
  boot_app / proxy / admin_token fixtures. Honours
  WEBAPP_TEMPLATE_DIR + WEBAPP_TEMPLATE_BUILD_DIR env vars so CI can
  point at a pre-built tree to skip the build step.
- tests/e2e/test_password_setup.py — three assertions per #7:
    - /set-password?token=… returns HTML, not JSON 401
    - every <script src>/<link href> resolves through the prefix
    - /api/* still returns JSON 401 (sanity-check negotiation)

No Selenium dependency — the assertions are HTTP-level and reliable
in CI without a Chrome/Geckodriver setup. Selenium can be added later
for actual form-submission coverage if needed.

Test runs are skipped automatically when webapp-template source is
absent, so the suite is safe to drop into any pytest invocation.

Closes #7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:23:30 +02:00
fd451fd452 #5: createCoreFetch default responseShape flipped to 'body'
Orval's `client: 'fetch'` (with the no-`includeHttpResponseReturnType`
default) emits mutator signatures expecting the parsed body — so making
`'body'` the default means the no-arg `createCoreFetch()` form is the
right answer for the typical Orval-driven derived project. Callers that
need Response metadata opt in via `responseShape: 'wrapped'`.

Aligns README + docstring + the inline default. Tests updated to assert
default-`'body'` and explicit-`'wrapped'`.

Bump to 0.4.0 (BREAKING — default flipped). Downstream consumers on the
default need to either (a) add explicit `responseShape: 'wrapped'` to
preserve old behaviour, or (b) migrate callers to the body shape.
fewo-webapp's existing `responseShape: 'body'` override is now redundant
and will be dropped in a follow-up.

Closes #5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:47:40 +02:00
90c5ca2248 #4: vitest harness + suites for createCoreFetch / createI18n / useI18nStore
Bootstrap vitest with jsdom + @testing-library/react. 25 tests covering:
- core-fetch.test.ts (14): wrapped vs body shape, 204, non-JSON 2xx text,
  401/409 hooks (incl. decorate), formatError override, network failure +
  onNetworkFailure, sync queue + non-mutating bypass, request shape
  (X-Requested-With, credentials: include), baseUrl trailing-slash strip.
- i18n.test.ts (10): resolver chain (locale-tone → locale → default →
  raw), once-per-(key,locale,tone) onMiss firing + flip on locale change,
  subscribe/notify with same-value no-op + unsubscribe, getSnapshot.
- i18n-react.test.tsx (1): useI18nStore re-renders on locale change.

prepare: drops `|| true` per audit — `tsc && vitest run` so publish is
gated on tests passing.

Bump to 0.3.7.

Closes #4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:45:31 +02:00
f4f9289bdc gitignore: drop accidentally-committed __pycache__/.pytest_cache
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:38:32 +02:00
b3b2903c75 #3: inject-hashed-filenames.py — tag-aware HTML rewrite
Replace substring `html.replace(old_src, new_src)` with a regex anchored
to <script src="…"> / <link href="…"> attribute values. Inert occurrences
in comments, JSON literals, or unrelated attributes are left alone.

Loud warning (stderr) when zero matches occur — previously the script
silently skipped a typo'd old_src.

Also rewrites <link href> in the same pass so adjacent CSS hashing doesn't
need a follow-up edit.

Tests: tests/test_inject_hashed_filenames.py covers happy path (both quote
styles, extra attributes), inert-substring cases (comment, JSON literal,
data-attr, anchor href), and link-href rewriting.

Closes #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:38:19 +02:00
b1a13b83fd #6: Use Vite's loadEnv() for VITE_BASE so .env.production actually wins
defineAdminConfig / defineGuestConfig were reading process.env.VITE_BASE,
but Vite does NOT populate process.env from .env files at config-evaluation
time — those go into import.meta.env for the client bundle only. So the
VITE_BASE that new-project.sh writes to frontend/.env.production was
silently ignored, base fell back to '/', and SPA assets 404'd behind the
Apache /projects/<name>/ proxy prefix (blank page on every public route).

Switch both factories to Vite's defineConfig + loadEnv pattern. A
process.env.VITE_BASE override still wins so CI invocations that
explicitly export the variable keep working.

Bumps to 0.3.6.

Closes #6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:36:23 +02:00
5b0bec8850 #2: fetch-openapi.sh — pass $OUT via sys.argv, not f-string in heredoc
Closes #2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:34:02 +02:00
Uwe Schuster
b14b8188fe v0.3.5: readBody tries .json() without clone() first
Some test mocks expose .json() but not .clone() — cloning throws on
object mocks. Try .json() directly, fall back to .text(). Real Response
objects are unaffected (calling .json() twice would throw, but we only
call it once since we're on the error path).
2026-04-21 22:33:01 +02:00
Uwe Schuster
0673c4c0d8 v0.3.4: error path prefers .json() with .text() fallback; always decorate 409
Matches the common test pattern of mocking .json() for error responses
(vitest helpers often do that by default and don't bother with .text()).
The error path now clones the response, tries .json() first, and falls
back to .text() on parse failure. Populates both text and json for
formatError callers.

409 errors are always decorated with .status=409 and .body=<json>, even
when the consumer doesn't provide on409 — conflict resolvers at call
sites can still reach the payload.

Default 409 message is the body's .message field if present; otherwise
falls through to the usual format.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:31:56 +02:00
Uwe Schuster
84693a7af5 v0.3.3: resolve fetch at call time, not factory-creation
Tests that vi.stubGlobal('fetch', ...) AFTER module import (standard
vitest pattern) couldn't see their stub because the factory captured
globalThis.fetch at createCoreFetch() time. Switch to a thin wrapper
that does the lookup per call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:29:54 +02:00
Uwe Schuster
de150e790d v0.3.2: createCoreFetch — responseShape + network-failure + formatError
New options to fit fewo's core-fetch contract:
  responseShape: 'wrapped' | 'body'   — 'body' matches orval's
                                        includeHttpResponseReturnType:false
  onNetworkFailure(req, error)        — return a value to swallow fetch()
                                        rejection; enables online-first +
                                        enqueue-on-offline pattern (fewo)
  formatError(req, resp, text, json)  — override default error message
                                        format (needed for localized 401/403
                                        messages, conflict-body decoration)

409 errors throw an Error decorated with .status = 409 and .body = parsed
JSON so conflict resolvers at call sites can still inspect them.

Backwards-compatible: new options default to prior behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:27:30 +02:00
Uwe Schuster
b2b598c6c8 v0.3.1: ship compiled dist/ so npm git installs work without TS stripping
Consumers installing via 'git+http://.../webapp-scaffold.git#<tag>' hit
Node's ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING because .ts files
under node_modules can't be loaded by vite.config.ts loaders / node's
default loader.

Add tsconfig.json + 'prepare: tsc' (runs on git install). Emit .js + .d.ts
into dist/. package.json exports point at dist/; .ts remains in src/ for
direct-source consumers (e.g. monorepo setups). 'dist/' is gitignored —
it's a build artifact, populated at install time.

Version bump to 0.3.1 since this is a patch on the already-released
0.3.0 ABI (no API changes, just packaging).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:23:36 +02:00
Uwe Schuster
ed9863c037 v0.3.0: i18n system (locale × tone) with fallback chain
createI18n({ bundles, defaultLocale, defaultTone, initialLocale,
initialTone, onMiss }) → { t, locale, tone, setLocale, setTone,
subscribe, getSnapshot }

Resolution order:
    <cur-locale>-<cur-tone>
  → <cur-locale>
  → <default-locale>-<default-tone>
  → <default-locale>
  → raw key (onMiss logs first occurrence)

Typed keys via keyof on the caller-supplied Bundle generic. Tone
bundles are Partial<B> so overrides only need the strings that differ
from the locale bundle — the defaultLocale bundle is the only one
expected to be complete.

React bindings isolated in src/i18n-react.ts so the core ships without
a React peer dep. SSR-safe: constructor takes initial locale/tone
rather than reading from window.

Typecheck clean under strict tsc (ES2020/DOM).

Closes fewo-webapp#416

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:12:18 +02:00
Uwe Schuster
69aec0f86c v0.2.0: add createCoreFetch factory
Orval-mutator factory for derived projects. Baseline behaviour
(credentials, X-Requested-With, 204/JSON handling) baked in; four
hooks let consumers wire the app-specific concerns without forking:

  baseUrl      — Apache-proxy prefix (empty for root-hosted apps)
  syncTables   — table names that route mutations through a sync queue
  onEnqueue    — queue callback (returns true ⇒ skip network, 202 back)
  on401        — session-invalidation redirect
  on409        — conflict resolver (return value swallows the error)
  fetchImpl    — test injection

Typechecks clean with tsc --strict against ES2020/DOM lib. Exported as
'@uschuster/webapp-scaffold/core-fetch' so consumers import only what
they need.

Closes fewo-webapp#415

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:09:08 +02:00
Uwe Schuster
55968fbd73 v0.1.0: fetch/postprocess/inject scripts + vite config factories
Bootstraps the shared frontend build glue for webapp-template-derived
projects:

  bin/fetch-openapi.sh         — pull Swagger JSON from a running backend
  bin/postprocess-openapi.py   — fix oatpp 1.3 rough edges before orval
  bin/inject-hashed-filenames.py — rewrite HTML tags, config-driven
  src/vite-config.ts           — defineAdminConfig / defineGuestConfig
  templates/orval.config.template.ts — starting point for derived repos

Package name @uschuster/webapp-scaffold. Consumed as a devDependency
through the internal Forgejo npm registry; binaries exposed for use in
package.json scripts. createCoreFetch + i18n deferred to v0.2 / v0.3.

Closes fewo-webapp#414

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:06:58 +02:00