@grapity/grapity 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -14
- package/dist/assets/index-JAhtTTW2.js +336 -0
- package/dist/assets/index-JAhtTTW2.js.map +1 -0
- package/dist/assets/index-LDlidn22.css +1 -0
- package/dist/cli/index.js +794 -69
- package/dist/core/index.d.ts +391 -23
- package/dist/hub/index.js +34 -7
- package/dist/hub/serve.d.ts +9 -1
- package/dist/hub/serve.js +34 -7
- package/dist/index.html +3 -2
- package/dist/registry/{index-DUPbMrAe.d.ts → index-Bx-7YlUF.d.ts} +15 -0
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +298 -3
- package/dist/registry/serve.d.ts +2 -1
- package/dist/registry/serve.js +349 -7
- package/package.json +13 -11
- package/dist/assets/index-Dq5tdnlb.js +0 -326
- package/dist/assets/index-Dq5tdnlb.js.map +0 -1
- package/dist/assets/index-NJpHAonA.css +0 -1
package/dist/registry/serve.js
CHANGED
|
@@ -1756,6 +1756,7 @@ var pushRoute = new Hono().post("/", async (c) => {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
const store = c.get("store");
|
|
1758
1758
|
const service = new RegistryService(store);
|
|
1759
|
+
const actor = c.get("actor") ?? body.pushedBy;
|
|
1759
1760
|
try {
|
|
1760
1761
|
const result = await service.pushSpec(body.content, body.name, {
|
|
1761
1762
|
type: body.type,
|
|
@@ -1764,7 +1765,7 @@ var pushRoute = new Hono().post("/", async (c) => {
|
|
|
1764
1765
|
sourceRepo: body.sourceRepo,
|
|
1765
1766
|
tags: Array.isArray(body.tags) ? body.tags : void 0,
|
|
1766
1767
|
gitRef: body.gitRef,
|
|
1767
|
-
pushedBy:
|
|
1768
|
+
pushedBy: actor,
|
|
1768
1769
|
prerelease: body.prerelease,
|
|
1769
1770
|
force: body.force,
|
|
1770
1771
|
reason: body.reason
|
|
@@ -1929,7 +1930,8 @@ var deleteSpecRoute = new Hono5().delete(
|
|
|
1929
1930
|
const name = c.req.param("name");
|
|
1930
1931
|
const store = c.get("store");
|
|
1931
1932
|
const service = new RegistryService(store);
|
|
1932
|
-
const
|
|
1933
|
+
const actor = c.get("actor");
|
|
1934
|
+
const deleted = await service.deleteSpec(name, actor);
|
|
1933
1935
|
if (!deleted) {
|
|
1934
1936
|
return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
|
|
1935
1937
|
}
|
|
@@ -2670,6 +2672,7 @@ var pushGatewayConfigRoute = new Hono13().post("/", async (c) => {
|
|
|
2670
2672
|
}
|
|
2671
2673
|
const store = c.get("store");
|
|
2672
2674
|
const service = new GatewayService(store, store);
|
|
2675
|
+
const actor = c.get("actor") ?? body.pushedBy;
|
|
2673
2676
|
try {
|
|
2674
2677
|
const result = await service.pushGatewayConfig({
|
|
2675
2678
|
name: body.name,
|
|
@@ -2680,7 +2683,7 @@ var pushGatewayConfigRoute = new Hono13().post("/", async (c) => {
|
|
|
2680
2683
|
environments: body.environments ?? {},
|
|
2681
2684
|
callerIdentification: body.callerIdentification,
|
|
2682
2685
|
content: body.content,
|
|
2683
|
-
pushedBy:
|
|
2686
|
+
pushedBy: actor
|
|
2684
2687
|
});
|
|
2685
2688
|
return c.json({ data: result }, 201);
|
|
2686
2689
|
} catch (err) {
|
|
@@ -2945,6 +2948,283 @@ var gatewayLogStatsRoute = new Hono21().get("/stats", async (c) => {
|
|
|
2945
2948
|
});
|
|
2946
2949
|
});
|
|
2947
2950
|
|
|
2951
|
+
// src/registry/auth/middleware.ts
|
|
2952
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2953
|
+
var AuthError = class extends Error {
|
|
2954
|
+
constructor(statusCode, code, message) {
|
|
2955
|
+
super(message);
|
|
2956
|
+
this.statusCode = statusCode;
|
|
2957
|
+
this.code = code;
|
|
2958
|
+
this.name = "AuthError";
|
|
2959
|
+
}
|
|
2960
|
+
statusCode;
|
|
2961
|
+
code;
|
|
2962
|
+
};
|
|
2963
|
+
function buildKeycloakUrls(config) {
|
|
2964
|
+
const base = `${config.serverUrl}/realms/${config.realm}`;
|
|
2965
|
+
return {
|
|
2966
|
+
issuer: base,
|
|
2967
|
+
jwksUri: `${base}/protocol/openid-connect/certs`,
|
|
2968
|
+
tokenUrl: `${base}/protocol/openid-connect/token`
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
function createAuthMiddleware(config, routeScopes2) {
|
|
2972
|
+
if (config.auth?.mode !== "keycloak") {
|
|
2973
|
+
return async (_c, next) => await next();
|
|
2974
|
+
}
|
|
2975
|
+
const authConfig = config.auth;
|
|
2976
|
+
const { issuer, jwksUri } = buildKeycloakUrls(authConfig);
|
|
2977
|
+
const jwks = createRemoteJWKSet(new URL(jwksUri));
|
|
2978
|
+
const scopeByRoute = /* @__PURE__ */ new Map();
|
|
2979
|
+
for (const route of routeScopes2) {
|
|
2980
|
+
const key = `${route.method.toUpperCase()}:${route.path}`;
|
|
2981
|
+
scopeByRoute.set(key, {
|
|
2982
|
+
operationId: route.operationId,
|
|
2983
|
+
scopes: route.scopes
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
return async (c, next) => {
|
|
2987
|
+
const matchedPath = c.req.matchedRoutes.map((r) => r.path).filter((p) => p !== "/*").pop();
|
|
2988
|
+
const routeKey = matchedPath ? `${c.req.method}:${matchedPath}` : `${c.req.method}:${c.req.routePath}`;
|
|
2989
|
+
const required = scopeByRoute.get(routeKey);
|
|
2990
|
+
if (!required || required.scopes.length === 0) {
|
|
2991
|
+
return await next();
|
|
2992
|
+
}
|
|
2993
|
+
const authHeader = c.req.header("Authorization");
|
|
2994
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
2995
|
+
throw new AuthError(401, "unauthorized", "Bearer token required");
|
|
2996
|
+
}
|
|
2997
|
+
const token = authHeader.slice("Bearer ".length).trim();
|
|
2998
|
+
if (!token) {
|
|
2999
|
+
throw new AuthError(401, "unauthorized", "Bearer token required");
|
|
3000
|
+
}
|
|
3001
|
+
let payload;
|
|
3002
|
+
try {
|
|
3003
|
+
const result = await jwtVerify(token, jwks, {
|
|
3004
|
+
issuer,
|
|
3005
|
+
audience: authConfig.audience
|
|
3006
|
+
});
|
|
3007
|
+
payload = result.payload;
|
|
3008
|
+
} catch (err) {
|
|
3009
|
+
const message = err instanceof Error ? err.message : "Invalid token";
|
|
3010
|
+
throw new AuthError(401, "unauthorized", `Invalid or expired token: ${message}`);
|
|
3011
|
+
}
|
|
3012
|
+
const subject = payload.sub;
|
|
3013
|
+
if (!subject) {
|
|
3014
|
+
throw new AuthError(401, "unauthorized", "Token missing subject claim");
|
|
3015
|
+
}
|
|
3016
|
+
const grantedScopes = extractScopes(payload, authConfig.roleSource ?? "scope");
|
|
3017
|
+
const missing = required.scopes.filter((s) => !grantedScopes.has(s));
|
|
3018
|
+
if (missing.length > 0) {
|
|
3019
|
+
throw new AuthError(
|
|
3020
|
+
403,
|
|
3021
|
+
"forbidden",
|
|
3022
|
+
`Missing required scope${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
|
|
3023
|
+
);
|
|
3024
|
+
}
|
|
3025
|
+
c.set("actor", subject);
|
|
3026
|
+
c.set("claims", payload);
|
|
3027
|
+
await next();
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
3030
|
+
function extractScopes(payload, source) {
|
|
3031
|
+
if (source === "realm_access.roles") {
|
|
3032
|
+
const roles = payload.realm_access?.roles;
|
|
3033
|
+
return new Set(roles ?? []);
|
|
3034
|
+
}
|
|
3035
|
+
const scopeValue = payload.scope;
|
|
3036
|
+
if (typeof scopeValue === "string") {
|
|
3037
|
+
return new Set(scopeValue.split(/\s+/).filter(Boolean));
|
|
3038
|
+
}
|
|
3039
|
+
return /* @__PURE__ */ new Set();
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
// src/registry/generated/route-scopes.ts
|
|
3043
|
+
var routeScopes = [
|
|
3044
|
+
{
|
|
3045
|
+
"method": "GET",
|
|
3046
|
+
"path": "/v1/health",
|
|
3047
|
+
"operationId": "getHealth",
|
|
3048
|
+
"scopes": []
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
"method": "GET",
|
|
3052
|
+
"path": "/v1/specs",
|
|
3053
|
+
"operationId": "listSpecs",
|
|
3054
|
+
"scopes": [
|
|
3055
|
+
"specs:read"
|
|
3056
|
+
]
|
|
3057
|
+
},
|
|
3058
|
+
{
|
|
3059
|
+
"method": "POST",
|
|
3060
|
+
"path": "/v1/specs",
|
|
3061
|
+
"operationId": "pushSpec",
|
|
3062
|
+
"scopes": [
|
|
3063
|
+
"specs:write"
|
|
3064
|
+
]
|
|
3065
|
+
},
|
|
3066
|
+
{
|
|
3067
|
+
"method": "GET",
|
|
3068
|
+
"path": "/v1/specs/:name",
|
|
3069
|
+
"operationId": "getSpec",
|
|
3070
|
+
"scopes": [
|
|
3071
|
+
"specs:read"
|
|
3072
|
+
]
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
"method": "DELETE",
|
|
3076
|
+
"path": "/v1/specs/:name",
|
|
3077
|
+
"operationId": "deleteSpec",
|
|
3078
|
+
"scopes": [
|
|
3079
|
+
"specs:write"
|
|
3080
|
+
]
|
|
3081
|
+
},
|
|
3082
|
+
{
|
|
3083
|
+
"method": "POST",
|
|
3084
|
+
"path": "/v1/specs/:name/validate",
|
|
3085
|
+
"operationId": "validateSpec",
|
|
3086
|
+
"scopes": [
|
|
3087
|
+
"specs:write"
|
|
3088
|
+
]
|
|
3089
|
+
},
|
|
3090
|
+
{
|
|
3091
|
+
"method": "GET",
|
|
3092
|
+
"path": "/v1/specs/:name/versions",
|
|
3093
|
+
"operationId": "listVersions",
|
|
3094
|
+
"scopes": [
|
|
3095
|
+
"specs:read"
|
|
3096
|
+
]
|
|
3097
|
+
},
|
|
3098
|
+
{
|
|
3099
|
+
"method": "GET",
|
|
3100
|
+
"path": "/v1/specs/:name/versions/:semver",
|
|
3101
|
+
"operationId": "getVersion",
|
|
3102
|
+
"scopes": [
|
|
3103
|
+
"specs:read"
|
|
3104
|
+
]
|
|
3105
|
+
},
|
|
3106
|
+
{
|
|
3107
|
+
"method": "GET",
|
|
3108
|
+
"path": "/v1/specs/:name/spec.json",
|
|
3109
|
+
"operationId": "getSpecJson",
|
|
3110
|
+
"scopes": [
|
|
3111
|
+
"specs:read"
|
|
3112
|
+
]
|
|
3113
|
+
},
|
|
3114
|
+
{
|
|
3115
|
+
"method": "GET",
|
|
3116
|
+
"path": "/v1/specs/:name/spec.yaml",
|
|
3117
|
+
"operationId": "getSpecYaml",
|
|
3118
|
+
"scopes": [
|
|
3119
|
+
"specs:read"
|
|
3120
|
+
]
|
|
3121
|
+
},
|
|
3122
|
+
{
|
|
3123
|
+
"method": "GET",
|
|
3124
|
+
"path": "/v1/specs/:name/versions/:semver/spec.json",
|
|
3125
|
+
"operationId": "getVersionSpecJson",
|
|
3126
|
+
"scopes": [
|
|
3127
|
+
"specs:read"
|
|
3128
|
+
]
|
|
3129
|
+
},
|
|
3130
|
+
{
|
|
3131
|
+
"method": "GET",
|
|
3132
|
+
"path": "/v1/specs/:name/versions/:semver/spec.yaml",
|
|
3133
|
+
"operationId": "getVersionSpecYaml",
|
|
3134
|
+
"scopes": [
|
|
3135
|
+
"specs:read"
|
|
3136
|
+
]
|
|
3137
|
+
},
|
|
3138
|
+
{
|
|
3139
|
+
"method": "GET",
|
|
3140
|
+
"path": "/v1/specs/:name/compat/:semver",
|
|
3141
|
+
"operationId": "getCompatReport",
|
|
3142
|
+
"scopes": [
|
|
3143
|
+
"specs:read"
|
|
3144
|
+
]
|
|
3145
|
+
},
|
|
3146
|
+
{
|
|
3147
|
+
"method": "GET",
|
|
3148
|
+
"path": "/v1/specs/:name/compare",
|
|
3149
|
+
"operationId": "compareVersions",
|
|
3150
|
+
"scopes": [
|
|
3151
|
+
"specs:read"
|
|
3152
|
+
]
|
|
3153
|
+
},
|
|
3154
|
+
{
|
|
3155
|
+
"method": "GET",
|
|
3156
|
+
"path": "/v1/gateway-configs",
|
|
3157
|
+
"operationId": "listGatewayConfigs",
|
|
3158
|
+
"scopes": [
|
|
3159
|
+
"gateway-configs:read"
|
|
3160
|
+
]
|
|
3161
|
+
},
|
|
3162
|
+
{
|
|
3163
|
+
"method": "POST",
|
|
3164
|
+
"path": "/v1/gateway-configs",
|
|
3165
|
+
"operationId": "pushGatewayConfig",
|
|
3166
|
+
"scopes": [
|
|
3167
|
+
"gateway-configs:write"
|
|
3168
|
+
]
|
|
3169
|
+
},
|
|
3170
|
+
{
|
|
3171
|
+
"method": "GET",
|
|
3172
|
+
"path": "/v1/gateway-configs/:name",
|
|
3173
|
+
"operationId": "getGatewayConfig",
|
|
3174
|
+
"scopes": [
|
|
3175
|
+
"gateway-configs:read"
|
|
3176
|
+
]
|
|
3177
|
+
},
|
|
3178
|
+
{
|
|
3179
|
+
"method": "GET",
|
|
3180
|
+
"path": "/v1/gateway-configs/:name/versions",
|
|
3181
|
+
"operationId": "listGatewayConfigVersions",
|
|
3182
|
+
"scopes": [
|
|
3183
|
+
"gateway-configs:read"
|
|
3184
|
+
]
|
|
3185
|
+
},
|
|
3186
|
+
{
|
|
3187
|
+
"method": "GET",
|
|
3188
|
+
"path": "/v1/gateway-configs/:name/versions/:versionId",
|
|
3189
|
+
"operationId": "getGatewayConfigVersion",
|
|
3190
|
+
"scopes": [
|
|
3191
|
+
"gateway-configs:read"
|
|
3192
|
+
]
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
"method": "POST",
|
|
3196
|
+
"path": "/v1/gateway-logs/ingest/:provider/:environment",
|
|
3197
|
+
"operationId": "ingestGatewayLog",
|
|
3198
|
+
"scopes": [
|
|
3199
|
+
"gateway-logs:write"
|
|
3200
|
+
]
|
|
3201
|
+
},
|
|
3202
|
+
{
|
|
3203
|
+
"method": "GET",
|
|
3204
|
+
"path": "/v1/gateway-logs",
|
|
3205
|
+
"operationId": "listGatewayLogs",
|
|
3206
|
+
"scopes": [
|
|
3207
|
+
"gateway-logs:read"
|
|
3208
|
+
]
|
|
3209
|
+
},
|
|
3210
|
+
{
|
|
3211
|
+
"method": "GET",
|
|
3212
|
+
"path": "/v1/gateway-logs/stats",
|
|
3213
|
+
"operationId": "getGatewayLogStats",
|
|
3214
|
+
"scopes": [
|
|
3215
|
+
"gateway-logs:read"
|
|
3216
|
+
]
|
|
3217
|
+
},
|
|
3218
|
+
{
|
|
3219
|
+
"method": "GET",
|
|
3220
|
+
"path": "/v1/gateway-logs/:id",
|
|
3221
|
+
"operationId": "getGatewayLog",
|
|
3222
|
+
"scopes": [
|
|
3223
|
+
"gateway-logs:read"
|
|
3224
|
+
]
|
|
3225
|
+
}
|
|
3226
|
+
];
|
|
3227
|
+
|
|
2948
3228
|
// src/registry/server.ts
|
|
2949
3229
|
function createApp(config, store) {
|
|
2950
3230
|
const app = new Hono22();
|
|
@@ -2956,6 +3236,21 @@ function createApp(config, store) {
|
|
|
2956
3236
|
c.set("config", config);
|
|
2957
3237
|
await next();
|
|
2958
3238
|
});
|
|
3239
|
+
const authRouteScopes = config.auth?.mode === "keycloak" ? routeScopes : [];
|
|
3240
|
+
app.use("*", createAuthMiddleware(config, authRouteScopes));
|
|
3241
|
+
app.onError((err, c) => {
|
|
3242
|
+
if (err instanceof AuthError) {
|
|
3243
|
+
return c.json(
|
|
3244
|
+
{ error: err.code, message: err.message, statusCode: err.statusCode },
|
|
3245
|
+
err.statusCode
|
|
3246
|
+
);
|
|
3247
|
+
}
|
|
3248
|
+
console.error("Unhandled error:", err);
|
|
3249
|
+
return c.json(
|
|
3250
|
+
{ error: "internal_error", message: "Internal server error", statusCode: 500 },
|
|
3251
|
+
500
|
|
3252
|
+
);
|
|
3253
|
+
});
|
|
2959
3254
|
app.route("/v1/specs", pushRoute);
|
|
2960
3255
|
app.route("/v1/specs", validateRoute);
|
|
2961
3256
|
app.route("/v1/specs", listRoute);
|
|
@@ -2983,7 +3278,8 @@ function createApp(config, store) {
|
|
|
2983
3278
|
// src/registry/config.ts
|
|
2984
3279
|
var defaultConfig = {
|
|
2985
3280
|
port: 3750,
|
|
2986
|
-
database: "sqlite"
|
|
3281
|
+
database: "sqlite",
|
|
3282
|
+
auth: { mode: "none" }
|
|
2987
3283
|
};
|
|
2988
3284
|
|
|
2989
3285
|
// src/registry/storage/sqlite.ts
|
|
@@ -3596,17 +3892,63 @@ var provisions2 = pgTable("provisions", {
|
|
|
3596
3892
|
// src/registry/storage/postgresql.ts
|
|
3597
3893
|
import { v4 as uuid6 } from "uuid";
|
|
3598
3894
|
var MIGRATIONS_FOLDER2 = PG_MIGRATIONS_FOLDER;
|
|
3895
|
+
var DatabaseConnectionError = class extends Error {
|
|
3896
|
+
constructor(message, postgresUrl, options) {
|
|
3897
|
+
super(message, options);
|
|
3898
|
+
this.postgresUrl = postgresUrl;
|
|
3899
|
+
this.name = "DatabaseConnectionError";
|
|
3900
|
+
}
|
|
3901
|
+
postgresUrl;
|
|
3902
|
+
};
|
|
3903
|
+
function isConnectionError(err) {
|
|
3904
|
+
if (typeof err !== "object" || err === null) return false;
|
|
3905
|
+
if ("code" in err) {
|
|
3906
|
+
const code = err.code;
|
|
3907
|
+
if (code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "ENOTFOUND") {
|
|
3908
|
+
return true;
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
if (err instanceof AggregateError) {
|
|
3912
|
+
if (err.errors.some(isConnectionError)) return true;
|
|
3913
|
+
}
|
|
3914
|
+
if ("cause" in err && err.cause) {
|
|
3915
|
+
return isConnectionError(err.cause);
|
|
3916
|
+
}
|
|
3917
|
+
return false;
|
|
3918
|
+
}
|
|
3919
|
+
function maskPostgresPassword(url) {
|
|
3920
|
+
try {
|
|
3921
|
+
const parsed = new URL(url);
|
|
3922
|
+
if (parsed.password) parsed.password = "***";
|
|
3923
|
+
return parsed.toString();
|
|
3924
|
+
} catch {
|
|
3925
|
+
return url;
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3599
3928
|
var PostgreSQLSpecStore = class {
|
|
3600
3929
|
db;
|
|
3601
3930
|
pool;
|
|
3931
|
+
postgresUrl;
|
|
3602
3932
|
constructor(postgresUrl) {
|
|
3933
|
+
this.postgresUrl = postgresUrl;
|
|
3603
3934
|
this.pool = new Pool({ connectionString: postgresUrl });
|
|
3604
3935
|
this.db = drizzle2(this.pool);
|
|
3605
3936
|
}
|
|
3606
3937
|
async migrate() {
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3938
|
+
try {
|
|
3939
|
+
await migrate2(this.db, {
|
|
3940
|
+
migrationsFolder: MIGRATIONS_FOLDER2
|
|
3941
|
+
});
|
|
3942
|
+
} catch (err) {
|
|
3943
|
+
if (isConnectionError(err)) {
|
|
3944
|
+
throw new DatabaseConnectionError(
|
|
3945
|
+
`PostgreSQL is not reachable at ${maskPostgresPassword(this.postgresUrl)}`,
|
|
3946
|
+
this.postgresUrl,
|
|
3947
|
+
{ cause: err }
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
throw err;
|
|
3951
|
+
}
|
|
3610
3952
|
}
|
|
3611
3953
|
async end() {
|
|
3612
3954
|
await this.pool.end();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grapity/grapity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "grapity - API spec registry and compatibility guardian",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -66,26 +66,28 @@
|
|
|
66
66
|
"provenance": true
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"
|
|
70
|
-
"js-yaml": "^4.1.0",
|
|
71
|
-
"chalk": "^5.4.0",
|
|
72
|
-
"ora": "^8.2.0",
|
|
69
|
+
"@apidevtools/swagger-parser": "^10.1.0",
|
|
73
70
|
"@hono/node-server": "^2.0.4",
|
|
71
|
+
"better-sqlite3": "^12.10.0",
|
|
72
|
+
"chalk": "^5.4.0",
|
|
73
|
+
"commander": "^13.1.0",
|
|
74
|
+
"drizzle-orm": "^0.44.0",
|
|
74
75
|
"hono": "^4.12.22",
|
|
76
|
+
"jose": "^6.2.3",
|
|
77
|
+
"js-yaml": "^4.1.0",
|
|
75
78
|
"lucide-react": "^0.475.0",
|
|
79
|
+
"ora": "^8.2.0",
|
|
80
|
+
"pg": "^8.16.0",
|
|
76
81
|
"react": "^19.0.0",
|
|
77
82
|
"react-dom": "^19.0.0",
|
|
78
83
|
"react-router-dom": "^7.4.0",
|
|
79
84
|
"shiki": "^4.2.0",
|
|
80
|
-
"drizzle-orm": "^0.44.0",
|
|
81
|
-
"better-sqlite3": "^12.10.0",
|
|
82
|
-
"pg": "^8.16.0",
|
|
83
|
-
"@apidevtools/swagger-parser": "^10.1.0",
|
|
84
85
|
"uuid": "^11.1.0",
|
|
85
86
|
"zod": "^3.24.0"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
89
|
"@tailwindcss/vite": "^4.3.0",
|
|
90
|
+
"@testcontainers/postgresql": "^11.14.0",
|
|
89
91
|
"@testing-library/dom": "^10.4.0",
|
|
90
92
|
"@testing-library/jest-dom": "^6.6.0",
|
|
91
93
|
"@testing-library/react": "^16.2.0",
|
|
@@ -103,10 +105,10 @@
|
|
|
103
105
|
"openapi-typescript": "^7.13.0",
|
|
104
106
|
"postcss": "^8.5.3",
|
|
105
107
|
"tailwindcss": "^4.0.0",
|
|
108
|
+
"testcontainers": "11.14.0",
|
|
106
109
|
"tsup": "^8.4.0",
|
|
107
110
|
"tsx": "^4.19.0",
|
|
108
111
|
"typescript": "^5.8.0",
|
|
109
|
-
"vite": "^6.3.0"
|
|
110
|
-
"@testcontainers/postgresql": "^11.14.0"
|
|
112
|
+
"vite": "^6.3.0"
|
|
111
113
|
}
|
|
112
114
|
}
|