@timothyrusso/effect-core 0.1.0

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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/dist/config/makeConfig.d.ts +43 -0
  4. package/dist/config/makeConfig.d.ts.map +1 -0
  5. package/dist/config/makeConfig.js +44 -0
  6. package/dist/config/makeConfig.js.map +1 -0
  7. package/dist/errors/appError.d.ts +55 -0
  8. package/dist/errors/appError.d.ts.map +1 -0
  9. package/dist/errors/appError.js +31 -0
  10. package/dist/errors/appError.js.map +1 -0
  11. package/dist/errors/messageKeys.d.ts +36 -0
  12. package/dist/errors/messageKeys.d.ts.map +1 -0
  13. package/dist/errors/messageKeys.js +26 -0
  14. package/dist/errors/messageKeys.js.map +1 -0
  15. package/dist/errors/unexpected.d.ts +26 -0
  16. package/dist/errors/unexpected.d.ts.map +1 -0
  17. package/dist/errors/unexpected.js +16 -0
  18. package/dist/errors/unexpected.js.map +1 -0
  19. package/dist/index.d.ts +15 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +15 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/logger/logger.d.ts +25 -0
  24. package/dist/logger/logger.d.ts.map +1 -0
  25. package/dist/logger/logger.js +27 -0
  26. package/dist/logger/logger.js.map +1 -0
  27. package/dist/react/EffectRuntimeProvider.d.ts +22 -0
  28. package/dist/react/EffectRuntimeProvider.d.ts.map +1 -0
  29. package/dist/react/EffectRuntimeProvider.js +17 -0
  30. package/dist/react/EffectRuntimeProvider.js.map +1 -0
  31. package/dist/react/index.d.ts +10 -0
  32. package/dist/react/index.d.ts.map +1 -0
  33. package/dist/react/index.js +9 -0
  34. package/dist/react/index.js.map +1 -0
  35. package/dist/react/register.d.ts +30 -0
  36. package/dist/react/register.d.ts.map +1 -0
  37. package/dist/react/register.js +2 -0
  38. package/dist/react/register.js.map +1 -0
  39. package/dist/react/runAtBoundary.d.ts +13 -0
  40. package/dist/react/runAtBoundary.d.ts.map +1 -0
  41. package/dist/react/runAtBoundary.js +23 -0
  42. package/dist/react/runAtBoundary.js.map +1 -0
  43. package/dist/react/useEffectMutation.d.ts +19 -0
  44. package/dist/react/useEffectMutation.d.ts.map +1 -0
  45. package/dist/react/useEffectMutation.js +21 -0
  46. package/dist/react/useEffectMutation.js.map +1 -0
  47. package/dist/react/useEffectQuery.d.ts +20 -0
  48. package/dist/react/useEffectQuery.d.ts.map +1 -0
  49. package/dist/react/useEffectQuery.js +22 -0
  50. package/dist/react/useEffectQuery.js.map +1 -0
  51. package/dist/react/useErrorMessage.d.ts +13 -0
  52. package/dist/react/useErrorMessage.d.ts.map +1 -0
  53. package/dist/react/useErrorMessage.js +15 -0
  54. package/dist/react/useErrorMessage.js.map +1 -0
  55. package/dist/runtime/makeAppRuntime.d.ts +38 -0
  56. package/dist/runtime/makeAppRuntime.d.ts.map +1 -0
  57. package/dist/runtime/makeAppRuntime.js +26 -0
  58. package/dist/runtime/makeAppRuntime.js.map +1 -0
  59. package/dist/sqlite/migrations.d.ts +33 -0
  60. package/dist/sqlite/migrations.d.ts.map +1 -0
  61. package/dist/sqlite/migrations.js +49 -0
  62. package/dist/sqlite/migrations.js.map +1 -0
  63. package/dist/sqlite/sqliteClient.d.ts +63 -0
  64. package/dist/sqlite/sqliteClient.d.ts.map +1 -0
  65. package/dist/sqlite/sqliteClient.js +37 -0
  66. package/dist/sqlite/sqliteClient.js.map +1 -0
  67. package/dist/testing/clock.d.ts +16 -0
  68. package/dist/testing/clock.d.ts.map +1 -0
  69. package/dist/testing/clock.js +18 -0
  70. package/dist/testing/clock.js.map +1 -0
  71. package/dist/testing/collectLogs.d.ts +32 -0
  72. package/dist/testing/collectLogs.d.ts.map +1 -0
  73. package/dist/testing/collectLogs.js +31 -0
  74. package/dist/testing/collectLogs.js.map +1 -0
  75. package/dist/testing/index.d.ts +12 -0
  76. package/dist/testing/index.d.ts.map +1 -0
  77. package/dist/testing/index.js +12 -0
  78. package/dist/testing/index.js.map +1 -0
  79. package/dist/testing/nodeSqlite.d.ts +27 -0
  80. package/dist/testing/nodeSqlite.d.ts.map +1 -0
  81. package/dist/testing/nodeSqlite.js +74 -0
  82. package/dist/testing/nodeSqlite.js.map +1 -0
  83. package/dist/testing/runTest.d.ts +25 -0
  84. package/dist/testing/runTest.d.ts.map +1 -0
  85. package/dist/testing/runTest.js +18 -0
  86. package/dist/testing/runTest.js.map +1 -0
  87. package/package.json +58 -0
