@zemnmez/future 0.0.0 → 1.0.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.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # `@zemnmez/future`
2
+
3
+ `@zemnmez/future` is a tiny TypeScript type for a value that may be loading,
4
+ resolved, or errored. A `Future<Then, Loading, Error>` represents all three
5
+ states exactly and requires each state to be handled before its resolved value
6
+ can be used.
7
+
8
+ Like [`@zemnmez/result`][result], `Future` is implemented purely functionally.
9
+ Advanced JavaScript and TypeScript compilers can inline its implementation, so
10
+ no class names, property names, discriminants, or symbols identifying a Future
11
+ need to remain in a minified bundle.
12
+
13
+ ## React Query
14
+
15
+ `useQueryFuture` converts a React Query result into a `Future`. This is useful
16
+ when a component should call all of its hooks unconditionally and select its
17
+ loading, error, or resolved UI only after those hooks have run:
18
+
19
+ ```tsx
20
+ import { useQuery } from '@tanstack/react-query';
21
+ import { future, useQueryFuture } from '@zemnmez/future';
22
+
23
+ interface Todo {
24
+ id: number;
25
+ title: string;
26
+ }
27
+
28
+ function TodoList() {
29
+ const theme = useTheme();
30
+ const todos = useQueryFuture(
31
+ useQuery<Todo[], Error>({
32
+ queryKey: ['todos'],
33
+ queryFn: async () => {
34
+ const response = await fetch('/api/todos');
35
+ if (!response.ok) throw new Error('Could not load todos.');
36
+ return response.json();
37
+ },
38
+ })
39
+ );
40
+
41
+ return future(
42
+ todos,
43
+ todos => (
44
+ <ul className={theme.todoList}>
45
+ {todos.map(todo => (
46
+ <li key={todo.id}>{todo.title}</li>
47
+ ))}
48
+ </ul>
49
+ ),
50
+ () => <p>Loading todos…</p>,
51
+ error => <p className={theme.error}>{error.message}</p>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ Construct a Future with `resolve`, `loading`, or `error`, then handle its three
59
+ possible states with `future`:
60
+
61
+ ```typescript
62
+ import { Future, future, resolve } from '@zemnmez/future';
63
+
64
+ const answer: Future<number, number, Error> = resolve(42);
65
+ const message = future(
66
+ answer,
67
+ value => `The answer is ${value}.`,
68
+ progress => `Loading: ${progress}%`,
69
+ error => `Failed: ${error.message}`
70
+ );
71
+ ```
72
+
73
+ `future_and_then` maps a resolved value while preserving the loading and error
74
+ types. `future_flatten_then` flattens nested Futures, and `future_collect`
75
+ combines several Futures into one.
76
+
77
+ [result]: https://www.npmjs.com/package/@zemnmez/result
package/future.d.ts ADDED
@@ -0,0 +1,69 @@
1
+ import type { UseQueryResult } from '@tanstack/react-query';
2
+ /**
3
+ * A value which hasn't arrived yet.
4
+ */
5
+ export type Future<Then, Loading, Error> = <T1, T2, T3>(
6
+ /**
7
+ * Executed when the {@link Future} succeeds.
8
+ */
9
+ then: (value: Then) => T1,
10
+ /**
11
+ * Executed when the {@link Future} is loading.
12
+ */
13
+ loading: (value: Loading) => T2,
14
+ /**
15
+ * Executed when the {@link Future} errors.
16
+ */
17
+ error: (value: Error) => T3) => T1 | T2 | T3;
18
+ /**
19
+ * A {@link Future} that is never loading
20
+ * and never errors.
21
+ */
22
+ export declare const resolve: <Then, Loading = never, Error = never>(then_value: Then) => Future<Then, Loading, Error>;
23
+ /**
24
+ * A {@link Future} that always errors.
25
+ */
26
+ export declare const error: <Error, Loading = never, Then = never>(error_value: Error) => Future<Then, Loading, Error>;
27
+ /**
28
+ * A {@link Future} that is always loading.
29
+ */
30
+ export declare const loading: <Loading, Then = never, Error = never>(loading_value: Loading) => Future<Then, Loading, Error>;
31
+ export { error as future_error, loading as future_loading, resolve as future_resolve, };
32
+ /**
33
+ * Convert a React Query result into a {@link Future}.
34
+ */
35
+ export declare function useQueryFuture<Then, Error>(result: UseQueryResult<Then, Error>): Future<Then, void, Error>;
36
+ /**
37
+ * Execute a {@link Future} with a set of handlers.
38
+ *
39
+ * This is identical to calling the future directly, but
40
+ * may read better.
41
+ */
42
+ export declare const future: <Then, Loading, Error, T1, T2, T3>(f: Future<Then, Loading, Error>, then: (value: Then) => T1, onLoading: (value: Loading) => T2, onError: (value: Error) => T3) => T1 | T2 | T3;
43
+ /**
44
+ * Modify the contained value of an {@link Future} on success.
45
+ */
46
+ export declare const future_and_then: <Then, Loading, Error, NewThen>(future: Future<Then, Loading, Error>, f_then: (value: Then) => NewThen) => Future<NewThen, Loading, Error>;
47
+ export declare const future_flatten_then: <Then, Loading1, Error1, Loading2, Error2>(a: Future<Future<Then, Loading1, Error1>, Loading2, Error2>) => Future<Then, Loading1 | Loading2, Error1 | Error2>;
48
+ export declare function future_collect<Then, Loading, Error>(futures: readonly Future<Then, Loading, Error>[]): Future<readonly Then[], Loading, Error>;
49
+ export declare function future_collect_incremental<Then, Loading, Error>(futures: readonly Future<Then, Loading, Error>[]): Future<readonly Then[], Loading, Error>;
50
+ export declare const coincide_then: <Then1, Loading1, Error1, Then2, Loading2, Error2, NewThen>(future1: Future<Then1, Loading1, Error1>, future2: Future<Then2, Loading2, Error2>, then: (a: Then1, b: Then2) => NewThen) => Future<NewThen, Loading1 | Loading2, Error1 | Error2>;
51
+ /**
52
+ * Declare that a {@link Future} depends on another {@link Future}.
53
+ *
54
+ * This can be used when it's not possible to {@link future_and_then} pipeline
55
+ * the future but they're co-dependent, such as when using useQuery (which
56
+ * cannot have functions returned if using `localStorage`).
57
+ *
58
+ * If you *don't* use this function to declare the dependency,
59
+ * then the child future will often show a loading state if
60
+ * the parent future is in error.
61
+ *
62
+ * (1) if the child has loaded then child is always returned
63
+ * (2) if child is in error and parent is in error,
64
+ * then parent error is returned
65
+ * usw
66
+ */
67
+ export declare function future_declare_dependency<T1, T2, L1, L2, E1, E2>(parent: Future<T1, L1, E1>, child: Future<T2, L2, E2>): Future<T2, L1 | L2, E1 | E2>;
68
+ export declare function future_or_else<T, L, E, E2>(fut: Future<T, L, E>, f: (e: E) => E2): Future<T, L, E2>;
69
+ //# sourceMappingURL=future.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"future.d.ts","sourceRoot":"","sources":["future.ts"],"names":[],"mappings":"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"}
package/future.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * A {@link Future} that is never loading
3
+ * and never errors.
4
+ */ export const resolve = (then_value)=>(then, _loading, _error)=>then(then_value);
5
+ /**
6
+ * A {@link Future} that always errors.
7
+ */ export const error = (error_value)=>(_then, _loading, error)=>error(error_value);
8
+ /**
9
+ * A {@link Future} that is always loading.
10
+ */ export const loading = (loading_value)=>(_then, loading, _error)=>loading(loading_value);
11
+ export { error as future_error, loading as future_loading, resolve as future_resolve };
12
+ /**
13
+ * Convert a React Query result into a {@link Future}.
14
+ */ export function useQueryFuture(result) {
15
+ switch(result.status){
16
+ case 'success':
17
+ return resolve(result.data);
18
+ case 'pending':
19
+ return loading(undefined);
20
+ case 'error':
21
+ return error(result.error);
22
+ }
23
+ }
24
+ /**
25
+ * Execute a {@link Future} with a set of handlers.
26
+ *
27
+ * This is identical to calling the future directly, but
28
+ * may read better.
29
+ */ export const future = (f, then, onLoading, onError)=>f(then, onLoading, onError);
30
+ /**
31
+ * Modify the contained value of an {@link Future} on success.
32
+ */ export const future_and_then = (future, f_then)=>future((actual)=>resolve(f_then(actual)), (l)=>loading(l), (e)=>error(e));
33
+ export const future_flatten_then = (a)=>a((then1)=>then1((then)=>resolve(then), (loading2)=>loading(loading2), (error2)=>error(error2)), (loading1)=>loading(loading1), (error1)=>error(error1));
34
+ export function future_collect(futures) {
35
+ return futures.reduce((collected, next)=>coincide_then(collected, next, (values, value)=>[
36
+ ...values,
37
+ value
38
+ ]), resolve([]));
39
+ }
40
+ export function future_collect_incremental(futures) {
41
+ const values = [];
42
+ const loadings = [];
43
+ const errors = [];
44
+ for (const future of futures){
45
+ future((value)=>values.push(value), (value)=>loadings.push(value), (value)=>errors.push(value));
46
+ }
47
+ if (errors.length > 0) {
48
+ return error(errors[0]);
49
+ }
50
+ if (values.length > 0) {
51
+ return resolve(values);
52
+ }
53
+ if (loadings.length > 0) {
54
+ return loading(loadings[0]);
55
+ }
56
+ return resolve(values);
57
+ }
58
+ export const coincide_then = (future1, future2, then)=>future1((then1)=>future2((then2)=>resolve(then(then1, then2)), (loading2)=>loading(loading2), (error2)=>error(error2)), (loading1)=>future2(()=>loading(loading1), ()=>loading(loading1), (error2)=>error(error2)), (error1)=>future2(()=>error(error1), ()=>error(error1), ()=>error(error1)));
59
+ /**
60
+ * Declare that a {@link Future} depends on another {@link Future}.
61
+ *
62
+ * This can be used when it's not possible to {@link future_and_then} pipeline
63
+ * the future but they're co-dependent, such as when using useQuery (which
64
+ * cannot have functions returned if using `localStorage`).
65
+ *
66
+ * If you *don't* use this function to declare the dependency,
67
+ * then the child future will often show a loading state if
68
+ * the parent future is in error.
69
+ *
70
+ * (1) if the child has loaded then child is always returned
71
+ * (2) if child is in error and parent is in error,
72
+ * then parent error is returned
73
+ * usw
74
+ */ export function future_declare_dependency(parent, child) {
75
+ return parent(()=>child((child_then)=>resolve(child_then), (child_loading)=>loading(child_loading), (child_error)=>error(child_error)), (parent_loading)=>child((child_then)=>resolve(child_then), ()=>loading(parent_loading), ()=>loading(parent_loading)), (parent_error)=>child((child_then)=>resolve(child_then), (child_loading)=>loading(child_loading), ()=>error(parent_error)));
76
+ }
77
+ export function future_or_else(fut, f) {
78
+ return fut((t)=>resolve(t), (l)=>loading(l), (e)=>error(f(e)));
79
+ }
80
+
81
+ //# sourceMappingURL=future.js.map
package/future.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["future.ts"],"sourcesContent":["import type { UseQueryResult } from '@tanstack/react-query';\n\n/**\n * A value which hasn't arrived yet.\n */\nexport type Future<Then, Loading, Error> = <T1, T2, T3>(\n\t/**\n\t * Executed when the {@link Future} succeeds.\n\t */\n\tthen: (value: Then) => T1,\n\t/**\n\t * Executed when the {@link Future} is loading.\n\t */\n\tloading: (value: Loading) => T2,\n\t/**\n\t * Executed when the {@link Future} errors.\n\t */\n\terror: (value: Error) => T3\n) => T1 | T2 | T3;\n\n/**\n * A {@link Future} that is never loading\n * and never errors.\n */\nexport const resolve =\n\t<Then, Loading = never, Error = never>(\n\t\tthen_value: Then\n\t): Future<Then, Loading, Error> =>\n\t(then, _loading, _error) =>\n\t\tthen(then_value);\n\n/**\n * A {@link Future} that always errors.\n */\nexport const error =\n\t<Error, Loading = never, Then = never>(\n\t\terror_value: Error\n\t): Future<Then, Loading, Error> =>\n\t(_then, _loading, error) =>\n\t\terror(error_value);\n\n/**\n * A {@link Future} that is always loading.\n */\nexport const loading =\n\t<Loading, Then = never, Error = never>(\n\t\tloading_value: Loading\n\t): Future<Then, Loading, Error> =>\n\t(_then, loading, _error) =>\n\t\tloading(loading_value);\n\nexport {\n\terror as future_error,\n\tloading as future_loading,\n\tresolve as future_resolve,\n};\n\n/**\n * Convert a React Query result into a {@link Future}.\n */\nexport function useQueryFuture<Then, Error>(\n\tresult: UseQueryResult<Then, Error>\n): Future<Then, void, Error> {\n\tswitch (result.status) {\n\t\tcase 'success':\n\t\t\treturn resolve(result.data);\n\t\tcase 'pending':\n\t\t\treturn loading(undefined);\n\t\tcase 'error':\n\t\t\treturn error(result.error);\n\t}\n}\n\n/**\n * Execute a {@link Future} with a set of handlers.\n *\n * This is identical to calling the future directly, but\n * may read better.\n */\nexport const future = <Then, Loading, Error, T1, T2, T3>(\n\tf: Future<Then, Loading, Error>,\n\tthen: (value: Then) => T1,\n\tonLoading: (value: Loading) => T2,\n\tonError: (value: Error) => T3\n): T1 | T2 | T3 => f(then, onLoading, onError);\n\n/**\n * Modify the contained value of an {@link Future} on success.\n */\nexport const future_and_then = <Then, Loading, Error, NewThen>(\n\tfuture: Future<Then, Loading, Error>,\n\tf_then: (value: Then) => NewThen\n): Future<NewThen, Loading, Error> =>\n\tfuture(\n\t\tactual => resolve(f_then(actual)),\n\t\tl => loading(l),\n\t\te => error(e)\n\t);\n\nexport const future_flatten_then = <Then, Loading1, Error1, Loading2, Error2>(\n\ta: Future<Future<Then, Loading1, Error1>, Loading2, Error2>\n): Future<Then, Loading1 | Loading2, Error1 | Error2> =>\n\ta(\n\t\tthen1 =>\n\t\t\tthen1(\n\t\t\t\tthen => resolve(then),\n\t\t\t\tloading2 => loading(loading2),\n\t\t\t\terror2 => error(error2)\n\t\t\t),\n\t\tloading1 => loading(loading1),\n\t\terror1 => error(error1)\n\t);\n\nexport function future_collect<Then, Loading, Error>(\n\tfutures: readonly Future<Then, Loading, Error>[]\n): Future<readonly Then[], Loading, Error> {\n\treturn futures.reduce<Future<readonly Then[], Loading, Error>>(\n\t\t(collected, next) =>\n\t\t\tcoincide_then(collected, next, (values, value) => [\n\t\t\t\t...values,\n\t\t\t\tvalue,\n\t\t\t]),\n\t\tresolve([])\n\t);\n}\n\nexport function future_collect_incremental<Then, Loading, Error>(\n\tfutures: readonly Future<Then, Loading, Error>[]\n): Future<readonly Then[], Loading, Error> {\n\tconst values: Then[] = [];\n\tconst loadings: Loading[] = [];\n\tconst errors: Error[] = [];\n\n\tfor (const future of futures) {\n\t\tfuture(\n\t\t\tvalue => values.push(value),\n\t\t\tvalue => loadings.push(value),\n\t\t\tvalue => errors.push(value)\n\t\t);\n\t}\n\n\tif (errors.length > 0) {\n\t\treturn error(errors[0]!);\n\t}\n\n\tif (values.length > 0) {\n\t\treturn resolve(values);\n\t}\n\n\tif (loadings.length > 0) {\n\t\treturn loading(loadings[0]!);\n\t}\n\n\treturn resolve(values);\n}\n\nexport const coincide_then = <\n\tThen1,\n\tLoading1,\n\tError1,\n\tThen2,\n\tLoading2,\n\tError2,\n\tNewThen,\n>(\n\tfuture1: Future<Then1, Loading1, Error1>,\n\tfuture2: Future<Then2, Loading2, Error2>,\n\tthen: (a: Then1, b: Then2) => NewThen\n): Future<NewThen, Loading1 | Loading2, Error1 | Error2> =>\n\tfuture1(\n\t\tthen1 =>\n\t\t\tfuture2(\n\t\t\t\tthen2 => resolve(then(then1, then2)),\n\t\t\t\tloading2 => loading(loading2),\n\t\t\t\terror2 => error(error2)\n\t\t\t),\n\t\tloading1 =>\n\t\t\tfuture2(\n\t\t\t\t() => loading(loading1),\n\t\t\t\t() => loading(loading1),\n\t\t\t\terror2 => error(error2)\n\t\t\t),\n\t\terror1 =>\n\t\t\tfuture2(\n\t\t\t\t() => error(error1),\n\t\t\t\t() => error(error1),\n\t\t\t\t() => error(error1)\n\t\t\t)\n\t);\n\n/**\n * Declare that a {@link Future} depends on another {@link Future}.\n *\n * This can be used when it's not possible to {@link future_and_then} pipeline\n * the future but they're co-dependent, such as when using useQuery (which\n * cannot have functions returned if using `localStorage`).\n *\n * If you *don't* use this function to declare the dependency,\n * then the child future will often show a loading state if\n * the parent future is in error.\n *\n * (1)\tif the child has loaded then child is always returned\n * (2)\tif child is in error and parent is in error,\n * \t\tthen parent error is returned\n * usw\n */\nexport function future_declare_dependency<T1, T2, L1, L2, E1, E2>(\n\tparent: Future<T1, L1, E1>,\n\tchild: Future<T2, L2, E2>\n): Future<T2, L1 | L2, E1 | E2> {\n\treturn parent(\n\t\t(/*parent_then*/) =>\n\t\t\tchild(\n\t\t\t\tchild_then => resolve(child_then),\n\t\t\t\tchild_loading => loading(child_loading),\n\t\t\t\tchild_error => error(child_error)\n\t\t\t),\n\t\tparent_loading =>\n\t\t\tchild(\n\t\t\t\tchild_then => resolve(child_then),\n\t\t\t\t(/*child_loading*/) => loading(parent_loading),\n\t\t\t\t(/*child_error*/) => loading(parent_loading)\n\t\t\t),\n\t\tparent_error =>\n\t\t\tchild(\n\t\t\t\tchild_then => resolve(child_then),\n\t\t\t\tchild_loading => loading(child_loading),\n\t\t\t\t(/*child_error*/) => error(parent_error)\n\t\t\t)\n\t);\n}\n\nexport function future_or_else<T, L, E, E2>(\n\tfut: Future<T, L, E>,\n\tf: (e: E) => E2\n): Future<T, L, E2> {\n\treturn fut(\n\t\tt => resolve(t),\n\t\tl => loading(l),\n\t\te => error(f(e))\n\t);\n}\n"],"names":["resolve","then_value","then","_loading","_error","error","error_value","_then","loading","loading_value","future_error","future_loading","future_resolve","useQueryFuture","result","status","data","undefined","future","f","onLoading","onError","future_and_then","f_then","actual","l","e","future_flatten_then","a","then1","loading2","error2","loading1","error1","future_collect","futures","reduce","collected","next","coincide_then","values","value","future_collect_incremental","loadings","errors","push","length","future1","future2","then2","future_declare_dependency","parent","child","child_then","child_loading","child_error","parent_loading","parent_error","future_or_else","fut","t"],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,88 @@
1
+ import type { UseQueryResult } from '@tanstack/react-query';
2
+
3
+ export declare const coincide_then: <Then1, Loading1, Error1, Then2, Loading2, Error2, NewThen>(future1: Future<Then1, Loading1, Error1>, future2: Future<Then2, Loading2, Error2>, then: (a: Then1, b: Then2) => NewThen) => Future<NewThen, Loading1 | Loading2, Error1 | Error2>;
4
+
5
+ /**
6
+ * A {@link Future} that always errors.
7
+ */
8
+ declare const error: <Error, Loading = never, Then = never>(error_value: Error) => Future<Then, Loading, Error>;
9
+ export { error }
10
+ export { error as future_error }
11
+
12
+ /**
13
+ * A value which hasn't arrived yet.
14
+ */
15
+ export declare type Future<Then, Loading, Error> = <T1, T2, T3>(
16
+ /**
17
+ * Executed when the {@link Future} succeeds.
18
+ */
19
+ then: (value: Then) => T1,
20
+ /**
21
+ * Executed when the {@link Future} is loading.
22
+ */
23
+ loading: (value: Loading) => T2,
24
+ /**
25
+ * Executed when the {@link Future} errors.
26
+ */
27
+ error: (value: Error) => T3) => T1 | T2 | T3;
28
+
29
+ /**
30
+ * Execute a {@link Future} with a set of handlers.
31
+ *
32
+ * This is identical to calling the future directly, but
33
+ * may read better.
34
+ */
35
+ export declare const future: <Then, Loading, Error, T1, T2, T3>(f: Future<Then, Loading, Error>, then: (value: Then) => T1, onLoading: (value: Loading) => T2, onError: (value: Error) => T3) => T1 | T2 | T3;
36
+
37
+ /**
38
+ * Modify the contained value of an {@link Future} on success.
39
+ */
40
+ export declare const future_and_then: <Then, Loading, Error, NewThen>(future: Future<Then, Loading, Error>, f_then: (value: Then) => NewThen) => Future<NewThen, Loading, Error>;
41
+
42
+ export declare function future_collect<Then, Loading, Error>(futures: readonly Future<Then, Loading, Error>[]): Future<readonly Then[], Loading, Error>;
43
+
44
+ export declare function future_collect_incremental<Then, Loading, Error>(futures: readonly Future<Then, Loading, Error>[]): Future<readonly Then[], Loading, Error>;
45
+
46
+ /**
47
+ * Declare that a {@link Future} depends on another {@link Future}.
48
+ *
49
+ * This can be used when it's not possible to {@link future_and_then} pipeline
50
+ * the future but they're co-dependent, such as when using useQuery (which
51
+ * cannot have functions returned if using `localStorage`).
52
+ *
53
+ * If you *don't* use this function to declare the dependency,
54
+ * then the child future will often show a loading state if
55
+ * the parent future is in error.
56
+ *
57
+ * (1) if the child has loaded then child is always returned
58
+ * (2) if child is in error and parent is in error,
59
+ * then parent error is returned
60
+ * usw
61
+ */
62
+ export declare function future_declare_dependency<T1, T2, L1, L2, E1, E2>(parent: Future<T1, L1, E1>, child: Future<T2, L2, E2>): Future<T2, L1 | L2, E1 | E2>;
63
+
64
+ export declare const future_flatten_then: <Then, Loading1, Error1, Loading2, Error2>(a: Future<Future<Then, Loading1, Error1>, Loading2, Error2>) => Future<Then, Loading1 | Loading2, Error1 | Error2>;
65
+
66
+ export declare function future_or_else<T, L, E, E2>(fut: Future<T, L, E>, f: (e: E) => E2): Future<T, L, E2>;
67
+
68
+ /**
69
+ * A {@link Future} that is always loading.
70
+ */
71
+ declare const loading: <Loading, Then = never, Error = never>(loading_value: Loading) => Future<Then, Loading, Error>;
72
+ export { loading as future_loading }
73
+ export { loading }
74
+
75
+ /**
76
+ * A {@link Future} that is never loading
77
+ * and never errors.
78
+ */
79
+ declare const resolve: <Then, Loading = never, Error = never>(then_value: Then) => Future<Then, Loading, Error>;
80
+ export { resolve as future_resolve }
81
+ export { resolve }
82
+
83
+ /**
84
+ * Convert a React Query result into a {@link Future}.
85
+ */
86
+ export declare function useQueryFuture<Then, Error>(result: UseQueryResult<Then, Error>): Future<Then, void, Error>;
87
+
88
+ export { }
package/package.json CHANGED
@@ -1,9 +1,38 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/package.json",
2
3
  "name": "@zemnmez/future",
3
- "version": "0.0.0",
4
- "description": "Reserved for the forthcoming @zemnmez/future package",
4
+ "description": "A tiny, purely functional TypeScript type for values that may be loading, resolved, or errored",
5
+ "main": "future.js",
6
+ "type": "module",
7
+ "types": "index.d.ts",
8
+ "author": "zemnmez",
5
9
  "license": "MIT",
10
+ "keywords": [
11
+ "typescript",
12
+ "future",
13
+ "react",
14
+ "react-query",
15
+ "functional"
16
+ ],
17
+ "sideEffects": false,
6
18
  "publishConfig": {
7
19
  "access": "public"
20
+ },
21
+ "version": "1.0.0",
22
+ "dependencies": {
23
+ "@tanstack/react-query": "5.101.4",
24
+ "react": "19.2.8"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "24.13.3",
28
+ "@types/react": "18.3.31"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/zemn-me/monorepo.git",
33
+ "directory": "ts/future"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/zemn-me/monorepo/issues/new?title=%2F%2Fts%2Ffuture%401.0.0%3A+something+went+wrong%21"
8
37
  }
9
- }
38
+ }