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.
Files changed (59) hide show
  1. package/README.md +123 -12
  2. package/dist/__tmp_db_safety_probe.d.ts +2 -0
  3. package/dist/__tmp_db_safety_probe.d.ts.map +1 -0
  4. package/dist/__tmp_db_safety_probe.js +13 -0
  5. package/dist/__tmp_scoped_astrusted_probe.d.ts +2 -0
  6. package/dist/__tmp_scoped_astrusted_probe.d.ts.map +1 -0
  7. package/dist/__tmp_scoped_astrusted_probe.js +10 -0
  8. package/dist/__tmp_scoped_derived_probe.d.ts +2 -0
  9. package/dist/__tmp_scoped_derived_probe.d.ts.map +1 -0
  10. package/dist/__tmp_scoped_derived_probe.js +12 -0
  11. package/dist/__tmp_scoped_mutation_probe.d.ts +2 -0
  12. package/dist/__tmp_scoped_mutation_probe.d.ts.map +1 -0
  13. package/dist/__tmp_scoped_mutation_probe.js +15 -0
  14. package/dist/cache.d.ts +3 -1
  15. package/dist/cache.d.ts.map +1 -1
  16. package/dist/cache.js +14 -3
  17. package/dist/cli/bin.d.ts +8 -0
  18. package/dist/cli/bin.d.ts.map +1 -0
  19. package/dist/cli/bin.js +150 -0
  20. package/dist/cli/db.d.ts.map +1 -1
  21. package/dist/cli/db.js +83 -18
  22. package/dist/cli/feature.d.ts +1 -0
  23. package/dist/cli/feature.d.ts.map +1 -1
  24. package/dist/cli/feature.js +76 -11
  25. package/dist/cli/generate.d.ts.map +1 -1
  26. package/dist/cli/generate.js +124 -22
  27. package/dist/cli/index.d.ts +6 -2
  28. package/dist/cli/index.d.ts.map +1 -1
  29. package/dist/cli/index.js +16 -4
  30. package/dist/cli/inline-tests.d.ts.map +1 -1
  31. package/dist/cli/inline-tests.js +4 -0
  32. package/dist/cli/openapi.d.ts +2 -1
  33. package/dist/cli/openapi.d.ts.map +1 -1
  34. package/dist/cli/openapi.js +68 -3
  35. package/dist/effect/action.d.ts +92 -3
  36. package/dist/effect/action.d.ts.map +1 -1
  37. package/dist/effect/action.js +38 -32
  38. package/dist/effect/auth.d.ts +3 -1
  39. package/dist/effect/auth.d.ts.map +1 -1
  40. package/dist/effect/auth.js +17 -3
  41. package/dist/effect/bridge.d.ts +5 -0
  42. package/dist/effect/bridge.d.ts.map +1 -1
  43. package/dist/effect/bridge.js +5 -2
  44. package/dist/effect/index.d.ts +2 -2
  45. package/dist/effect/index.d.ts.map +1 -1
  46. package/dist/effect/index.js +2 -2
  47. package/dist/effect/routing.d.ts.map +1 -1
  48. package/dist/effect/routing.js +14 -14
  49. package/dist/effect/services.d.ts +3 -0
  50. package/dist/effect/services.d.ts.map +1 -1
  51. package/dist/effect/validation.d.ts +10 -1
  52. package/dist/effect/validation.d.ts.map +1 -1
  53. package/dist/effect/validation.js +49 -14
  54. package/dist/schema.d.ts +1 -1
  55. package/dist/schema.d.ts.map +1 -1
  56. package/dist/schema.js +1 -1
  57. package/dist/setup.d.ts.map +1 -1
  58. package/dist/setup.js +3 -2
  59. 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 # Rollback last migration
121
- honertia db rollback --preview # Preview rollback SQL
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 keys = yield* cache.list({ prefix: 'user:' })
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 { keys: keys.map((name) => ({ name })) }
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
- Effect.tryPromise(() =>
2134
- db.insert(auditLogs).values({
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 tasks are skipped
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__tmp_db_safety_probe.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__tmp_scoped_astrusted_probe.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__tmp_scoped_derived_probe.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__tmp_scoped_mutation_probe.d.ts.map
@@ -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, the refresh happens on the next request.
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
@@ -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,CAuDtH,CAAA;AAEJ;;;;;;;;;;;;;GAaG;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,CAWjD,CAAA"}
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
- return entry.v;
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
- const list = yield* cacheService.list({ prefix });
224
- yield* Effect.forEach(list.keys, (key) => cacheService.delete(key.name), { concurrency: 10 });
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,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Honertia CLI entrypoint.
4
+ *
5
+ * This powers the `honertia` executable distributed with the package.
6
+ */
7
+ export declare function runCli(args?: string[]): Promise<void>;
8
+ //# sourceMappingURL=bin.d.ts.map
@@ -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"}
@@ -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
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/cli/db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/cli/db.ts"],"names":[],"mappings":"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"}