@suluk/platform - v0.5.2
    Preparing search index...

    @suluk/platform - v0.5.2

    @suluk/platform

    Write one manifest; the generator plans the shadcn-registry adds, generates the wired Hono entry, merges each module's provision fragment, and emits the whole scaffold (package.json / tsconfig / wrangler.toml / .env.example / …). The higher-level surface over the Suluk registry + @suluk/provision.

    There are two authoring surfaces. The legacy one (C051) is a single object and is supported forever. The C053 one splits a platform into a reusable system and a swappable brand, adds typed opts, and lets services compose.

    defineService<SO, BO>({ id, mount, provision?, deps?, env?, serviceOpts?, brandOpts?, reads?, compose? }) — the shape a community shadcn registry extends. The 19 core services are exported as typed consts (authService, creditsService, …).

    import { defineSystem, defineBrand, definePlatform, authService, creditsService, emailService } from "@suluk/platform";
    import { analyticsService } from "@acme/suluk-analytics"; // a community service

    export const system = defineSystem({
    registry: "MahmoodKhalil57/suluk",
    services: [authService, creditsService, emailService, analyticsService],
    globalServiceOpts: { ENVIRONMENT: "production", TRUSTED_ORIGINS: "https://app.example" },
    serviceOpts: { auth: { mcp: { loginPage: "…", consentPage: "…", resource: "…", scopes: ["credits:read"] } } }, // typed by id
    wire: [{ id: "signup-grant", from: "auth.onUserCreated", to: "credits.grantOnSignup", with: { amount: 100 } }],
    });

    export const brand = defineBrand({
    name: "app",
    globalBrandOpts: { BRAND_NAME: "App", BASE_URL: "https://app.example", EMAIL_FROM: "hi@app.example" },
    });

    export default definePlatform({ system, brand });

    A system (services + serviceOpts + globalServiceOpts + wiring) is the reusable, publishable template. A brand (brandOpts + globalBrandOpts) is thin and swappable — two businesses run the same system with different brands; the generated entry code is identical, only wrangler.toml [vars] differ.

    per-service global
    service axis (how it works) serviceOpts → the entry (mount opts) globalServiceOpts (a service reads the keys it needs)
    brand axis (identity) brandOpts[vars] globalBrandOpts[vars]

    serviceOpts is typed per service id off the imported service objects (or CoreServiceOptsMap for a string id) — a wrong opt is a compile error.

    An edge binds a producer port to a consumer capability. It renders into the producer's existing mount-opt field (e.g. auth.onUserCreated), not a separate statement — so it reuses a real seam and the hook closure gets a real env. resolveWiring validates presence, port/capability existence, JSON-safe params, safe identifiers, and acyclicity; fan-out (several wires on one port) composes in declaration order. A community service participates by offering a capability (fills a core port) or exposing its own.

    suluk-platform            # generate from ./platform.config.ts (legacy OR { system, brand })
    suluk-platform --config <path>
    suluk-platform migrate # print the { system, brand } split of a legacy config (a starting point)

    migrate is byte-faithful: liftLegacy → the same generated app as the legacy manifest.

    • Byte-identity. A legacy manifest — and any { system, brand } with no wire — regenerates the exact bytes the C051 generator produced (pinned by a golden test over the real 18-service reference app).
    • Fail closed. A missing registry, a wrong-typed wire param, or a colliding wire import throws at generate time, not in the shipped app.
    • Own the wiring, npm the logic (C052). A community extends services and composition without forking @suluk logic.