@civet/core 1.4.2 → 2.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.
@@ -1,228 +0,0 @@
1
- import deepEquals from 'fast-deep-equal';
2
- import objectHash from 'object-hash';
3
- import PropTypes from 'prop-types';
4
- import AbortSignal from './AbortSignal';
5
- import ChannelNotifier from './ChannelNotifier';
6
- import Meta from './Meta';
7
-
8
- const getMeta = (meta) => (meta instanceof Meta ? meta : new Meta(meta));
9
-
10
- class DataProvider {
11
- notifier = new ChannelNotifier();
12
-
13
- constructor() {
14
- const contextPlugins = [];
15
- const uiPlugins = [];
16
- this.extend({
17
- context: (plugin) => {
18
- const plugins = contextPlugins;
19
- if (plugin != null && !plugins.includes(plugin)) plugins.push(plugin);
20
- },
21
- ui: (plugin) => {
22
- const plugins = uiPlugins;
23
- if (plugin != null && !plugins.includes(plugin)) plugins.push(plugin);
24
- },
25
- });
26
- Object.defineProperties(this, {
27
- contextPlugins: {
28
- value: Object.freeze(contextPlugins.slice()),
29
- enumerable: true,
30
- writable: false,
31
- configurable: false,
32
- },
33
- uiPlugins: {
34
- value: Object.freeze(uiPlugins.slice()),
35
- enumerable: true,
36
- writable: false,
37
- configurable: false,
38
- },
39
- });
40
- }
41
-
42
- extend() {}
43
-
44
- createInstance() {
45
- return null;
46
- }
47
-
48
- releaseInstance() {}
49
-
50
- subscribe(resource, handler) {
51
- if (resource == null) throw new Error('No resource name specified');
52
- return this.notifier.subscribe(resource, handler);
53
- }
54
-
55
- notify(resource) {
56
- this.notifier.trigger(resource);
57
- }
58
-
59
- get(resource, query, options, meta, abortSignal) {
60
- return new Promise((resolve, reject) =>
61
- this.continuousGet(
62
- resource,
63
- query,
64
- options,
65
- meta,
66
- (error, done, result) => {
67
- if (error != null) {
68
- reject(error);
69
- return;
70
- }
71
- if (done) resolve(result);
72
- },
73
- abortSignal,
74
- ),
75
- );
76
- }
77
-
78
- continuousGet(resource, query, options, meta, callback, abortSignal) {
79
- const signal = abortSignal == null ? new AbortSignal() : abortSignal;
80
-
81
- new Promise((resolve) => {
82
- if (resource == null) throw new Error('No resource name specified');
83
-
84
- // result transformation
85
- const cb = (error, done, result) => {
86
- // prevent updates after completion
87
- if (signal.locked) return;
88
- if (error != null || done) {
89
- signal.lock();
90
- }
91
- if (error != null) callback(error, true, []);
92
- else if (result == null) callback(undefined, done, []);
93
- else if (Array.isArray(result)) callback(undefined, done, result);
94
- else callback(undefined, done, [result]);
95
- };
96
-
97
- const proxy = signal.proxy();
98
-
99
- resolve(
100
- Promise.resolve(
101
- this.handleGet(resource, query, options, getMeta(meta), proxy),
102
- ).then((result) => {
103
- if (typeof result === 'function') {
104
- // `proxy` is being passed down here for backwards compatibility
105
- result(cb, proxy);
106
- } else {
107
- cb(undefined, true, result);
108
- }
109
- }),
110
- );
111
- }).catch((e) => {
112
- if (!signal.locked) callback(e, true, []);
113
- });
114
- }
115
-
116
- create(resource, data, options, meta) {
117
- return new Promise((resolve) => {
118
- if (resource == null) throw new Error('No resource name specified');
119
- if (data == null) throw new Error('No data specified');
120
- resolve(
121
- Promise.resolve(
122
- this.handleCreate(resource, data, options, getMeta(meta)),
123
- ),
124
- );
125
- });
126
- }
127
-
128
- update(resource, query, data, options, meta) {
129
- return new Promise((resolve) => {
130
- if (resource == null) throw new Error('No resource name specified');
131
- if (data == null) throw new Error('No data specified');
132
- resolve(
133
- Promise.resolve(
134
- this.handleUpdate(resource, query, data, options, getMeta(meta)),
135
- ),
136
- );
137
- });
138
- }
139
-
140
- patch(resource, query, data, options, meta) {
141
- return new Promise((resolve) => {
142
- if (resource == null) throw new Error('No resource name specified');
143
- if (data == null) throw new Error('No data specified');
144
- resolve(
145
- Promise.resolve(
146
- this.handlePatch(resource, query, data, options, getMeta(meta)),
147
- ),
148
- );
149
- });
150
- }
151
-
152
- remove(resource, query, options, meta) {
153
- return new Promise((resolve) => {
154
- if (resource == null) throw new Error('No resource name specified');
155
- resolve(
156
- Promise.resolve(
157
- this.handleRemove(resource, query, options, getMeta(meta)),
158
- ),
159
- );
160
- });
161
- }
162
-
163
- compareRequests(nextRequestDetails, prevRequestDetails) {
164
- return deepEquals(nextRequestDetails, prevRequestDetails);
165
- }
166
-
167
- shouldPersist(nextRequestDetails, prevRequestDetails, persistent) {
168
- return (
169
- persistent === 'very' ||
170
- (persistent && prevRequestDetails.name === nextRequestDetails.name)
171
- );
172
- }
173
-
174
- compareItemVersions() {
175
- return true;
176
- }
177
-
178
- getItemIdentifier(item) {
179
- return objectHash(item);
180
- }
181
-
182
- transition(nextContext) {
183
- return nextContext.data;
184
- }
185
-
186
- recycleItems(nextContext, prevContext) {
187
- const prevMapping = {};
188
- if (nextContext.data.length > 0) {
189
- prevContext.data.forEach((item) => {
190
- const id = this.getItemIdentifier(item);
191
- if (id != null) prevMapping[id] = item;
192
- });
193
- }
194
- let result;
195
- if (prevContext.data.length > 0) {
196
- result = nextContext.data.map((nextItem) => {
197
- const id = this.getItemIdentifier(nextItem);
198
- if (
199
- id != null &&
200
- Object.prototype.hasOwnProperty.call(prevMapping, id)
201
- ) {
202
- const prevItem = prevMapping[id];
203
- if (this.compareItemVersions(nextItem, prevItem)) return prevItem;
204
- }
205
- return nextItem;
206
- });
207
- } else {
208
- result = nextContext.data;
209
- }
210
- if (
211
- prevContext.data.length === result.length &&
212
- result.reduce(
213
- (sum, item, i) => sum && Object.is(prevContext.data[i], item),
214
- true,
215
- )
216
- ) {
217
- return prevContext.data;
218
- }
219
- return result;
220
- }
221
- }
222
-
223
- const isDataProvider = (dataProvider) => dataProvider instanceof DataProvider;
224
-
225
- const dataProviderPropType = PropTypes.instanceOf(DataProvider);
226
-
227
- export default DataProvider;
228
- export { dataProviderPropType, isDataProvider };
package/src/Meta.js DELETED
@@ -1,50 +0,0 @@
1
- class Meta {
2
- constructor(base, instance) {
3
- this.data = base == null ? {} : base;
4
- this.instance = instance;
5
- }
6
-
7
- clear = () => {
8
- Object.keys(this.data).forEach((key) => {
9
- delete this.data[key];
10
- });
11
- };
12
-
13
- delete = (key) => {
14
- const value = this.data[key];
15
- delete this.data[key];
16
- return value;
17
- };
18
-
19
- entries = () => Object.entries(this.data);
20
-
21
- get = (key) => this.data[key];
22
-
23
- has = (key) => Object.prototype.hasOwnProperty.call(this.data, key);
24
-
25
- keys = () => Object.keys(this.data);
26
-
27
- set = (key, value) => {
28
- this.data[key] = value;
29
- };
30
-
31
- values = () => Object.values(this.data);
32
-
33
- commit = (prev, ignore) => {
34
- const next = { ...this.data };
35
- (ignore || []).forEach((item) => {
36
- delete next[item];
37
- });
38
- const keys = Object.keys(next);
39
- if (
40
- prev != null &&
41
- Object.keys(prev).length === keys.length &&
42
- keys.reduce((sum, key) => sum && Object.is(next[key], prev[key]), true)
43
- ) {
44
- return prev;
45
- }
46
- return next;
47
- };
48
- }
49
-
50
- export default Meta;
package/src/Notifier.js DELETED
@@ -1,21 +0,0 @@
1
- class Notifier {
2
- listeners = new Set();
3
-
4
- subscribe = (handler) => {
5
- if (typeof handler !== 'function') {
6
- throw new Error('Handler must be a function');
7
- }
8
- this.listeners.add(handler);
9
- return () => {
10
- this.listeners.delete(handler);
11
- };
12
- };
13
-
14
- isSubscribed = (handler) => this.listeners.has(handler);
15
-
16
- trigger = (...args) => {
17
- this.listeners.forEach((handler) => handler(...args));
18
- };
19
- }
20
-
21
- export default Notifier;
package/src/Resource.jsx DELETED
@@ -1,68 +0,0 @@
1
- import PropTypes from 'prop-types';
2
- import { dataProviderPropType } from './DataProvider';
3
- import { ResourceContext } from './context';
4
- import useResource from './useResource';
5
-
6
- const propTypes = {
7
- /** DataProvider to be used for requests - must not be changed */
8
- dataProvider: dataProviderPropType,
9
- /** Resource name */
10
- name: PropTypes.string.isRequired,
11
- /** Query instructions */
12
- query: PropTypes.any,
13
- /** Disables fetching data, resulting in an empty data array */
14
- empty: PropTypes.bool,
15
- /** Query options for requests */
16
- options: PropTypes.object,
17
- /** Whether stale data should be retained during the next request - this only applies if name did not change, unless set to "very" */
18
- persistent: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['very'])]),
19
- children: PropTypes.node,
20
- };
21
-
22
- /**
23
- * Provides data based on the given request details and DataProvider.
24
- * Context provider for the ResourceContext.
25
- *
26
- * Necessary configuration that is not directly specified is taken from the ConfigContext.
27
- *
28
- * The provided DataProvider must not be changed.
29
- */
30
- function Resource({
31
- dataProvider,
32
- name,
33
- query,
34
- empty,
35
- options,
36
- persistent,
37
- children,
38
- ...rest
39
- }) {
40
- const context = useResource({
41
- dataProvider,
42
- name,
43
- query,
44
- empty,
45
- options,
46
- persistent,
47
- ...rest,
48
- });
49
-
50
- return context.dataProvider.uiPlugins.reduceRight(
51
- (next, Plugin) =>
52
- // eslint-disable-next-line react/display-name
53
- (result) => (
54
- <Plugin {...rest} context={result}>
55
- {next}
56
- </Plugin>
57
- ),
58
- (result) => (
59
- <ResourceContext.Provider value={result}>
60
- {children}
61
- </ResourceContext.Provider>
62
- ),
63
- )(context);
64
- }
65
-
66
- Resource.propTypes = propTypes;
67
-
68
- export default Resource;
package/src/compose.js DELETED
@@ -1,9 +0,0 @@
1
- function compose(...fn) {
2
- if (fn.length === 0) return (arg) => arg;
3
-
4
- if (fn.length === 1) return fn[0];
5
-
6
- return fn.reduce((a, b) => (arg) => a(b(arg)));
7
- }
8
-
9
- export default compose;
package/src/context.js DELETED
@@ -1,11 +0,0 @@
1
- import { createContext, useContext } from 'react';
2
-
3
- const noop = () => {};
4
-
5
- export const ConfigContext = createContext({});
6
- ConfigContext.displayName = 'ConfigContext';
7
- export const useConfigContext = () => useContext(ConfigContext);
8
-
9
- export const ResourceContext = createContext({ data: [], notify: noop });
10
- ResourceContext.displayName = 'ResourceContext';
11
- export const useResourceContext = () => useContext(ResourceContext);
@@ -1,23 +0,0 @@
1
- import DataProvider from './DataProvider';
2
-
3
- function createPlugin(plugin) {
4
- if (typeof plugin !== 'function') {
5
- throw new Error('No valid plugin definition specified');
6
- }
7
- return (dataProviderClass) => {
8
- if (!Object.prototype.isPrototypeOf.call(DataProvider, dataProviderClass)) {
9
- console.error(
10
- 'A plugin should be given a derivative of the DataProvider class as its first parameter',
11
- );
12
- }
13
- const result = plugin(class extends dataProviderClass {});
14
- if (result == null) {
15
- throw new Error(
16
- 'A plugin is expected to return a derivative of the DataProvider class but returned nothing',
17
- );
18
- }
19
- return result;
20
- };
21
- }
22
-
23
- export default createPlugin;
package/src/index.js DELETED
@@ -1,25 +0,0 @@
1
- import {
2
- ConfigContext,
3
- ResourceContext,
4
- useConfigContext,
5
- useResourceContext,
6
- } from './context';
7
-
8
- export const { Consumer: ConfigConsumer } = ConfigContext;
9
- export const { Provider: ResourceProvider, Consumer: ResourceConsumer } =
10
- ResourceContext;
11
- export { default as AbortSignal } from './AbortSignal';
12
- export { default as ChannelNotifier } from './ChannelNotifier';
13
- export { default as ConfigProvider } from './ConfigProvider';
14
- export {
15
- default as DataProvider,
16
- dataProviderPropType,
17
- isDataProvider,
18
- } from './DataProvider';
19
- export { default as Meta } from './Meta';
20
- export { default as Notifier } from './Notifier';
21
- export { default as Resource } from './Resource';
22
- export { default as compose } from './compose';
23
- export { default as createPlugin } from './createPlugin';
24
- export { default as useResource } from './useResource';
25
- export { useConfigContext, useResourceContext };
@@ -1,27 +0,0 @@
1
- import { v1 as uuid } from 'uuid';
2
-
3
- /**
4
- * Returns incrementing unique string identifiers.
5
- * Uniqueness is guaranteed for <Number.MAX_SAFE_INTEGER> iterations.
6
- * The values can be compared alphanumerically, as long as they do not exceed the previously specified number of iterations.
7
- *
8
- * @param {string?} previous Previous identifier
9
- */
10
- function uniqueIdentifier(previous) {
11
- let prefix;
12
- let value;
13
- let scope;
14
- if (typeof previous !== 'string') {
15
- prefix = '~';
16
- value = 0;
17
- scope = uuid();
18
- } else {
19
- let prevValue;
20
- [prefix, prevValue, scope] = previous.split('$');
21
- value = ((parseInt(prevValue, 36) || 0) + 1) % Number.MAX_SAFE_INTEGER;
22
- if (value === 0) prefix += '~';
23
- }
24
- return `${prefix}$${value.toString(36).padStart(11, '0')}$${scope}`;
25
- }
26
-
27
- export default uniqueIdentifier;