@graffy/core 0.18.1-alpha.2 → 0.19.1-alpha.1

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,5 +1,5 @@
1
1
  export default class Core {
2
- handlers: {};
2
+ constructor();
3
3
  on(type: any, path: any, handle: any): void;
4
4
  call(type: any, payload: any, options?: {}): any;
5
5
  }
package/Core.js ADDED
@@ -0,0 +1,40 @@
1
+ import { encodePath, unwrap } from '@graffy/common';
2
+ import debug from 'debug';
3
+ const log = debug('graffy:core');
4
+ function resolve(handlers, firstPayload, firstOptions) {
5
+ if (!handlers?.length)
6
+ throw Error('resolve.no_provider');
7
+ function run(i, payload, options) {
8
+ if (i >= handlers.length) {
9
+ throw Error(`resolve.no_providers_for ${JSON.stringify(payload)}`);
10
+ }
11
+ const { path, handle } = handlers[i];
12
+ if (!unwrap(payload, path)) {
13
+ return run(i + 1, payload, options);
14
+ }
15
+ let nextCalled = false;
16
+ return handle(payload, options, (nextPayload, nextOptions) => {
17
+ if (nextCalled) {
18
+ throw Error(`resolve.duplicate_next_call: ${handlers[i].name}`);
19
+ }
20
+ nextCalled = true;
21
+ if (typeof nextPayload === 'undefined' || !nextPayload.length)
22
+ return;
23
+ return run(i + 1, nextPayload, nextOptions || options);
24
+ });
25
+ }
26
+ return run(0, firstPayload, firstOptions);
27
+ }
28
+ export default class Core {
29
+ constructor() {
30
+ this.handlers = {};
31
+ }
32
+ on(type, path, handle) {
33
+ this.handlers[type] = this.handlers[type] || [];
34
+ this.handlers[type].push({ path: encodePath(path), handle });
35
+ }
36
+ call(type, payload, options = {}) {
37
+ log('call', type, payload);
38
+ return resolve(this.handlers[type], payload, options);
39
+ }
40
+ }
@@ -1,8 +1,7 @@
1
- export { unchanged } from "./shift.js";
2
- declare class Graffy {
1
+ import Core from './Core.js';
2
+ export { unchanged } from './shift.js';
3
+ export default class Graffy {
3
4
  constructor(path?: any[], core?: Core);
4
- core: Core;
5
- path: any[];
6
5
  on(type: any, ...args: any[]): void;
7
6
  onRead(...args: any[]): void;
8
7
  onWatch(...args: any[]): void;
@@ -13,21 +12,9 @@ declare class Graffy {
13
12
  watch(...args: any[]): {
14
13
  debugId: any;
15
14
  next: () => any;
16
- return(value: any): Promise<{
17
- value: any;
18
- done: boolean;
19
- }>;
20
- throw(error: any): Promise<{
21
- value: any;
22
- done: boolean;
23
- }>;
24
- [Symbol.asyncIterator](): /*elided*/ any;
15
+ return(value: any): any;
16
+ throw(error: any): any;
17
+ [Symbol.asyncIterator](): any;
25
18
  };
26
19
  write(...args: any[]): Promise<any>;
27
20
  }
28
- declare namespace Graffy {
29
- export { unchanged };
30
- }
31
- export default Graffy;
32
- import Core from './Core.js';
33
- import { unchanged } from './shift.js';
package/Graffy.js ADDED
@@ -0,0 +1,81 @@
1
+ import { decodeGraph, decodeQuery, decorate, encodeGraph, encodeQuery, finalize, unwrapObject, wrapObject, } from '@graffy/common';
2
+ import { makeStream, mapStream } from '@graffy/stream';
3
+ import Core from "./Core.js";
4
+ import { shiftGen, unchanged, wrapProvider } from "./shift.js";
5
+ import { validateCall, validateOn } from "./validate.js";
6
+ export { unchanged } from "./shift.js";
7
+ export default class Graffy {
8
+ constructor(path = [], core = new Core()) {
9
+ this.core = core;
10
+ this.path = path;
11
+ }
12
+ on(type, ...args) {
13
+ const [pathArg, handler] = validateOn(...args);
14
+ const path = this.path.concat(pathArg);
15
+ this.core.on(type, path, handler);
16
+ }
17
+ onRead(...args) {
18
+ const [pathArg, handle] = validateOn(...args);
19
+ const path = this.path.concat(pathArg);
20
+ this.core.on('read', path, wrapProvider(handle, path, true));
21
+ }
22
+ onWatch(...args) {
23
+ const [pathArg, handle] = validateOn(...args);
24
+ const path = this.path.concat(pathArg);
25
+ this.core.on('watch', path, shiftGen(function porcelainWatch(query, options) {
26
+ return makeStream((push, end) => {
27
+ const subscription = handle(decodeQuery(query), options, () => {
28
+ // TODO: Implement this using mergeStreams
29
+ throw Error(`porcelain.watch_next_unsupported: ${path}`);
30
+ });
31
+ (async () => {
32
+ try {
33
+ const firstValue = (await subscription.next()).value;
34
+ push(firstValue && finalize(encodeGraph(firstValue), query));
35
+ for await (const value of subscription) {
36
+ push(value && encodeGraph(value));
37
+ }
38
+ }
39
+ catch (e) {
40
+ end(e);
41
+ }
42
+ })();
43
+ return () => subscription.return();
44
+ });
45
+ }, path));
46
+ }
47
+ onWrite(...args) {
48
+ const [pathArg, handle] = validateOn(...args);
49
+ const path = this.path.concat(pathArg);
50
+ this.core.on('write', path, wrapProvider(handle, path, false));
51
+ }
52
+ use(...args) {
53
+ const [pathArg, provider] = validateOn(...args);
54
+ const path = this.path.concat(pathArg);
55
+ provider(new Graffy(path, this.core));
56
+ }
57
+ call(type, payload, options = {}) {
58
+ return this.core.call(type, payload, options);
59
+ }
60
+ async read(...args) {
61
+ const [path, porcelainQuery, options] = validateCall(...args);
62
+ const rootQuery = wrapObject(porcelainQuery, path);
63
+ const query = encodeQuery(rootQuery);
64
+ const result = await this.core.call('read', query, options || {});
65
+ return unwrapObject(decorate(result, rootQuery), path);
66
+ }
67
+ watch(...args) {
68
+ const [path, porcelainQuery, options] = validateCall(...args);
69
+ const rootQuery = wrapObject(porcelainQuery, path);
70
+ const query = encodeQuery(rootQuery);
71
+ const stream = this.core.call('watch', query, options || {});
72
+ return mapStream(stream, (value) => unwrapObject(decorate(value, rootQuery), path));
73
+ }
74
+ async write(...args) {
75
+ const [path, porcelainChange, options] = validateCall(...args);
76
+ const change = encodeGraph(wrapObject(porcelainChange, path));
77
+ const writtenChange = await this.core.call('write', change, options || {});
78
+ return unwrapObject(decodeGraph(writtenChange), path);
79
+ }
80
+ }
81
+ Graffy.unchanged = unchanged;
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import Graffy from "./Graffy.js";
2
+ export default Graffy;
package/package.json CHANGED
@@ -2,22 +2,27 @@
2
2
  "name": "@graffy/core",
3
3
  "description": "The main module for Graffy, a library for intuitive real-time data APIs.",
4
4
  "author": "aravind (https://github.com/aravindet)",
5
- "version": "0.18.1-alpha.2",
6
- "main": "./index.cjs",
5
+ "version": "0.19.1-alpha.1",
6
+ "main": "./cjs/index.js",
7
7
  "exports": {
8
- "import": "./index.mjs",
9
- "require": "./index.cjs"
8
+ ".": {
9
+ "import": "./index.js",
10
+ "types": "./index.d.ts"
11
+ },
12
+ "./*": {
13
+ "import": "./*.js",
14
+ "types": "./*.d.ts"
15
+ }
10
16
  },
11
- "module": "./index.mjs",
12
- "types": "./types/index.d.ts",
17
+ "types": "./index.d.ts",
13
18
  "repository": {
14
19
  "type": "git",
15
20
  "url": "git+https://github.com/usegraffy/graffy.git"
16
21
  },
17
22
  "license": "Apache-2.0",
18
23
  "dependencies": {
19
- "@graffy/common": "0.18.1-alpha.2",
20
- "@graffy/stream": "0.18.1-alpha.2",
21
- "debug": "^4.4.3"
24
+ "@graffy/common": "0.19.1-alpha.1",
25
+ "debug": "^4.4.3",
26
+ "@graffy/stream": "0.19.1-alpha.1"
22
27
  }
23
28
  }
package/shift.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const unchanged: unique symbol;
2
+ export declare function wrapProvider(fn: any, decodedPath: any, isRead: any): (payload: any, options: any, next: any) => Promise<any>;
3
+ export declare function shiftGen(fn: any, path: any): (payload: any, options: any, next: any) => AsyncGenerator<any, void, any>;
package/shift.js ADDED
@@ -0,0 +1,121 @@
1
+ import { encodeGraph, encodePath, encodeQuery, finalize, merge, mergeStreams, decodeGraph as origDecodeGraph, decodeQuery as origDecodeQuery, remove, unwrap, unwrapObject, wrap, wrapObject, } from '@graffy/common';
2
+ import { makeStream } from '@graffy/stream';
3
+ async function mapStream(stream, fn) {
4
+ for await (const value of stream) {
5
+ fn(value);
6
+ }
7
+ }
8
+ export const unchanged = Symbol('Payload or result unchanged by handler');
9
+ const decodeCache = new WeakMap();
10
+ function memoizeDecode(origDecode) {
11
+ return (payload) => {
12
+ if (decodeCache.has(payload))
13
+ return decodeCache.get(payload);
14
+ const decoded = origDecode(payload);
15
+ if (payload.type === 'object' && payload)
16
+ decodeCache.set(payload, decoded);
17
+ return decoded;
18
+ };
19
+ }
20
+ const decodeGraph = memoizeDecode(origDecodeGraph);
21
+ const decodeQuery = memoizeDecode(origDecodeQuery);
22
+ export function wrapProvider(fn, decodedPath, isRead) {
23
+ const decodePayload = isRead ? decodeQuery : decodeGraph;
24
+ const encodePayload = isRead ? encodeQuery : encodeGraph;
25
+ const path = encodePath(decodedPath);
26
+ return async function wrappedProvider(payload, options, next) {
27
+ let nextCalled = false;
28
+ let nextResult;
29
+ let remainingNextResult;
30
+ const porcelainPayload = unwrapObject(decodePayload(payload), decodedPath);
31
+ const remainingPayload = remove(payload, path) || [];
32
+ // This next function is offered to the provider function.
33
+ async function shiftedNext(porcelainNextPayload, nextOptions) {
34
+ nextCalled = true;
35
+ let nextPayload;
36
+ if (porcelainNextPayload === unchanged) {
37
+ nextPayload = payload;
38
+ }
39
+ else {
40
+ nextPayload = encodePayload(wrapObject(porcelainNextPayload, decodedPath));
41
+ if (remainingPayload.length)
42
+ merge(nextPayload, remainingPayload);
43
+ }
44
+ nextResult = await next(nextPayload, nextOptions);
45
+ // Remember the next() results that are not returned to this provider.
46
+ // These will be merged into the result later.
47
+ remainingNextResult = remove(nextResult, path) || [];
48
+ return unwrapObject(decodeGraph(nextResult), decodedPath);
49
+ }
50
+ const porcelainResult = await fn(porcelainPayload, options, shiftedNext);
51
+ let result;
52
+ if (porcelainResult === unchanged) {
53
+ result = nextResult;
54
+ }
55
+ else {
56
+ result = encodeGraph(wrapObject(porcelainResult, decodedPath));
57
+ // TODO: Get rid of this special handling by requiring read providers to
58
+ // finalize results themselves.
59
+ if (isRead && !nextCalled) {
60
+ // This does the opposite of "remove"; "keep"?
61
+ const appliedQuery = wrap(unwrap(payload, path), path);
62
+ result = finalize(result, appliedQuery);
63
+ result = wrap(unwrap(result, path), path);
64
+ }
65
+ if (!nextCalled && remainingPayload.length) {
66
+ remainingNextResult = await next(remainingPayload);
67
+ }
68
+ if (remainingNextResult?.length) {
69
+ merge(result, remainingNextResult);
70
+ }
71
+ }
72
+ // console.log('Shifted', path, format(payload), format(result));
73
+ return result;
74
+ };
75
+ }
76
+ // TODO: Provider calling next in a subscription function is not tested.
77
+ export function shiftGen(fn, path) {
78
+ path = encodePath(path);
79
+ return async function* shiftedGen(payload, options, next) {
80
+ let nextCalled = false;
81
+ let remainingNextStream;
82
+ const unwrappedPayload = unwrap(payload, path);
83
+ const remainingPayload = remove(payload, path) || [];
84
+ // TODO: This should probably use makeStream and propagate returns.
85
+ const shiftedNext = async function* shiftedNextFn(unwrappedNextPayload, nextOptions) {
86
+ nextCalled = true;
87
+ const nextPayload = wrap(unwrappedNextPayload, path);
88
+ if (remainingPayload.length)
89
+ merge(nextPayload, remainingPayload);
90
+ /** @type {Function} */
91
+ let pushRemaining;
92
+ remainingNextStream = makeStream((push) => {
93
+ pushRemaining = push;
94
+ });
95
+ for await (const value of next(nextPayload, nextOptions)) {
96
+ const unwrappedValue = unwrap(value, path);
97
+ const remainingValue = remove(value, path);
98
+ if (remainingValue)
99
+ pushRemaining(remainingValue);
100
+ if (unwrappedValue)
101
+ yield unwrappedValue;
102
+ }
103
+ };
104
+ const unwrappedStream = fn(unwrappedPayload, options, shiftedNext);
105
+ // We expect next() to be called before the first value is yielded.
106
+ const firstValue = await (await unwrappedStream.next()).value;
107
+ const resultStream = makeStream((push) => {
108
+ push(wrap(firstValue, path));
109
+ mapStream(unwrappedStream, (value) => {
110
+ push(wrap(value, path));
111
+ });
112
+ return () => unwrappedStream.return();
113
+ });
114
+ if (!nextCalled && remainingPayload.length) {
115
+ remainingNextStream = next(remainingPayload);
116
+ }
117
+ yield* remainingNextStream
118
+ ? mergeStreams(resultStream, remainingNextStream)
119
+ : resultStream;
120
+ };
121
+ }
package/validate.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function validateCall(...args: any[]): any[];
2
+ export declare function validateOn(...args: any[]): any[];
package/validate.js ADDED
@@ -0,0 +1,46 @@
1
+ import { isPlainObject } from '@graffy/common';
2
+ const splitPath = (path) => Array.isArray(path) ? path : path === '' ? [] : String(path).split('.');
3
+ /*
4
+ any -> payload
5
+
6
+ object, object -> payload, options
7
+ string | array, any -> path, payload
8
+
9
+ string | array, any, object -> path, payload, options
10
+ */
11
+ export function validateCall(...args) {
12
+ if (args.length === 1) {
13
+ return [[], args[0], {}];
14
+ }
15
+ if (args.length === 2) {
16
+ if (isPlainObject(args[0])) {
17
+ if (!isPlainObject(args[1])) {
18
+ throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
19
+ }
20
+ return [[], args[0], args[1]];
21
+ }
22
+ return [splitPath(args[0]), args[1], {}];
23
+ }
24
+ if (args.length === 3) {
25
+ if (!isPlainObject(args[2])) {
26
+ throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
27
+ }
28
+ return [splitPath(args[0]), args[1], args[2]];
29
+ }
30
+ throw Error(`validateCall.invalid_args: ${JSON.stringify(args)}`);
31
+ }
32
+ export function validateOn(...args) {
33
+ if (args.length === 1) {
34
+ if (typeof args[0] !== 'function') {
35
+ throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[0])}`);
36
+ }
37
+ return [[], args[0]];
38
+ }
39
+ if (args.length === 2) {
40
+ if (typeof args[1] !== 'function') {
41
+ throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[1])}`);
42
+ }
43
+ return [splitPath(args[0]), args[1]];
44
+ }
45
+ throw Error(`validateOn.invalid_args: ${JSON.stringify(args)}`);
46
+ }
package/index.cjs DELETED
@@ -1,258 +0,0 @@
1
- "use strict";
2
- const common = require("@graffy/common");
3
- const stream = require("@graffy/stream");
4
- const debug = require("debug");
5
- const log = debug("graffy:core");
6
- function resolve(handlers, firstPayload, firstOptions) {
7
- if (!handlers?.length) throw Error("resolve.no_provider");
8
- function run(i, payload, options) {
9
- if (i >= handlers.length) {
10
- throw Error(`resolve.no_providers_for ${JSON.stringify(payload)}`);
11
- }
12
- const { path, handle } = handlers[i];
13
- if (!common.unwrap(payload, path)) {
14
- return run(i + 1, payload, options);
15
- }
16
- let nextCalled = false;
17
- return handle(payload, options, (nextPayload, nextOptions) => {
18
- if (nextCalled) {
19
- throw Error(`resolve.duplicate_next_call: ${handlers[i].name}`);
20
- }
21
- nextCalled = true;
22
- if (typeof nextPayload === "undefined" || !nextPayload.length) return;
23
- return run(i + 1, nextPayload, nextOptions || options);
24
- });
25
- }
26
- return run(0, firstPayload, firstOptions);
27
- }
28
- class Core {
29
- constructor() {
30
- this.handlers = {};
31
- }
32
- on(type, path, handle) {
33
- this.handlers[type] = this.handlers[type] || [];
34
- this.handlers[type].push({ path: common.encodePath(path), handle });
35
- }
36
- call(type, payload, options = {}) {
37
- log("call", type, payload);
38
- return resolve(this.handlers[type], payload, options);
39
- }
40
- }
41
- async function mapStream(stream2, fn) {
42
- for await (const value of stream2) {
43
- fn(value);
44
- }
45
- }
46
- const unchanged = /* @__PURE__ */ Symbol("Payload or result unchanged by handler");
47
- const decodeCache = /* @__PURE__ */ new WeakMap();
48
- function memoizeDecode(origDecode) {
49
- return (payload) => {
50
- if (decodeCache.has(payload)) return decodeCache.get(payload);
51
- const decoded = origDecode(payload);
52
- if (payload.type === "object" && payload) decodeCache.set(payload, decoded);
53
- return decoded;
54
- };
55
- }
56
- const decodeGraph = memoizeDecode(common.decodeGraph);
57
- const decodeQuery = memoizeDecode(common.decodeQuery);
58
- function wrapProvider(fn, decodedPath, isRead) {
59
- const decodePayload = isRead ? decodeQuery : decodeGraph;
60
- const encodePayload = isRead ? common.encodeQuery : common.encodeGraph;
61
- const path = common.encodePath(decodedPath);
62
- return async function wrappedProvider(payload, options, next) {
63
- let nextCalled = false;
64
- let nextResult;
65
- let remainingNextResult;
66
- const porcelainPayload = common.unwrapObject(decodePayload(payload), decodedPath);
67
- const remainingPayload = common.remove(payload, path) || [];
68
- async function shiftedNext(porcelainNextPayload, nextOptions) {
69
- nextCalled = true;
70
- let nextPayload;
71
- if (porcelainNextPayload === unchanged) {
72
- nextPayload = payload;
73
- } else {
74
- nextPayload = encodePayload(
75
- common.wrapObject(porcelainNextPayload, decodedPath)
76
- );
77
- if (remainingPayload.length) common.merge(nextPayload, remainingPayload);
78
- }
79
- nextResult = await next(nextPayload, nextOptions);
80
- remainingNextResult = common.remove(nextResult, path) || [];
81
- return common.unwrapObject(decodeGraph(nextResult), decodedPath);
82
- }
83
- const porcelainResult = await fn(porcelainPayload, options, shiftedNext);
84
- let result;
85
- if (porcelainResult === unchanged) {
86
- result = nextResult;
87
- } else {
88
- result = common.encodeGraph(common.wrapObject(porcelainResult, decodedPath));
89
- if (isRead && !nextCalled) {
90
- const appliedQuery = common.wrap(common.unwrap(payload, path), path);
91
- result = common.finalize(result, appliedQuery);
92
- result = common.wrap(common.unwrap(result, path), path);
93
- }
94
- if (!nextCalled && remainingPayload.length) {
95
- remainingNextResult = await next(remainingPayload);
96
- }
97
- if (remainingNextResult?.length) {
98
- common.merge(result, remainingNextResult);
99
- }
100
- }
101
- return result;
102
- };
103
- }
104
- function shiftGen(fn, path) {
105
- path = common.encodePath(path);
106
- return async function* shiftedGen(payload, options, next) {
107
- let nextCalled = false;
108
- let remainingNextStream;
109
- const unwrappedPayload = common.unwrap(payload, path);
110
- const remainingPayload = common.remove(payload, path) || [];
111
- const shiftedNext = async function* shiftedNextFn(unwrappedNextPayload, nextOptions) {
112
- nextCalled = true;
113
- const nextPayload = common.wrap(unwrappedNextPayload, path);
114
- if (remainingPayload.length) common.merge(nextPayload, remainingPayload);
115
- let pushRemaining;
116
- remainingNextStream = stream.makeStream((push) => {
117
- pushRemaining = push;
118
- });
119
- for await (const value of next(nextPayload, nextOptions)) {
120
- const unwrappedValue = common.unwrap(value, path);
121
- const remainingValue = common.remove(value, path);
122
- if (remainingValue) pushRemaining(remainingValue);
123
- if (unwrappedValue) yield unwrappedValue;
124
- }
125
- };
126
- const unwrappedStream = fn(unwrappedPayload, options, shiftedNext);
127
- const firstValue = await (await unwrappedStream.next()).value;
128
- const resultStream = stream.makeStream((push) => {
129
- push(common.wrap(firstValue, path));
130
- mapStream(unwrappedStream, (value) => {
131
- push(common.wrap(value, path));
132
- });
133
- return () => unwrappedStream.return();
134
- });
135
- if (!nextCalled && remainingPayload.length) {
136
- remainingNextStream = next(remainingPayload);
137
- }
138
- yield* remainingNextStream ? common.mergeStreams(resultStream, remainingNextStream) : resultStream;
139
- };
140
- }
141
- const splitPath = (path) => Array.isArray(path) ? path : path === "" ? [] : String(path).split(".");
142
- function validateCall(...args) {
143
- if (args.length === 1) {
144
- return [[], args[0], {}];
145
- }
146
- if (args.length === 2) {
147
- if (common.isPlainObject(args[0])) {
148
- if (!common.isPlainObject(args[1])) {
149
- throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
150
- }
151
- return [[], args[0], args[1]];
152
- }
153
- return [splitPath(args[0]), args[1], {}];
154
- }
155
- if (args.length === 3) {
156
- if (!common.isPlainObject(args[2])) {
157
- throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
158
- }
159
- return [splitPath(args[0]), args[1], args[2]];
160
- }
161
- throw Error(`validateCall.invalid_args: ${JSON.stringify(args)}`);
162
- }
163
- function validateOn(...args) {
164
- if (args.length === 1) {
165
- if (typeof args[0] !== "function") {
166
- throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[0])}`);
167
- }
168
- return [[], args[0]];
169
- }
170
- if (args.length === 2) {
171
- if (typeof args[1] !== "function") {
172
- throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[1])}`);
173
- }
174
- return [splitPath(args[0]), args[1]];
175
- }
176
- throw Error(`validateOn.invalid_args: ${JSON.stringify(args)}`);
177
- }
178
- class Graffy {
179
- constructor(path = [], core = new Core()) {
180
- this.core = core;
181
- this.path = path;
182
- }
183
- on(type, ...args) {
184
- const [pathArg, handler] = validateOn(...args);
185
- const path = this.path.concat(pathArg);
186
- this.core.on(type, path, handler);
187
- }
188
- onRead(...args) {
189
- const [pathArg, handle] = validateOn(...args);
190
- const path = this.path.concat(pathArg);
191
- this.core.on("read", path, wrapProvider(handle, path, true));
192
- }
193
- onWatch(...args) {
194
- const [pathArg, handle] = validateOn(...args);
195
- const path = this.path.concat(pathArg);
196
- this.core.on(
197
- "watch",
198
- path,
199
- shiftGen(function porcelainWatch(query, options) {
200
- return stream.makeStream((push, end) => {
201
- const subscription = handle(common.decodeQuery(query), options, () => {
202
- throw Error(`porcelain.watch_next_unsupported: ${path}`);
203
- });
204
- (async () => {
205
- try {
206
- const firstValue = (await subscription.next()).value;
207
- push(firstValue && common.finalize(common.encodeGraph(firstValue), query));
208
- for await (const value of subscription) {
209
- push(value && common.encodeGraph(value));
210
- }
211
- } catch (e) {
212
- end(e);
213
- }
214
- })();
215
- return () => subscription.return();
216
- });
217
- }, path)
218
- );
219
- }
220
- onWrite(...args) {
221
- const [pathArg, handle] = validateOn(...args);
222
- const path = this.path.concat(pathArg);
223
- this.core.on("write", path, wrapProvider(handle, path, false));
224
- }
225
- use(...args) {
226
- const [pathArg, provider] = validateOn(...args);
227
- const path = this.path.concat(pathArg);
228
- provider(new Graffy(path, this.core));
229
- }
230
- call(type, payload, options = {}) {
231
- return this.core.call(type, payload, options);
232
- }
233
- async read(...args) {
234
- const [path, porcelainQuery, options] = validateCall(...args);
235
- const rootQuery = common.wrapObject(porcelainQuery, path);
236
- const query = common.encodeQuery(rootQuery);
237
- const result = await this.core.call("read", query, options || {});
238
- return common.unwrapObject(common.decorate(result, rootQuery), path);
239
- }
240
- watch(...args) {
241
- const [path, porcelainQuery, options] = validateCall(...args);
242
- const rootQuery = common.wrapObject(porcelainQuery, path);
243
- const query = common.encodeQuery(rootQuery);
244
- const stream$1 = this.core.call("watch", query, options || {});
245
- return stream.mapStream(
246
- stream$1,
247
- (value) => common.unwrapObject(common.decorate(value, rootQuery), path)
248
- );
249
- }
250
- async write(...args) {
251
- const [path, porcelainChange, options] = validateCall(...args);
252
- const change = common.encodeGraph(common.wrapObject(porcelainChange, path));
253
- const writtenChange = await this.core.call("write", change, options || {});
254
- return common.unwrapObject(common.decodeGraph(writtenChange), path);
255
- }
256
- }
257
- Graffy.unchanged = unchanged;
258
- module.exports = Graffy;
package/index.mjs DELETED
@@ -1,259 +0,0 @@
1
- import { encodePath, unwrap, unwrapObject, remove, encodeGraph, wrapObject, wrap, finalize, merge, mergeStreams, decodeGraph as decodeGraph$1, encodeQuery, decodeQuery as decodeQuery$1, isPlainObject, decorate } from "@graffy/common";
2
- import { makeStream, mapStream as mapStream$1 } from "@graffy/stream";
3
- import debug from "debug";
4
- const log = debug("graffy:core");
5
- function resolve(handlers, firstPayload, firstOptions) {
6
- if (!handlers?.length) throw Error("resolve.no_provider");
7
- function run(i, payload, options) {
8
- if (i >= handlers.length) {
9
- throw Error(`resolve.no_providers_for ${JSON.stringify(payload)}`);
10
- }
11
- const { path, handle } = handlers[i];
12
- if (!unwrap(payload, path)) {
13
- return run(i + 1, payload, options);
14
- }
15
- let nextCalled = false;
16
- return handle(payload, options, (nextPayload, nextOptions) => {
17
- if (nextCalled) {
18
- throw Error(`resolve.duplicate_next_call: ${handlers[i].name}`);
19
- }
20
- nextCalled = true;
21
- if (typeof nextPayload === "undefined" || !nextPayload.length) return;
22
- return run(i + 1, nextPayload, nextOptions || options);
23
- });
24
- }
25
- return run(0, firstPayload, firstOptions);
26
- }
27
- class Core {
28
- constructor() {
29
- this.handlers = {};
30
- }
31
- on(type, path, handle) {
32
- this.handlers[type] = this.handlers[type] || [];
33
- this.handlers[type].push({ path: encodePath(path), handle });
34
- }
35
- call(type, payload, options = {}) {
36
- log("call", type, payload);
37
- return resolve(this.handlers[type], payload, options);
38
- }
39
- }
40
- async function mapStream(stream, fn) {
41
- for await (const value of stream) {
42
- fn(value);
43
- }
44
- }
45
- const unchanged = /* @__PURE__ */ Symbol("Payload or result unchanged by handler");
46
- const decodeCache = /* @__PURE__ */ new WeakMap();
47
- function memoizeDecode(origDecode) {
48
- return (payload) => {
49
- if (decodeCache.has(payload)) return decodeCache.get(payload);
50
- const decoded = origDecode(payload);
51
- if (payload.type === "object" && payload) decodeCache.set(payload, decoded);
52
- return decoded;
53
- };
54
- }
55
- const decodeGraph = memoizeDecode(decodeGraph$1);
56
- const decodeQuery = memoizeDecode(decodeQuery$1);
57
- function wrapProvider(fn, decodedPath, isRead) {
58
- const decodePayload = isRead ? decodeQuery : decodeGraph;
59
- const encodePayload = isRead ? encodeQuery : encodeGraph;
60
- const path = encodePath(decodedPath);
61
- return async function wrappedProvider(payload, options, next) {
62
- let nextCalled = false;
63
- let nextResult;
64
- let remainingNextResult;
65
- const porcelainPayload = unwrapObject(decodePayload(payload), decodedPath);
66
- const remainingPayload = remove(payload, path) || [];
67
- async function shiftedNext(porcelainNextPayload, nextOptions) {
68
- nextCalled = true;
69
- let nextPayload;
70
- if (porcelainNextPayload === unchanged) {
71
- nextPayload = payload;
72
- } else {
73
- nextPayload = encodePayload(
74
- wrapObject(porcelainNextPayload, decodedPath)
75
- );
76
- if (remainingPayload.length) merge(nextPayload, remainingPayload);
77
- }
78
- nextResult = await next(nextPayload, nextOptions);
79
- remainingNextResult = remove(nextResult, path) || [];
80
- return unwrapObject(decodeGraph(nextResult), decodedPath);
81
- }
82
- const porcelainResult = await fn(porcelainPayload, options, shiftedNext);
83
- let result;
84
- if (porcelainResult === unchanged) {
85
- result = nextResult;
86
- } else {
87
- result = encodeGraph(wrapObject(porcelainResult, decodedPath));
88
- if (isRead && !nextCalled) {
89
- const appliedQuery = wrap(unwrap(payload, path), path);
90
- result = finalize(result, appliedQuery);
91
- result = wrap(unwrap(result, path), path);
92
- }
93
- if (!nextCalled && remainingPayload.length) {
94
- remainingNextResult = await next(remainingPayload);
95
- }
96
- if (remainingNextResult?.length) {
97
- merge(result, remainingNextResult);
98
- }
99
- }
100
- return result;
101
- };
102
- }
103
- function shiftGen(fn, path) {
104
- path = encodePath(path);
105
- return async function* shiftedGen(payload, options, next) {
106
- let nextCalled = false;
107
- let remainingNextStream;
108
- const unwrappedPayload = unwrap(payload, path);
109
- const remainingPayload = remove(payload, path) || [];
110
- const shiftedNext = async function* shiftedNextFn(unwrappedNextPayload, nextOptions) {
111
- nextCalled = true;
112
- const nextPayload = wrap(unwrappedNextPayload, path);
113
- if (remainingPayload.length) merge(nextPayload, remainingPayload);
114
- let pushRemaining;
115
- remainingNextStream = makeStream((push) => {
116
- pushRemaining = push;
117
- });
118
- for await (const value of next(nextPayload, nextOptions)) {
119
- const unwrappedValue = unwrap(value, path);
120
- const remainingValue = remove(value, path);
121
- if (remainingValue) pushRemaining(remainingValue);
122
- if (unwrappedValue) yield unwrappedValue;
123
- }
124
- };
125
- const unwrappedStream = fn(unwrappedPayload, options, shiftedNext);
126
- const firstValue = await (await unwrappedStream.next()).value;
127
- const resultStream = makeStream((push) => {
128
- push(wrap(firstValue, path));
129
- mapStream(unwrappedStream, (value) => {
130
- push(wrap(value, path));
131
- });
132
- return () => unwrappedStream.return();
133
- });
134
- if (!nextCalled && remainingPayload.length) {
135
- remainingNextStream = next(remainingPayload);
136
- }
137
- yield* remainingNextStream ? mergeStreams(resultStream, remainingNextStream) : resultStream;
138
- };
139
- }
140
- const splitPath = (path) => Array.isArray(path) ? path : path === "" ? [] : String(path).split(".");
141
- function validateCall(...args) {
142
- if (args.length === 1) {
143
- return [[], args[0], {}];
144
- }
145
- if (args.length === 2) {
146
- if (isPlainObject(args[0])) {
147
- if (!isPlainObject(args[1])) {
148
- throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
149
- }
150
- return [[], args[0], args[1]];
151
- }
152
- return [splitPath(args[0]), args[1], {}];
153
- }
154
- if (args.length === 3) {
155
- if (!isPlainObject(args[2])) {
156
- throw Error(`validateCall.invalid_options: ${JSON.stringify(args[1])}`);
157
- }
158
- return [splitPath(args[0]), args[1], args[2]];
159
- }
160
- throw Error(`validateCall.invalid_args: ${JSON.stringify(args)}`);
161
- }
162
- function validateOn(...args) {
163
- if (args.length === 1) {
164
- if (typeof args[0] !== "function") {
165
- throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[0])}`);
166
- }
167
- return [[], args[0]];
168
- }
169
- if (args.length === 2) {
170
- if (typeof args[1] !== "function") {
171
- throw Error(`validateOn.invalid_handler: ${JSON.stringify(args[1])}`);
172
- }
173
- return [splitPath(args[0]), args[1]];
174
- }
175
- throw Error(`validateOn.invalid_args: ${JSON.stringify(args)}`);
176
- }
177
- class Graffy {
178
- constructor(path = [], core = new Core()) {
179
- this.core = core;
180
- this.path = path;
181
- }
182
- on(type, ...args) {
183
- const [pathArg, handler] = validateOn(...args);
184
- const path = this.path.concat(pathArg);
185
- this.core.on(type, path, handler);
186
- }
187
- onRead(...args) {
188
- const [pathArg, handle] = validateOn(...args);
189
- const path = this.path.concat(pathArg);
190
- this.core.on("read", path, wrapProvider(handle, path, true));
191
- }
192
- onWatch(...args) {
193
- const [pathArg, handle] = validateOn(...args);
194
- const path = this.path.concat(pathArg);
195
- this.core.on(
196
- "watch",
197
- path,
198
- shiftGen(function porcelainWatch(query, options) {
199
- return makeStream((push, end) => {
200
- const subscription = handle(decodeQuery$1(query), options, () => {
201
- throw Error(`porcelain.watch_next_unsupported: ${path}`);
202
- });
203
- (async () => {
204
- try {
205
- const firstValue = (await subscription.next()).value;
206
- push(firstValue && finalize(encodeGraph(firstValue), query));
207
- for await (const value of subscription) {
208
- push(value && encodeGraph(value));
209
- }
210
- } catch (e) {
211
- end(e);
212
- }
213
- })();
214
- return () => subscription.return();
215
- });
216
- }, path)
217
- );
218
- }
219
- onWrite(...args) {
220
- const [pathArg, handle] = validateOn(...args);
221
- const path = this.path.concat(pathArg);
222
- this.core.on("write", path, wrapProvider(handle, path, false));
223
- }
224
- use(...args) {
225
- const [pathArg, provider] = validateOn(...args);
226
- const path = this.path.concat(pathArg);
227
- provider(new Graffy(path, this.core));
228
- }
229
- call(type, payload, options = {}) {
230
- return this.core.call(type, payload, options);
231
- }
232
- async read(...args) {
233
- const [path, porcelainQuery, options] = validateCall(...args);
234
- const rootQuery = wrapObject(porcelainQuery, path);
235
- const query = encodeQuery(rootQuery);
236
- const result = await this.core.call("read", query, options || {});
237
- return unwrapObject(decorate(result, rootQuery), path);
238
- }
239
- watch(...args) {
240
- const [path, porcelainQuery, options] = validateCall(...args);
241
- const rootQuery = wrapObject(porcelainQuery, path);
242
- const query = encodeQuery(rootQuery);
243
- const stream = this.core.call("watch", query, options || {});
244
- return mapStream$1(
245
- stream,
246
- (value) => unwrapObject(decorate(value, rootQuery), path)
247
- );
248
- }
249
- async write(...args) {
250
- const [path, porcelainChange, options] = validateCall(...args);
251
- const change = encodeGraph(wrapObject(porcelainChange, path));
252
- const writtenChange = await this.core.call("write", change, options || {});
253
- return unwrapObject(decodeGraph$1(writtenChange), path);
254
- }
255
- }
256
- Graffy.unchanged = unchanged;
257
- export {
258
- Graffy as default
259
- };
package/types/shift.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export function wrapProvider(fn: any, decodedPath: any, isRead: any): (payload: any, options: any, next: any) => Promise<any>;
2
- export function shiftGen(fn: any, path: any): (payload: any, options: any, next: any) => AsyncGenerator<any, void, any>;
3
- export const unchanged: unique symbol;
@@ -1,2 +0,0 @@
1
- export function validateCall(...args: any[]): any[];
2
- export function validateOn(...args: any[]): any[];
@@ -1,2 +1,2 @@
1
- export default Graffy;
2
1
  import Graffy from './Graffy.js';
2
+ export default Graffy;