feat: @gsc/web-kit v0.1.0 — Phase 1 scaffold

The kit. Drop into any GSC Next.js frontend; everything that's not
domain content lives here. Wraps @limitless/ui primitives with the
app-shaped patterns we keep reimplementing: layout, auth, data
display, forms, feedback, navigation.

Phase 1 ships the package skeleton:

- package.json with 14 sub-exports (./layout · ./auth · ./auth/server
  · ./auth/middleware · ./shell · ./shell/server · ./data · ./forms ·
  ./feedback · ./navigation · ./api · ./utils + the root and ./css).
- Empty module stubs so the import map resolves while later phases
  fill in real surface area.
- Canonical CSS bundle at @gsc/web-kit/css — all.min.css +
  sidebar-overrides.css + the seven layout-3 background images,
  copied from chronos and committed in one place so no app has to
  ship the 1MB sidecar on its own anymore.
- tsc-based build + a postbuild script that mirrors @limitless/ui:
  emits .js + .d.ts, copies styles/, rewrites bare ESM imports to
  include .js extensions.
- Peer deps on next, react, react-dom, bootstrap.
- Hard deps on @limitless/ui (file: dep), next-auth, next-intl, zod.

Build verified: tsc emits, all 14 export paths resolve under dist/.
No functional code yet — Phase 2 lands AppLayout / createAuth /
fetchShellConfig and the gscCRM pilot cuts over.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude
2026-05-11 00:09:36 +02:00
commit 957880e5c5
29 changed files with 34491 additions and 0 deletions

65
scripts/postbuild.cjs Normal file
View File

@@ -0,0 +1,65 @@
/* Post-build: copy CSS + image assets into dist/, then fix ESM imports.
*
* tsc only emits .js + .d.ts. The CSS lives in src/styles/ alongside an
* `index.css` that @imports the other sheets; we copy the whole tree
* to dist/ so `@gsc/web-kit/css` resolves correctly.
*/
const fs = require("fs");
const path = require("path");
const root = path.join(__dirname, "..");
const distDir = path.join(root, "dist");
const srcStyles = path.join(root, "src", "styles");
const distStyles = path.join(distDir, "styles");
// 1. Mirror src/styles/ → dist/styles/ (CSS + images).
function copyDir(src, dst) {
fs.mkdirSync(dst, { recursive: true });
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
const s = path.join(src, entry.name);
const d = path.join(dst, entry.name);
if (entry.isDirectory()) copyDir(s, d);
else fs.copyFileSync(s, d);
}
}
if (fs.existsSync(srcStyles)) {
copyDir(srcStyles, distStyles);
}
// 2. Fix ESM relative imports: append `.js` to bare specifiers like
// `from './layout/AppLayout'` so the resolver can find the file.
function fixImports(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
fixImports(full);
continue;
}
if (!entry.name.endsWith(".js")) continue;
const content = fs.readFileSync(full, "utf8");
const fixed = content.replace(
/((?:from|import)\s+['"])(\.{1,2}\/[^'"]+?)(['"])/g,
(match, prefix, rel, quote) => {
if (rel.endsWith(".js") || rel.endsWith(".css")) return match;
const absDir = path.dirname(full);
const target = path.resolve(absDir, rel);
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
if (fs.existsSync(path.join(target, "index.js"))) {
return `${prefix}${rel}/index.js${quote}`;
}
}
if (fs.existsSync(`${target}.js`)) {
return `${prefix}${rel}.js${quote}`;
}
return match;
},
);
if (fixed !== content) fs.writeFileSync(full, fixed);
}
}
if (fs.existsSync(distDir)) {
fixImports(distDir);
}
console.log("Post-build: CSS copied, ESM imports fixed");