@@ -0,0 +1,31 @@
1
+ import { Effect, Layer } from 'effect';
2
+ import { isAppError } from '../errors/appError.js';
3
+ import { Logger } from '../logger/logger.js';
4
+ /**
5
+ * A `Logger` that records instead of printing, to assert what the boundary logged.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const logs = collectLogs();
10
+ * const runtime = makeAppRuntime(Layer.merge(logs.layer, TripsTestLayer));
11
+ * expect(logs.entries).toHaveLength(1);
12
+ * ```
13
+ */
14
+ export function collectLogs() {
15
+ const entries = [];
16
+ const record = (entry) => Effect.sync(() => void entries.push(entry));
17
+ const layer = Layer.succeed(Logger, {
18
+ error: (error, context = {}) => record({ level: 'error', message: isAppError(error) ? error._tag : String(error), error, context }),
19
+ warn: (message, context = {}) => record({ level: 'warn', message, error: undefined, context }),
20
+ info: (message, context = {}) => record({ level: 'info', message, error: undefined, context }),
21
+ debug: (message, context = {}) => record({ level: 'debug', message, error: undefined, context }),
22
+ });
23
+ return {
24
+ layer,
25
+ entries,
26
+ clear: () => {
27
+ entries.length = 0;
28
+ },
29
+ };
30
+ }
31
+ //# sourceMappingURL=collectLogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectLogs.js","sourceRoot":"","sources":["../../src/testing/collectLogs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAmB,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsB9D;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;QAClC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC7B,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACrG,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;QAC9F,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;QAC9F,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;KACjG,CAAC,CAAC;IACH,OAAO;QACL,KAAK;QACL,OAAO;QACP,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @timothyrusso/effect-core/testing. Jest helpers: `itEffect` and `runTest`, an in-memory
3
+ * `node:sqlite` `SqliteClient`, a collecting `Logger` and `TestClock` control. Import it only from
4
+ * tests: it loads `node:sqlite`, which Metro cannot bundle.
5
+ */
6
+ /** Effect's `TestClock` and `TestContext`, re-exported for tests that drive time by hand. */
7
+ export { TestClock, TestContext } from 'effect';
8
+ export { advanceClock } from './clock.js';
9
+ export { type CollectedLogs, collectLogs, type LogEntry } from './collectLogs.js';
10
+ export { makeNodeSqliteDatabase, makeNodeSqliteLayer, type NodeSqliteDatabase, type NodeSqliteOptions, } from './nodeSqlite.js';
11
+ export { type EffectOrThunk, itEffect, runTest } from './runTest.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6FAA6F;AAC7F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,KAAK,aAAa,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @timothyrusso/effect-core/testing. Jest helpers: `itEffect` and `runTest`, an in-memory
3
+ * `node:sqlite` `SqliteClient`, a collecting `Logger` and `TestClock` control. Import it only from
4
+ * tests: it loads `node:sqlite`, which Metro cannot bundle.
5
+ */
6
+ /** Effect's `TestClock` and `TestContext`, re-exported for tests that drive time by hand. */
7
+ export { TestClock, TestContext } from 'effect';
8
+ export { advanceClock } from './clock.js';
9
+ export { collectLogs } from './collectLogs.js';
10
+ export { makeNodeSqliteDatabase, makeNodeSqliteLayer, } from './nodeSqlite.js';
11
+ export { itEffect, runTest } from './runTest.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6FAA6F;AAC7F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAsB,WAAW,EAAiB,MAAM,kBAAkB,CAAC;AAClF,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAsB,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Layer } from 'effect';
2
+ import { SqliteClient, type SqliteDatabase } from '../sqlite/sqliteClient.js';
3
+ /** A {@link SqliteDatabase} over `node:sqlite`, plus `close`. */
4
+ export interface NodeSqliteDatabase extends SqliteDatabase {
5
+ /** Closes the connection. The Layer does this when it is released. */
6
+ close(): void;
7
+ }
8
+ /** Options for {@link makeNodeSqliteDatabase}. */
9
+ export interface NodeSqliteOptions {
10
+ /** A file path, or `:memory:` (the default). */
11
+ readonly path?: string;
12
+ /** Runs `PRAGMA foreign_keys = ON` on open. Default `true`, as most apps enable it. */
13
+ readonly foreignKeys?: boolean;
14
+ }
15
+ /**
16
+ * An in-memory SQLite database behind the {@link SqliteDatabase} interface, so repositories and
17
+ * migrations run real SQL in jest. `node:sqlite` ships with Node 22; it is synchronous, and each
18
+ * async method resolves with its answer. Rows are plain objects. Transactions run on the one
19
+ * connection, which is enough to test that a failure rolls everything back.
20
+ */
21
+ export declare function makeNodeSqliteDatabase(options?: NodeSqliteOptions): NodeSqliteDatabase;
22
+ /**
23
+ * A `SqliteClient` Layer over a fresh {@link makeNodeSqliteDatabase}, closed when the Layer is
24
+ * released. Each `runTest` or `itEffect` builds its own, so tests never share a database.
25
+ */
26
+ export declare function makeNodeSqliteLayer(options?: NodeSqliteOptions): Layer.Layer<SqliteClient>;
27
+ //# sourceMappingURL=nodeSqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeSqlite.d.ts","sourceRoot":"","sources":["../../src/testing/nodeSqlite.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAiC,MAAM,2BAA2B,CAAC;AAE7G,iEAAiE;AACjE,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAmBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAqD1F;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAQ9F"}
@@ -0,0 +1,74 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { Effect, Layer } from 'effect';
3
+ import { SqliteClient } from '../sqlite/sqliteClient.js';
4
+ function bindValue(value) {
5
+ if (typeof value === 'boolean')
6
+ return value ? 1 : 0;
7
+ if (value instanceof ArrayBuffer)
8
+ return new Uint8Array(value);
9
+ return value;
10
+ }
11
+ /** Calls `positional` with `?` parameters or `named` with `$name` parameters. */
12
+ function bind(params, positional, named) {
13
+ if (params === undefined)
14
+ return positional([]);
15
+ if (Array.isArray(params))
16
+ return positional(params.map(bindValue));
17
+ return named(Object.fromEntries(Object.entries(params).map(([key, value]) => [key, bindValue(value)])));
18
+ }
19
+ /**
20
+ * An in-memory SQLite database behind the {@link SqliteDatabase} interface, so repositories and
21
+ * migrations run real SQL in jest. `node:sqlite` ships with Node 22; it is synchronous, and each
22
+ * async method resolves with its answer. Rows are plain objects. Transactions run on the one
23
+ * connection, which is enough to test that a failure rolls everything back.
24
+ */
25
+ export function makeNodeSqliteDatabase(options = {}) {
26
+ const db = new DatabaseSync(options.path ?? ':memory:');
27
+ if (options.foreignKeys ?? true)
28
+ db.exec('PRAGMA foreign_keys = ON;');
29
+ async function transaction(task, begin) {
30
+ db.exec(begin);
31
+ try {
32
+ await task();
33
+ db.exec('COMMIT');
34
+ }
35
+ catch (error) {
36
+ db.exec('ROLLBACK');
37
+ throw error;
38
+ }
39
+ }
40
+ const handle = {
41
+ async execAsync(source) {
42
+ db.exec(source);
43
+ },
44
+ async runAsync(source, params) {
45
+ const statement = db.prepare(source);
46
+ const result = bind(params, values => statement.run(...values), values => statement.run(values));
47
+ return { lastInsertRowId: Number(result.lastInsertRowid), changes: Number(result.changes) };
48
+ },
49
+ async getAllAsync(source, params) {
50
+ const statement = db.prepare(source);
51
+ const rows = bind(params, values => statement.all(...values), values => statement.all(values));
52
+ return rows.map(row => ({ ...row }));
53
+ },
54
+ async getFirstAsync(source, params) {
55
+ const statement = db.prepare(source);
56
+ const row = bind(params, values => statement.get(...values), values => statement.get(values));
57
+ return row === undefined ? null : { ...row };
58
+ },
59
+ withTransactionAsync: task => transaction(task, 'BEGIN'),
60
+ withExclusiveTransactionAsync: task => transaction(() => task(handle), 'BEGIN EXCLUSIVE'),
61
+ close() {
62
+ db.close();
63
+ },
64
+ };
65
+ return handle;
66
+ }
67
+ /**
68
+ * A `SqliteClient` Layer over a fresh {@link makeNodeSqliteDatabase}, closed when the Layer is
69
+ * released. Each `runTest` or `itEffect` builds its own, so tests never share a database.
70
+ */
71
+ export function makeNodeSqliteLayer(options = {}) {
72
+ return Layer.scoped(SqliteClient, Effect.acquireRelease(Effect.sync(() => makeNodeSqliteDatabase(options)), db => Effect.sync(() => db.close())));
73
+ }
74
+ //# sourceMappingURL=nodeSqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeSqlite.js","sourceRoot":"","sources":["../../src/testing/nodeSqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAsD,MAAM,2BAA2B,CAAC;AAgB7G,SAAS,SAAS,CAAC,KAAe;IAChC,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,KAAK,YAAY,WAAW;QAAE,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iFAAiF;AACjF,SAAS,IAAI,CACX,MAA6B,EAC7B,UAA0C,EAC1C,KAAmD;IAEnD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1G,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAA6B,EAAE;IACpE,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,WAAW,IAAI,IAAI;QAAE,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAEtE,KAAK,UAAU,WAAW,CAAC,IAAyB,EAAE,KAAa;QACjE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACf,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;YACb,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAuB;QACjC,KAAK,CAAC,SAAS,CAAC,MAAM;YACpB,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;YAC3B,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CACjB,MAAM,EACN,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAClC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAChC,CAAC;YACF,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9F,CAAC;QACD,KAAK,CAAC,WAAW,CAAI,MAAc,EAAE,MAAkB;YACrD,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CACf,MAAM,EACN,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAClC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAChC,CAAC;YACF,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAM,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,CAAC,aAAa,CAAI,MAAc,EAAE,MAAkB;YACvD,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,GAAG,GAAG,IAAI,CACd,MAAM,EACN,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAClC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAChC,CAAC;YACF,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,GAAG,GAAG,EAAQ,CAAC;QACtD,CAAC;QACD,oBAAoB,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC;QACxD,6BAA6B,EAAE,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;QACzF,KAAK;YACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;KACF,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA6B,EAAE;IACjE,OAAO,KAAK,CAAC,MAAM,CACjB,YAAY,EACZ,MAAM,CAAC,cAAc,CACnB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,EAClD,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CACpC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Effect, type Layer } from 'effect';
2
+ /** An Effect, or a function building one per run (so each test starts from fresh state). */
3
+ export type EffectOrThunk<A, E, R> = Effect.Effect<A, E, R> | (() => Effect.Effect<A, E, R>);
4
+ /**
5
+ * Runs `effect` with `layer` and the Effect test services (`TestClock` included: time stands
6
+ * still until the test moves it with `advanceClock`). Resolves with the value; rejects with the
7
+ * failure, as `Effect.runPromise`. The Layer is built for this run and released after it.
8
+ */
9
+ export declare function runTest<A, E>(effect: EffectOrThunk<A, E, never>): Promise<A>;
10
+ export declare function runTest<A, E, R, LE>(effect: EffectOrThunk<A, E, R>, layer: Layer.Layer<R, LE, never>): Promise<A>;
11
+ /**
12
+ * A jest test whose body is an Effect, run with {@link runTest}. The test fails with the
13
+ * Effect's failure or defect.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * itEffect('saves a trip', Effect.gen(function* () {
18
+ * yield* saveTrip(trip);
19
+ * expect(yield* listTrips).toEqual([trip]);
20
+ * }), TripsTestLayer);
21
+ * ```
22
+ */
23
+ export declare function itEffect<A, E>(name: string, effect: EffectOrThunk<A, E, never>): void;
24
+ export declare function itEffect<A, E, R, LE>(name: string, effect: EffectOrThunk<A, E, R>, layer: Layer.Layer<R, LE, never>): void;
25
+ //# sourceMappingURL=runTest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTest.d.ts","sourceRoot":"","sources":["../../src/testing/runTest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAe,MAAM,QAAQ,CAAC;AAEzD,4FAA4F;AAC5F,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAQ7F;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAC9E,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAOnH;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;AACvF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,GAC/B,IAAI,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Effect, TestContext } from 'effect';
2
+ function build(effect) {
3
+ return Effect.isEffect(effect) ? effect : Effect.suspend(effect);
4
+ }
5
+ export function runTest(effect, layer) {
6
+ const program = layer === undefined ? build(effect) : Effect.provide(build(effect), layer);
7
+ // NOTE: without a layer R is never (the first overload), so the provide below leaves nothing open.
8
+ return Effect.runPromise(Effect.provide(program, TestContext.TestContext));
9
+ }
10
+ export function itEffect(name, effect, layer) {
11
+ const it = globalThis.it;
12
+ if (it === undefined)
13
+ throw new Error('itEffect runs inside jest, where `it` is a global');
14
+ it(name, async () => {
15
+ await (layer === undefined ? runTest(effect) : runTest(effect, layer));
16
+ });
17
+ }
18
+ //# sourceMappingURL=runTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runTest.js","sourceRoot":"","sources":["../../src/testing/runTest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,QAAQ,CAAC;AAOzD,SAAS,KAAK,CAAU,MAA8B;IACpD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACnE,CAAC;AASD,MAAM,UAAU,OAAO,CAAc,MAA8B,EAAE,KAAiC;IACpG,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3F,mGAAmG;IACnG,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,OAAmC,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;AACzG,CAAC;AAoBD,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,MAA8B,EAC9B,KAAiC;IAEjC,MAAM,EAAE,GAAI,UAA8B,CAAC,EAAE,CAAC;IAC9C,IAAI,EAAE,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC3F,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAClB,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAoC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@timothyrusso/effect-core",
3
+ "version": "0.1.0",
4
+ "description": "Shared Effect layer: AppError, Logger, Config, runtime, SqliteClient, React hooks and testing helpers.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/timothyrusso/agentic-kit.git",
10
+ "directory": "packages/effect-core"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./react": {
18
+ "types": "./dist/react/index.d.ts",
19
+ "default": "./dist/react/index.js"
20
+ },
21
+ "./testing": {
22
+ "types": "./dist/testing/index.d.ts",
23
+ "default": "./dist/testing/index.js"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "engines": {
33
+ "node": ">=22"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "sideEffects": false,
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.build.json",
41
+ "typecheck": "tsc --noEmit",
42
+ "test": "NODE_OPTIONS=\"--no-warnings=ExperimentalWarning\" jest"
43
+ },
44
+ "peerDependencies": {
45
+ "@tanstack/react-query": "^5.0.0",
46
+ "effect": "^3.22.0",
47
+ "react": "^18.0.0 || ^19.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@tanstack/react-query": "^5.104.0",
51
+ "@testing-library/react": "^16.3.3",
52
+ "@types/react": "^19.3.0",
53
+ "effect": "^3.22.2",
54
+ "jest-environment-jsdom": "^30.5.2",
55
+ "react": "^19.3.0",
56
+ "react-dom": "^19.3.0"
57
+ }
58
+ }