/
types.ts
136 lines (126 loc) · 3.55 KB
/
types.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import type Owner from '@ember/owner';
import type { Invoke } from '@glint/template/-private/integration';
export const INTERMEDIATE_VALUE = '__Intermediate_Value__';
export const INTERNAL = '__INTERNAL__';
export interface InternalFunctionResourceConfig<Value = unknown> {
definition: ResourceFunction<Value>;
type: 'function-based';
[INTERNAL]: true;
}
export const CURRENT = Symbol('ember-resources::CURRENT');
export interface GlintRenderable {
/**
* Cells aren't inherently understood by Glint,
* so to work around that, we'll hook in to the fact that
* ContentValue (the type expected for all renderables),
* defines an interface with this signature.
*
* (SafeString)
*
* There *has* been interest in the community to formally support
* toString and toHTML APIs across all objects. An RFC needs to be
* written so that we can gather feedback / potential problems.
*/
toHTML(): string;
}
// Will need to be a class for .current flattening / auto-rendering
export interface Reactive<Value> extends GlintRenderable {
current: Value;
[CURRENT]: Value;
[Invoke]?: Value;
}
/**
* This is the type of the arguments passed to the `resource` function
*
* ```ts
* import { resource, type ResourceAPI } from 'ember-resources';
*
* export const Clock = resource((api: ResourceAPI) => {
* let { on, use, owner } = api;
*
* // ...
* })
* ```
*/
export type ResourceAPI = {
on: {
/**
* Optionally a function-resource can provide a cleanup function.
*
*
* Example:
* ```js
* import { resource } from 'ember-resources';
* import { TrackedObject } from 'tracked-built-ins';
*
* const load = resource(({ on }) => {
* let state = new TrackedObject({});
* let controller = new AbortController();
*
* on.cleanup(() => controller.abort());
*
* fetch(this.url, { signal: controller.signal })
* // ...
*
* return state;
* })
*/
cleanup: (destroyer: Destructor) => void;
};
/**
* Allows for composition of resources.
*
* Example:
* ```js
* let formatter = new Intl.DateTimeFormat("en-US", {
* hour: "numeric",
* minute: "numeric",
* second: "numeric",
* hour12: false,
* });
* let format = (time: Reactive<Date>) => formatter.format(time.current);
*
* const Now = resource(({ on }) => {
* let now = cell(nowDate);
* let timer = setInterval(() => now.set(Date.now()), 1000);
*
* on.cleanup(() => clearInterval(timer));
*
* return () => now.current;
* });
*
* const Stopwatch = resource(({ use }) => {
* let time = use(Now);
*
* return () => format(time);
* });
* ```
*/
use: <Value>(resource: Value) => Reactive<Value extends Reactive<any> ? Value['current'] : Value>;
/**
* The Application owner.
* This allows for direct access to traditional ember services.
*
* Example:
* ```js
* resource(({ owner }) => {
* owner.lookup('service:router').currentRouteName
* //...
* }
* ```
*/
owner: Owner;
};
/**
* Type of the callback passed to `resource`
*/
export type ResourceFunction<Value = unknown> = (hooks: ResourceAPI) => Value | (() => Value);
/**
* The perceived return value of `resource`
* This is a lie to TypeScript, because the effective value of
* of the resource is the result of the collapsed functions
* passed to `resource`
*/
export type ResourceFn<Value = unknown> = (hooks: ResourceAPI) => Value;
export type Destructor = () => void;
export type Cache = object;