honertia 0.1.40 → 0.1.41
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 +123 -12
- package/dist/__tmp_db_safety_probe.d.ts +2 -0
- package/dist/__tmp_db_safety_probe.d.ts.map +1 -0
- package/dist/__tmp_db_safety_probe.js +13 -0
- package/dist/__tmp_scoped_astrusted_probe.d.ts +2 -0
- package/dist/__tmp_scoped_astrusted_probe.d.ts.map +1 -0
- package/dist/__tmp_scoped_astrusted_probe.js +10 -0
- package/dist/__tmp_scoped_derived_probe.d.ts +2 -0
- package/dist/__tmp_scoped_derived_probe.d.ts.map +1 -0
- package/dist/__tmp_scoped_derived_probe.js +12 -0
- package/dist/__tmp_scoped_mutation_probe.d.ts +2 -0
- package/dist/__tmp_scoped_mutation_probe.d.ts.map +1 -0
- package/dist/__tmp_scoped_mutation_probe.js +15 -0
- package/dist/cache.d.ts +3 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +14 -3
- package/dist/cli/bin.d.ts +8 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +150 -0
- package/dist/cli/db.d.ts.map +1 -1
- package/dist/cli/db.js +83 -18
- package/dist/cli/feature.d.ts +1 -0
- package/dist/cli/feature.d.ts.map +1 -1
- package/dist/cli/feature.js +76 -11
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +124 -22
- package/dist/cli/index.d.ts +6 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +16 -4
- package/dist/cli/inline-tests.d.ts.map +1 -1
- package/dist/cli/inline-tests.js +4 -0
- package/dist/cli/openapi.d.ts +2 -1
- package/dist/cli/openapi.d.ts.map +1 -1
- package/dist/cli/openapi.js +68 -3
- package/dist/effect/action.d.ts +92 -3
- package/dist/effect/action.d.ts.map +1 -1
- package/dist/effect/action.js +38 -32
- package/dist/effect/auth.d.ts +3 -1
- package/dist/effect/auth.d.ts.map +1 -1
- package/dist/effect/auth.js +17 -3
- package/dist/effect/bridge.d.ts +5 -0
- package/dist/effect/bridge.d.ts.map +1 -1
- package/dist/effect/bridge.js +5 -2
- package/dist/effect/index.d.ts +2 -2
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +2 -2
- package/dist/effect/routing.d.ts.map +1 -1
- package/dist/effect/routing.js +14 -14
- package/dist/effect/services.d.ts +3 -0
- package/dist/effect/services.d.ts.map +1 -1
- package/dist/effect/validation.d.ts +10 -1
- package/dist/effect/validation.d.ts.map +1 -1
- package/dist/effect/validation.js +49 -14
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +3 -2
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -4,6 +4,14 @@ Inertia.js adapter for Hono with Effect.ts. Server-driven app with SPA behavior.
|
|
|
4
4
|
|
|
5
5
|
## CLI Commands
|
|
6
6
|
|
|
7
|
+
`honertia` is shipped as a package binary. You can run commands with:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bunx honertia <command>
|
|
11
|
+
# or
|
|
12
|
+
npx honertia <command>
|
|
13
|
+
```
|
|
14
|
+
|
|
7
15
|
### Generate Action
|
|
8
16
|
|
|
9
17
|
```bash
|
|
@@ -117,8 +125,8 @@ honertia db status # Show migration status
|
|
|
117
125
|
honertia db status --json # JSON output
|
|
118
126
|
honertia db migrate # Run pending migrations
|
|
119
127
|
honertia db migrate --preview # Preview SQL without executing
|
|
120
|
-
honertia db rollback
|
|
121
|
-
|
|
128
|
+
honertia db rollback --preview # Preview rollback SQL for latest applied migration
|
|
129
|
+
# Non-preview rollback execution is manual (run preview SQL yourself)
|
|
122
130
|
honertia db generate add_email # Generate new migration
|
|
123
131
|
```
|
|
124
132
|
|
|
@@ -979,6 +987,93 @@ export const destroyProject = action(
|
|
|
979
987
|
)
|
|
980
988
|
```
|
|
981
989
|
|
|
990
|
+
### Real-World Transaction (Order Checkout)
|
|
991
|
+
|
|
992
|
+
Pass a validated/trusted transaction object as the second argument to
|
|
993
|
+
`dbMutation`/`dbTransaction` when you want strict write scoping.
|
|
994
|
+
Inside that callback, write methods only accept values that come from
|
|
995
|
+
that scoped object.
|
|
996
|
+
|
|
997
|
+
```typescript
|
|
998
|
+
import { Effect, Schema as S } from 'effect'
|
|
999
|
+
import {
|
|
1000
|
+
action,
|
|
1001
|
+
authorize,
|
|
1002
|
+
validate,
|
|
1003
|
+
validateRequest,
|
|
1004
|
+
DatabaseService,
|
|
1005
|
+
dbTransaction,
|
|
1006
|
+
mergeMutationInput,
|
|
1007
|
+
redirect,
|
|
1008
|
+
requiredString,
|
|
1009
|
+
} from 'honertia/effect'
|
|
1010
|
+
import { eq } from 'drizzle-orm'
|
|
1011
|
+
import { orders, orderItems, inventory } from '~/db/schema'
|
|
1012
|
+
|
|
1013
|
+
const CheckoutSchema = S.Struct({
|
|
1014
|
+
productId: requiredString,
|
|
1015
|
+
quantity: S.NumberFromString,
|
|
1016
|
+
})
|
|
1017
|
+
|
|
1018
|
+
const CheckoutTransactionSchema = S.Struct({
|
|
1019
|
+
createOrder: S.Struct({
|
|
1020
|
+
userId: S.String,
|
|
1021
|
+
status: S.Literal('pending'),
|
|
1022
|
+
}),
|
|
1023
|
+
createItem: S.Struct({
|
|
1024
|
+
productId: S.String,
|
|
1025
|
+
quantity: S.Number,
|
|
1026
|
+
// Reserve transaction-derived fields you plan to fill later.
|
|
1027
|
+
orderId: S.optional(S.String),
|
|
1028
|
+
}),
|
|
1029
|
+
updateInventory: S.Struct({
|
|
1030
|
+
reserved: S.Number,
|
|
1031
|
+
}),
|
|
1032
|
+
})
|
|
1033
|
+
|
|
1034
|
+
export const checkout = action(
|
|
1035
|
+
Effect.gen(function* () {
|
|
1036
|
+
const auth = yield* authorize()
|
|
1037
|
+
const input = yield* validateRequest(CheckoutSchema, {
|
|
1038
|
+
errorComponent: 'Checkout/Show',
|
|
1039
|
+
})
|
|
1040
|
+
const db = yield* DatabaseService
|
|
1041
|
+
|
|
1042
|
+
const txInput = yield* validate(CheckoutTransactionSchema, {
|
|
1043
|
+
createOrder: {
|
|
1044
|
+
userId: auth.user.id,
|
|
1045
|
+
status: 'pending',
|
|
1046
|
+
},
|
|
1047
|
+
createItem: {
|
|
1048
|
+
productId: input.productId,
|
|
1049
|
+
quantity: input.quantity,
|
|
1050
|
+
},
|
|
1051
|
+
updateInventory: {
|
|
1052
|
+
reserved: input.quantity,
|
|
1053
|
+
},
|
|
1054
|
+
})
|
|
1055
|
+
// For untyped/unknown payloads (e.g. external JSON), use validateUnknown(schema, raw)
|
|
1056
|
+
|
|
1057
|
+
const order = yield* dbTransaction(db, txInput, async (tx, scoped) => {
|
|
1058
|
+
const [created] = await tx.insert(orders).values(scoped.createOrder).returning()
|
|
1059
|
+
|
|
1060
|
+
const itemInsert = mergeMutationInput(scoped.createItem, {
|
|
1061
|
+
orderId: created.id,
|
|
1062
|
+
})
|
|
1063
|
+
await tx.insert(orderItems).values(itemInsert)
|
|
1064
|
+
|
|
1065
|
+
await tx.update(inventory)
|
|
1066
|
+
.set(scoped.updateInventory)
|
|
1067
|
+
.where(eq(inventory.productId, scoped.createItem.productId))
|
|
1068
|
+
|
|
1069
|
+
return created
|
|
1070
|
+
})
|
|
1071
|
+
|
|
1072
|
+
return yield* redirect(`/orders/${order.id}`)
|
|
1073
|
+
})
|
|
1074
|
+
)
|
|
1075
|
+
```
|
|
1076
|
+
|
|
982
1077
|
### API Endpoint (JSON Response)
|
|
983
1078
|
|
|
984
1079
|
```typescript
|
|
@@ -1819,7 +1914,8 @@ const handler = action(
|
|
|
1819
1914
|
yield* cache.delete('my-key')
|
|
1820
1915
|
|
|
1821
1916
|
// List keys by prefix
|
|
1822
|
-
const
|
|
1917
|
+
const page = yield* cache.list({ prefix: 'user:' })
|
|
1918
|
+
const keys = page.keys
|
|
1823
1919
|
})
|
|
1824
1920
|
)
|
|
1825
1921
|
```
|
|
@@ -1857,7 +1953,10 @@ const createRedisCacheClient = (redisUrl: string): CacheClient => {
|
|
|
1857
1953
|
Effect.tryPromise({
|
|
1858
1954
|
try: async () => {
|
|
1859
1955
|
const keys = await client.keys(options?.prefix ? `${options.prefix}*` : '*')
|
|
1860
|
-
return {
|
|
1956
|
+
return {
|
|
1957
|
+
keys: keys.map((name) => ({ name })),
|
|
1958
|
+
list_complete: true,
|
|
1959
|
+
}
|
|
1861
1960
|
},
|
|
1862
1961
|
catch: (e) => new CacheClientError('Redis keys failed', e),
|
|
1863
1962
|
}),
|
|
@@ -1907,6 +2006,7 @@ const makeTestCache = (): Layer.Layer<CacheService> => {
|
|
|
1907
2006
|
keys: [...store.keys()]
|
|
1908
2007
|
.filter((k) => !options?.prefix || k.startsWith(options.prefix))
|
|
1909
2008
|
.map((name) => ({ name })),
|
|
2009
|
+
list_complete: true,
|
|
1910
2010
|
})),
|
|
1911
2011
|
}
|
|
1912
2012
|
|
|
@@ -1988,7 +2088,7 @@ Recommended cache key patterns:
|
|
|
1988
2088
|
|
|
1989
2089
|
### Stale-While-Revalidate (SWR)
|
|
1990
2090
|
|
|
1991
|
-
The cache supports the stale-while-revalidate pattern for improved latency and resilience. When enabled, stale values are returned immediately while a background refresh is triggered.
|
|
2091
|
+
The cache supports the stale-while-revalidate pattern for improved latency and resilience. When enabled, stale values are returned immediately while a background refresh is triggered. In environments without `ExecutionContext`, stale entries are recomputed synchronously instead.
|
|
1992
2092
|
|
|
1993
2093
|
```typescript
|
|
1994
2094
|
import { Effect, Duration } from 'effect'
|
|
@@ -2122,6 +2222,16 @@ export const dashboard = action(
|
|
|
2122
2222
|
**Common use cases:**
|
|
2123
2223
|
|
|
2124
2224
|
```typescript
|
|
2225
|
+
import { Effect } from 'effect'
|
|
2226
|
+
import {
|
|
2227
|
+
ExecutionContextService,
|
|
2228
|
+
authorize,
|
|
2229
|
+
DatabaseService,
|
|
2230
|
+
dbMutation,
|
|
2231
|
+
asTrusted,
|
|
2232
|
+
BindingsService,
|
|
2233
|
+
} from 'honertia/effect'
|
|
2234
|
+
|
|
2125
2235
|
// Audit logging
|
|
2126
2236
|
const auditLog = (action: string, details: Record<string, unknown>) =>
|
|
2127
2237
|
Effect.gen(function* () {
|
|
@@ -2130,14 +2240,14 @@ const auditLog = (action: string, details: Record<string, unknown>) =>
|
|
|
2130
2240
|
const db = yield* DatabaseService
|
|
2131
2241
|
|
|
2132
2242
|
yield* ctx.runInBackground(
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
userId: user.id,
|
|
2243
|
+
dbMutation(db, async (tx) => {
|
|
2244
|
+
await tx.insert(auditLogs).values(asTrusted({
|
|
2245
|
+
userId: user.user.id,
|
|
2136
2246
|
action,
|
|
2137
2247
|
details,
|
|
2138
2248
|
timestamp: new Date(),
|
|
2139
|
-
})
|
|
2140
|
-
)
|
|
2249
|
+
}))
|
|
2250
|
+
})
|
|
2141
2251
|
)
|
|
2142
2252
|
})
|
|
2143
2253
|
|
|
@@ -2185,7 +2295,7 @@ const maybeNotifySlack = (message: string) =>
|
|
|
2185
2295
|
**Important notes:**
|
|
2186
2296
|
- Background tasks run after the response is sent to the user
|
|
2187
2297
|
- Errors in background tasks are logged but don't crash the worker
|
|
2188
|
-
- In non-Worker environments (tests, local dev), `isAvailable` is `false` and
|
|
2298
|
+
- In non-Worker environments (tests, local dev), `isAvailable` is `false` and stale entries are recomputed synchronously
|
|
2189
2299
|
- Use `catchAll` to handle errors gracefully in background tasks
|
|
2190
2300
|
|
|
2191
2301
|
### Cache Key Versioning
|
|
@@ -2276,6 +2386,7 @@ import {
|
|
|
2276
2386
|
authorize,
|
|
2277
2387
|
cache,
|
|
2278
2388
|
cacheInvalidate,
|
|
2389
|
+
asTrusted,
|
|
2279
2390
|
render,
|
|
2280
2391
|
redirect,
|
|
2281
2392
|
DatabaseService,
|
|
@@ -2331,7 +2442,7 @@ export const updateProfile = action(
|
|
|
2331
2442
|
|
|
2332
2443
|
yield* dbMutation(db, async (db) => {
|
|
2333
2444
|
await db.update(users)
|
|
2334
|
-
.set({ name: input.name, bio: input.bio })
|
|
2445
|
+
.set(asTrusted({ name: input.name, bio: input.bio }))
|
|
2335
2446
|
.where(eq(users.id, auth.user.id))
|
|
2336
2447
|
})
|
|
2337
2448
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__tmp_db_safety_probe.d.ts","sourceRoot":"","sources":["../src/__tmp_db_safety_probe.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { asValidated } from './effect/validation.js';
|
|
2
|
+
async function probe() {
|
|
3
|
+
const raw = { name: 'Pat', email: 'pat@example.com' };
|
|
4
|
+
// @ts-expect-error raw input should be rejected by SafeTx
|
|
5
|
+
await db.insert('users').values(raw);
|
|
6
|
+
const validated = asValidated(raw);
|
|
7
|
+
await db.insert('users').values(validated);
|
|
8
|
+
const merged = { ...validated, email: validated.email.toLowerCase() };
|
|
9
|
+
await db.insert('users').values(merged);
|
|
10
|
+
const role = (Math.random() > 0.5 ? 'admin' : 'member');
|
|
11
|
+
const mergedWithUnvalidated = { ...validated, role };
|
|
12
|
+
await dbWithRole.insert('users').values(mergedWithUnvalidated);
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__tmp_scoped_astrusted_probe.d.ts","sourceRoot":"","sources":["../src/__tmp_scoped_astrusted_probe.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { dbMutation } from './effect/action.js';
|
|
3
|
+
import { asTrusted } from './effect/validation.js';
|
|
4
|
+
const txInput = asTrusted({ createUser: { name: 'pat' } });
|
|
5
|
+
const program = dbMutation(db, txInput, async (tx, scoped) => {
|
|
6
|
+
await tx.insert('users').values(scoped.createUser);
|
|
7
|
+
// @ts-expect-error scoped mode rejects ad-hoc asTrusted payloads
|
|
8
|
+
await tx.insert('users').values(asTrusted({ name: 'other' }));
|
|
9
|
+
});
|
|
10
|
+
void Effect.runSync(program);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__tmp_scoped_derived_probe.d.ts","sourceRoot":"","sources":["../src/__tmp_scoped_derived_probe.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { dbTransaction } from './effect/action.js';
|
|
3
|
+
import { asTrusted } from './effect/validation.js';
|
|
4
|
+
const program = dbTransaction(db, txInput, async (tx, scoped) => {
|
|
5
|
+
const [created] = await tx.insert('orders').values(scoped.createOrder).returning();
|
|
6
|
+
// @ts-expect-error scoped mode should reject plain/asTrusted derived values
|
|
7
|
+
await tx.insert('orderItems').values(asTrusted({
|
|
8
|
+
...scoped.createItem,
|
|
9
|
+
orderId: created.id,
|
|
10
|
+
}));
|
|
11
|
+
});
|
|
12
|
+
void Effect.runSync(program);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__tmp_scoped_mutation_probe.d.ts","sourceRoot":"","sources":["../src/__tmp_scoped_mutation_probe.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { dbMutation } from './effect/action.js';
|
|
3
|
+
import { asTrusted } from './effect/validation.js';
|
|
4
|
+
const input = asTrusted({
|
|
5
|
+
createUser: {
|
|
6
|
+
name: 'Pat',
|
|
7
|
+
userId: 'u1',
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
const program = dbMutation(db, input, async (tx, scoped) => {
|
|
11
|
+
await tx.insert('users').values(scoped.createUser);
|
|
12
|
+
// @ts-expect-error scoped mode should reject ad-hoc trusted values
|
|
13
|
+
await tx.insert('users').values(asTrusted({ name: 'Other', userId: 'u2' }));
|
|
14
|
+
});
|
|
15
|
+
void Effect.runSync(program);
|
package/dist/cache.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type CacheOptions = {
|
|
|
15
15
|
/**
|
|
16
16
|
* Stale-while-revalidate window. When set, stale values within this window
|
|
17
17
|
* are returned immediately while a background refresh is triggered.
|
|
18
|
-
* Without ExecutionContext,
|
|
18
|
+
* Without ExecutionContext, stale entries are recomputed synchronously.
|
|
19
19
|
*/
|
|
20
20
|
swr?: Duration.DurationInput;
|
|
21
21
|
/**
|
|
@@ -84,6 +84,8 @@ export declare class CacheError extends CacheError_base {
|
|
|
84
84
|
export declare const cache: <V, E, R>(key: string, compute: Effect.Effect<V, E, R>, schema: Schema.Schema<V>, options: CacheOptions) => Effect.Effect<V, E | CacheError | CacheClientError | ParseResult.ParseError, R | CacheService | ExecutionContextService>;
|
|
85
85
|
/**
|
|
86
86
|
* Get a value from cache without computing.
|
|
87
|
+
* Returns any entry still present in the backing cache store.
|
|
88
|
+
* This does not apply `ttl`/`swr` freshness checks used by `cache()`.
|
|
87
89
|
*
|
|
88
90
|
* @example
|
|
89
91
|
* ```typescript
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAM9F,MAAM,MAAM,YAAY,GAAG;IACzB,qCAAqC;IACrC,GAAG,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;;;;;;;AAMD,qBAAa,UAAW,SAAQ,eAG9B;CAAG;AA2DL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,KAAK,MAAM,EACX,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,SAAS,YAAY,KACpB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC,GAAG,YAAY,GAAG,uBAAuB,
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAM9F,MAAM,MAAM,YAAY,GAAG;IACzB,qCAAqC;IACrC,GAAG,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;;;;;;;AAMD,qBAAa,UAAW,SAAQ,eAG9B;CAAG;AA2DL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,KAAK,MAAM,EACX,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,SAAS,YAAY,KACpB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC,GAAG,YAAY,GAAG,uBAAuB,CA0DtH,CAAA;AAEJ;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,UAAU,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAenG,CAAA;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,OAAO,CAAC,EACR,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,SAAS,YAAY,KACpB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAevF,CAAA;AAEJ,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI;IACtC,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACxB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,GAAG,OAAO,EACzC,KAAK,MAAM,EACX,UAAU,sBAAsB,CAAC,CAAC,CAAC,KAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAOjD,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAwBjD,CAAA"}
|
package/dist/cache.js
CHANGED
|
@@ -131,8 +131,10 @@ export const cache = (key, compute, schema, options) => Effect.gen(function* ()
|
|
|
131
131
|
const freshValue = yield* compute;
|
|
132
132
|
yield* storeInCache(freshValue);
|
|
133
133
|
}));
|
|
134
|
+
return entry.v;
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
+
// No background execution available (tests/local dev): refresh inline.
|
|
137
|
+
// Fall through to synchronous recompute below.
|
|
136
138
|
}
|
|
137
139
|
// Beyond SWR window - fall through to recompute
|
|
138
140
|
}
|
|
@@ -144,6 +146,8 @@ export const cache = (key, compute, schema, options) => Effect.gen(function* ()
|
|
|
144
146
|
});
|
|
145
147
|
/**
|
|
146
148
|
* Get a value from cache without computing.
|
|
149
|
+
* Returns any entry still present in the backing cache store.
|
|
150
|
+
* This does not apply `ttl`/`swr` freshness checks used by `cache()`.
|
|
147
151
|
*
|
|
148
152
|
* @example
|
|
149
153
|
* ```typescript
|
|
@@ -220,6 +224,13 @@ export const cacheInvalidate = (key, options) => Effect.gen(function* () {
|
|
|
220
224
|
*/
|
|
221
225
|
export const cacheInvalidatePrefix = (prefix) => Effect.gen(function* () {
|
|
222
226
|
const cacheService = yield* CacheService;
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
let cursor = undefined;
|
|
228
|
+
while (true) {
|
|
229
|
+
const page = yield* cacheService.list({ prefix, cursor });
|
|
230
|
+
yield* Effect.forEach(page.keys, (key) => cacheService.delete(key.name), { concurrency: 10 });
|
|
231
|
+
if (page.list_complete || page.cursor === undefined) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
cursor = page.cursor;
|
|
235
|
+
}
|
|
225
236
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAwFH,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DlF"}
|
package/dist/cli/bin.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Honertia CLI entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* This powers the `honertia` executable distributed with the package.
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { runRoutes, routesHelp } from './index.js';
|
|
10
|
+
import { runCheck, checkHelp } from './check.js';
|
|
11
|
+
import { runDb, dbHelp } from './db.js';
|
|
12
|
+
import { runGenerateAction, generateActionHelp, runGenerateCrud, generateCrudHelp } from './generate.js';
|
|
13
|
+
import { runGenerateFeature, generateFeatureHelp } from './feature.js';
|
|
14
|
+
import { runGenerateOpenApi, generateOpenApiHelp } from './openapi.js';
|
|
15
|
+
import { runGenerateInlineTestsRunner, generateInlineTestsRunnerHelp } from './inline-tests.js';
|
|
16
|
+
function mainHelp() {
|
|
17
|
+
return `
|
|
18
|
+
honertia - Agent-first CLI for Honertia
|
|
19
|
+
|
|
20
|
+
USAGE:
|
|
21
|
+
honertia <command> [OPTIONS]
|
|
22
|
+
|
|
23
|
+
COMMANDS:
|
|
24
|
+
routes List registered routes
|
|
25
|
+
check Validate project routes/configuration
|
|
26
|
+
db <subcommand> Database migration commands
|
|
27
|
+
db:status Alias for "db status"
|
|
28
|
+
db:migrate Alias for "db migrate"
|
|
29
|
+
db:rollback Alias for "db rollback"
|
|
30
|
+
db:generate <name> Alias for "db generate <name>"
|
|
31
|
+
generate:action <name> Generate a colocated action file
|
|
32
|
+
generate:crud <resource> Generate CRUD action files
|
|
33
|
+
generate:feature <name> Generate a colocated feature file
|
|
34
|
+
generate:openapi Generate OpenAPI spec
|
|
35
|
+
generate:tests-runner Generate inline tests runner
|
|
36
|
+
|
|
37
|
+
EXAMPLES:
|
|
38
|
+
honertia routes --json
|
|
39
|
+
honertia check --verbose
|
|
40
|
+
honertia db status
|
|
41
|
+
honertia db:migrate --preview
|
|
42
|
+
honertia generate:action projects/create --method POST --path /projects
|
|
43
|
+
honertia generate:openapi --output openapi.json --format json
|
|
44
|
+
|
|
45
|
+
Run "honertia <command> --help" for command-specific options.
|
|
46
|
+
`.trim();
|
|
47
|
+
}
|
|
48
|
+
function commandHelp(command) {
|
|
49
|
+
switch (command) {
|
|
50
|
+
case 'routes':
|
|
51
|
+
return routesHelp();
|
|
52
|
+
case 'check':
|
|
53
|
+
return checkHelp();
|
|
54
|
+
case 'db':
|
|
55
|
+
case 'db:status':
|
|
56
|
+
case 'db:migrate':
|
|
57
|
+
case 'db:rollback':
|
|
58
|
+
case 'db:generate':
|
|
59
|
+
return dbHelp();
|
|
60
|
+
case 'generate:action':
|
|
61
|
+
return generateActionHelp();
|
|
62
|
+
case 'generate:crud':
|
|
63
|
+
return generateCrudHelp();
|
|
64
|
+
case 'generate:feature':
|
|
65
|
+
return generateFeatureHelp();
|
|
66
|
+
case 'generate:openapi':
|
|
67
|
+
return generateOpenApiHelp();
|
|
68
|
+
case 'generate:tests-runner':
|
|
69
|
+
return generateInlineTestsRunnerHelp();
|
|
70
|
+
default:
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function normalizeCommand(args) {
|
|
75
|
+
if (args.length === 0) {
|
|
76
|
+
return { command: null, rest: [] };
|
|
77
|
+
}
|
|
78
|
+
const [first, ...rest] = args;
|
|
79
|
+
// Support grouped form: "generate action ..."
|
|
80
|
+
if (first === 'generate' && rest.length > 0) {
|
|
81
|
+
const [subcommand, ...remaining] = rest;
|
|
82
|
+
return { command: `generate:${subcommand}`, rest: remaining };
|
|
83
|
+
}
|
|
84
|
+
return { command: first, rest };
|
|
85
|
+
}
|
|
86
|
+
export async function runCli(args = process.argv.slice(2)) {
|
|
87
|
+
const { command, rest } = normalizeCommand(args);
|
|
88
|
+
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
89
|
+
if (rest.length > 0) {
|
|
90
|
+
const help = commandHelp(rest[0]);
|
|
91
|
+
if (help) {
|
|
92
|
+
console.log(help);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
console.log(mainHelp());
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
switch (command) {
|
|
100
|
+
case 'routes':
|
|
101
|
+
runRoutes(rest);
|
|
102
|
+
return;
|
|
103
|
+
case 'check':
|
|
104
|
+
runCheck(rest);
|
|
105
|
+
return;
|
|
106
|
+
case 'db':
|
|
107
|
+
await runDb(rest);
|
|
108
|
+
return;
|
|
109
|
+
case 'db:status':
|
|
110
|
+
await runDb(['status', ...rest]);
|
|
111
|
+
return;
|
|
112
|
+
case 'db:migrate':
|
|
113
|
+
await runDb(['migrate', ...rest]);
|
|
114
|
+
return;
|
|
115
|
+
case 'db:rollback':
|
|
116
|
+
await runDb(['rollback', ...rest]);
|
|
117
|
+
return;
|
|
118
|
+
case 'db:generate':
|
|
119
|
+
await runDb(['generate', ...rest]);
|
|
120
|
+
return;
|
|
121
|
+
case 'generate:action':
|
|
122
|
+
runGenerateAction(rest);
|
|
123
|
+
return;
|
|
124
|
+
case 'generate:crud':
|
|
125
|
+
runGenerateCrud(rest);
|
|
126
|
+
return;
|
|
127
|
+
case 'generate:feature':
|
|
128
|
+
runGenerateFeature(rest);
|
|
129
|
+
return;
|
|
130
|
+
case 'generate:openapi':
|
|
131
|
+
await runGenerateOpenApi(rest);
|
|
132
|
+
return;
|
|
133
|
+
case 'generate:tests-runner':
|
|
134
|
+
runGenerateInlineTestsRunner(rest);
|
|
135
|
+
return;
|
|
136
|
+
default:
|
|
137
|
+
console.error(`Unknown command: ${command}`);
|
|
138
|
+
console.error('Run "honertia --help" for usage');
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const isMain = process.argv[1]
|
|
143
|
+
? resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
144
|
+
: false;
|
|
145
|
+
if (isMain) {
|
|
146
|
+
runCli().catch((error) => {
|
|
147
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
150
|
+
}
|
package/dist/cli/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/cli/db.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;IAC1C;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAC7C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,mBAAmB,GAAG,mBAAmB,CAEpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAM/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,QAAQ,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC,CAkCtF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/cli/db.ts"],"names":[],"mappings":"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"}
|