teamplay 0.5.0-alpha.11 → 0.5.0-alpha.13

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/dist/index.d.ts CHANGED
@@ -57,7 +57,7 @@ export { default as sub } from './orm/sub.js';
57
57
  export { default as useSub, useAsyncSub, setUseDeferredValue as __setUseDeferredValue, setDefaultDefer as __setDefaultDefer } from './react/useSub.js';
58
58
  export { default as useSuspendMemo, useSuspendMemoByKey } from './react/useSuspendMemo.js';
59
59
  export declare const observer: ObserverFunction;
60
- export { useValue, useValue$, useModel, useLocal, useLocal$, useLocalDoc, useLocalDoc$, useSession, useSession$, usePage, usePage$, useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
60
+ export { useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
61
61
  export { emit, useOn, useEmit } from './orm/Compat/eventsCompat.js';
62
62
  export { useDidUpdate, useOnce, useSyncEffect } from './react/helpers.js';
63
63
  export { connection, setConnection, getConnection, getDefaultFetchOnly, setDefaultFetchOnly, publicOnly, setPublicOnly } from './orm/connection.js';
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ export { default as sub } from "./orm/sub.js";
18
18
  export { default as useSub, useAsyncSub, setUseDeferredValue as __setUseDeferredValue, setDefaultDefer as __setDefaultDefer } from "./react/useSub.js";
19
19
  export { default as useSuspendMemo, useSuspendMemoByKey } from "./react/useSuspendMemo.js";
20
20
  export const observer = runtimeObserver;
21
- export { useValue, useValue$, useModel, useLocal, useLocal$, useLocalDoc, useLocalDoc$, useSession, useSession$, usePage, usePage$, useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
21
+ export { useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
22
22
  export { emit, useOn, useEmit } from './orm/Compat/eventsCompat.js';
23
23
  export { useDidUpdate, useOnce, useSyncEffect } from "./react/helpers.js";
24
24
  export { connection, setConnection, getConnection, getDefaultFetchOnly, setDefaultFetchOnly, publicOnly, setPublicOnly } from "./orm/connection.js";
@@ -3,15 +3,17 @@ import type {
3
3
  QuerySubscriptions,
4
4
  QuerySignalOptions
5
5
  } from './Query.js'
6
+ import type { PathSegment } from './types/path.js'
6
7
 
7
8
  export const IS_AGGREGATION: unique symbol
8
9
  export const AGGREGATIONS: '$aggregations'
9
10
  export const aggregationSubscriptions: QuerySubscriptions
10
11
  export function getAggregationSignal (collectionName: string, params: unknown, options?: QuerySignalOptions): Signal
11
12
  export function isAggregationSignal ($signal: unknown): boolean | undefined
13
+ export function getAggregationRowId (row: unknown, collectionName?: string): string | undefined
12
14
  export function getAggregationDocId (
13
- segments: readonly unknown[],
15
+ segments: readonly PathSegment[],
14
16
  rootId?: string,
15
- method?: (path: unknown[]) => unknown
17
+ method?: (path: PathSegment[]) => unknown
16
18
  ): string | undefined
17
- export function getAggregationCollectionName (segments: readonly unknown[]): string | undefined
19
+ export function getAggregationCollectionName (segments: readonly PathSegment[]): string | undefined
@@ -8,6 +8,7 @@ import { delPrivateData, getPrivateData, setPrivateData } from './privateData.js
8
8
  import { setSignalRuntimeDescriptor } from "./signalRuntimeDescriptor.js";
9
9
  export const IS_AGGREGATION = Symbol('is aggregation signal');
10
10
  export const AGGREGATIONS = '$aggregations';
11
+ const DEFAULT_AGGREGATION_ID_FIELDS = ['_id', 'id'];
11
12
  class Aggregation extends Query {
12
13
  _initData() {
13
14
  this._syncAllRootsData();
@@ -39,17 +40,27 @@ aggregationSubscriptions.runtimeKind = 'aggregation';
39
40
  function injectAggregationIds(extra, collectionName) {
40
41
  if (!Array.isArray(extra))
41
42
  return;
42
- const idFields = getIdFieldsForSegments([collectionName, '']);
43
+ const idFields = getCollectionIdFields(collectionName);
43
44
  for (const doc of extra) {
44
45
  if (!isPlainObject(doc))
45
46
  continue;
46
- const docId = doc._id ?? doc.id;
47
+ const docId = getAggregationRowId(doc, collectionName);
47
48
  if (docId == null)
48
49
  continue;
49
- if (idFields.includes('_id') && doc._id !== docId)
50
- doc._id = docId;
51
- if (idFields.includes('id') && doc.id !== docId)
52
- doc.id = docId;
50
+ for (const field of idFields) {
51
+ if (doc[field] !== docId)
52
+ doc[field] = docId;
53
+ }
54
+ }
55
+ }
56
+ export function getAggregationRowId(row, collectionName) {
57
+ if (!isPlainObject(row))
58
+ return;
59
+ const idFields = getAggregationIdFields(collectionName);
60
+ for (const field of idFields) {
61
+ const value = row[field];
62
+ if (typeof value === 'string')
63
+ return value;
53
64
  }
54
65
  }
55
66
  export function getAggregationSignal(collectionName, params, options) {
@@ -80,7 +91,7 @@ export function isAggregationSignal($signal) {
80
91
  return true;
81
92
  }
82
93
  // example: ['$aggregations', '{"active":true}', 42]
83
- // AND only if it also has either '_id' or 'id' field inside
94
+ // AND only if the aggregation row carries a source document id field
84
95
  export function getAggregationDocId(segments, rootId, method) {
85
96
  if (!(segments.length >= 3))
86
97
  return;
@@ -88,15 +99,16 @@ export function getAggregationDocId(segments, rootId, method) {
88
99
  return;
89
100
  if (!(typeof segments[2] === 'number'))
90
101
  return;
102
+ const collectionName = getAggregationCollectionName(segments);
103
+ const idFields = getAggregationIdFields(collectionName);
91
104
  if (typeof method !== 'function') {
92
105
  method = path => rootId == null ? getRaw(path) : getPrivateData(rootId, path);
93
106
  }
94
- const underscoreId = method([...segments.slice(0, 3), '_id']);
95
- if (typeof underscoreId === 'string')
96
- return underscoreId;
97
- const id = method([...segments.slice(0, 3), 'id']);
98
- if (typeof id === 'string')
99
- return id;
107
+ for (const field of idFields) {
108
+ const id = method([...segments.slice(0, 3), field]);
109
+ if (typeof id === 'string')
110
+ return id;
111
+ }
100
112
  }
101
113
  export function getAggregationCollectionName(segments) {
102
114
  if (!(segments.length >= 2))
@@ -117,3 +129,15 @@ function parseAggregationSignalOptions(options) {
117
129
  const { root, ...signalOptions } = options;
118
130
  return { root, signalOptions };
119
131
  }
132
+ function getAggregationIdFields(collectionName) {
133
+ const idFields = getCollectionIdFields(collectionName);
134
+ return uniq(idFields.concat(DEFAULT_AGGREGATION_ID_FIELDS));
135
+ }
136
+ function getCollectionIdFields(collectionName) {
137
+ return collectionName
138
+ ? getIdFieldsForSegments([collectionName, ''])
139
+ : [];
140
+ }
141
+ function uniq(values) {
142
+ return Array.from(new Set(values));
143
+ }
@@ -5,7 +5,7 @@ import { getRoot, ROOT, ROOT_ID, getRootSignal, GLOBAL_ROOT_ID, unregisterRootFi
5
5
  import { isPrivateMutationForbidden } from "../connection.js";
6
6
  import { docSubscriptions } from '../Doc.js';
7
7
  import { IS_QUERY, getQuerySignal, querySubscriptions } from '../Query.js';
8
- import { IS_AGGREGATION, aggregationSubscriptions, getAggregationSignal } from '../Aggregation.js';
8
+ import { AGGREGATIONS, IS_AGGREGATION, aggregationSubscriptions, getAggregationSignal } from '../Aggregation.js';
9
9
  import { getIdFieldsForSegments, isIdFieldPath, isPublicDocPath, normalizeIdFields, isPlainObject } from "../idFields.js";
10
10
  import { incrementPublic as _incrementPublic, arrayPushPublic as _arrayPushPublic, arrayUnshiftPublic as _arrayUnshiftPublic, arrayInsertPublic as _arrayInsertPublic, arrayPopPublic as _arrayPopPublic, arrayShiftPublic as _arrayShiftPublic, arrayRemovePublic as _arrayRemovePublic, arrayMovePublic as _arrayMovePublic, setPublicDocReplace as _setPublicDocReplace, stringInsertPublic as _stringInsertPublic, stringRemovePublic as _stringRemovePublic } from '../dataTree.js';
11
11
  import { on as onCustomEvent, removeListener as removeCustomEventListener } from './eventsCompat.js';
@@ -28,6 +28,8 @@ class SignalCompat extends Signal {
28
28
  return super.path();
29
29
  }
30
30
  getId() {
31
+ if (isAggregationValuePath(this[SEGMENTS]))
32
+ return super.getId();
31
33
  const $target = resolveRefSignal(this);
32
34
  if ($target !== this)
33
35
  return $target.getId();
@@ -612,6 +614,11 @@ function readRefValue($signal) {
612
614
  throw err;
613
615
  }
614
616
  }
617
+ function isAggregationValuePath(segments) {
618
+ return Array.isArray(segments) &&
619
+ segments.length >= 3 &&
620
+ segments[0] === AGGREGATIONS;
621
+ }
615
622
  function resolveRefSignal($signal) {
616
623
  const directTarget = resolveRefSignalSafe($signal);
617
624
  if (directTarget && directTarget !== $signal)
@@ -1,14 +1,3 @@
1
- export const useValue: any
2
- export const useValue$: any
3
- export const useModel: any
4
- export const useLocal: any
5
- export const useLocal$: any
6
- export const useLocalDoc: any
7
- export const useLocalDoc$: any
8
- export const useSession: any
9
- export const useSession$: any
10
- export const usePage: any
11
- export const usePage$: any
12
1
  export const useBatch: any
13
2
  export const useDoc: any
14
3
  export const useDoc$: any
@@ -6,71 +6,6 @@ import { isCompatEnv } from '../compatEnv.js';
6
6
  import { isQueryReady } from './queryReadiness.js';
7
7
  const $root = getRootSignal({ rootId: GLOBAL_ROOT_ID, rootFunction: universal$ });
8
8
  const emittedCompatWarnings = new Set();
9
- // Hook-compatible wrapper around $() for compatibility mode.
10
- export function useValue$(defaultValue) {
11
- return $root(defaultValue);
12
- }
13
- // Returns [value, $signal] similar to useState, but backed by $().
14
- export function useValue(defaultValue) {
15
- const $sig = useValue$(defaultValue);
16
- return [$sig.get(), $sig];
17
- }
18
- export function useModel(path) {
19
- if (arguments.length === 0 || path == null)
20
- return $root;
21
- if (path && typeof path.path === 'function')
22
- return path;
23
- if (typeof path !== 'string')
24
- throw Error('useModel() expects a string path or a signal');
25
- const segments = path.split('.').filter(Boolean);
26
- if (segments.length === 0)
27
- return $root;
28
- let $cursor = $root;
29
- for (const segment of segments) {
30
- $cursor = $cursor[segment];
31
- }
32
- return $cursor;
33
- }
34
- export function useLocal$(path) {
35
- const resolvedPath = resolveLocalPath(path);
36
- if (!resolvedPath)
37
- return $root;
38
- const segments = resolvedPath.split('.').filter(Boolean);
39
- let $cursor = $root;
40
- for (const segment of segments) {
41
- $cursor = $cursor[segment];
42
- }
43
- return $cursor;
44
- }
45
- export function useLocal(path) {
46
- const $sig = useLocal$(path);
47
- return [$sig.get(), $sig];
48
- }
49
- export function useLocalDoc$(collection, id) {
50
- if (collection == null)
51
- throw Error('useLocalDoc() expects a collection name');
52
- if (id == null)
53
- return undefined;
54
- return $root[collection][id];
55
- }
56
- export function useLocalDoc(collection, id) {
57
- const $doc = useLocalDoc$(collection, id);
58
- if (!$doc)
59
- return [undefined, undefined];
60
- return [$doc.get(), $doc];
61
- }
62
- export function useSession$(path) {
63
- return useLocal$(prefixLocalPath('_session', path));
64
- }
65
- export function useSession(path) {
66
- return useLocal(prefixLocalPath('_session', path));
67
- }
68
- export function usePage$(path) {
69
- return useLocal$(prefixLocalPath('_page', path));
70
- }
71
- export function usePage(path) {
72
- return useLocal(prefixLocalPath('_page', path));
73
- }
74
9
  export function useBatch() {
75
10
  const promise = promiseBatcher.getPromiseAll();
76
11
  if (promise)
@@ -256,29 +191,6 @@ export function useAsyncQueryDoc$(collection, query, options) {
256
191
  const [, $doc] = useAsyncQueryDoc(collection, query, options);
257
192
  return $doc;
258
193
  }
259
- function resolveLocalPath(path) {
260
- if (path && typeof path.path === 'function')
261
- return path.path();
262
- if (typeof path === 'string')
263
- return path;
264
- if (path == null)
265
- return '';
266
- throw Error('useLocal() expects a string path or a signal');
267
- }
268
- function prefixLocalPath(prefix, path) {
269
- if (path == null || path === '')
270
- return prefix;
271
- let resolved = path;
272
- if (path && typeof path.path === 'function')
273
- resolved = path.path();
274
- if (typeof resolved !== 'string')
275
- throw Error(`${prefix} hook expects a string path or a signal`);
276
- if (resolved.startsWith(prefix + '.'))
277
- return resolved;
278
- if (resolved === prefix)
279
- return resolved;
280
- return `${prefix}.${resolved}`;
281
- }
282
194
  function getDocSignal(collection, id, hookName) {
283
195
  if (typeof collection !== 'string') {
284
196
  throw Error(`[${hookName}] collection must be a string. Got: ${collection}`);
@@ -36,14 +36,14 @@ export function getSignalId($signal, rootId, readPath) {
36
36
  throw Error('Can\'t get the id of a collection');
37
37
  if (isDirectPublicDocumentSegments(segments))
38
38
  return getLeafId(segments);
39
+ if (segments[0] === AGGREGATIONS && segments.length === 3) {
40
+ return getAggregationDocId(segments, rootId, readPath);
41
+ }
39
42
  if (readPath) {
40
43
  const valueId = getValueIdFromPaths(segments, readPath);
41
44
  if (valueId.found)
42
45
  return valueId.id;
43
46
  }
44
- if (segments[0] === AGGREGATIONS && segments.length === 3) {
45
- return getAggregationDocId(segments, rootId);
46
- }
47
47
  return getLeafId(segments);
48
48
  }
49
49
  export function getSignalCollection($signal) {
@@ -1,4 +1,4 @@
1
- import { AGGREGATIONS, IS_AGGREGATION } from './Aggregation.js';
1
+ import { AGGREGATIONS, IS_AGGREGATION, getAggregationCollectionName, getAggregationRowId } from './Aggregation.js';
2
2
  import { HASH, IS_QUERY, QUERIES } from './Query.js';
3
3
  import { SEGMENTS } from "./signalSymbols.js";
4
4
  export function readSignalValue($signal, context, method, rawMethod) {
@@ -41,7 +41,8 @@ export function getSignalIds($signal, context) {
41
41
  const docs = context.readPrivateData(rootId, $signal[SEGMENTS], false);
42
42
  if (!Array.isArray(docs))
43
43
  return [];
44
- return docs.map(getAggregationRowId).filter(isString);
44
+ const collectionName = getAggregationCollectionName($signal[SEGMENTS]);
45
+ return docs.map(doc => getAggregationRowId(doc, collectionName)).filter(isString);
45
46
  }
46
47
  context.error('Signal.getIds() can only be used on query signals or aggregation signals. ' +
47
48
  'Received a regular signal: ' + JSON.stringify($signal[SEGMENTS]));
@@ -55,13 +56,6 @@ export function isAggregationValueSignal($signal) {
55
56
  const segments = $signal[SEGMENTS];
56
57
  return segments.length >= 2 && segments[0] === AGGREGATIONS;
57
58
  }
58
- function getAggregationRowId(doc) {
59
- const row = doc;
60
- if (typeof row?._id === 'string')
61
- return row._id;
62
- if (typeof row?.id === 'string')
63
- return row.id;
64
- }
65
59
  function isString(value) {
66
60
  return typeof value === 'string';
67
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamplay",
3
- "version": "0.5.0-alpha.11",
3
+ "version": "0.5.0-alpha.13",
4
4
  "description": "Full-stack signals ORM with multiplayer",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -69,13 +69,13 @@
69
69
  "dependencies": {
70
70
  "@nx-js/observer-util": "^4.1.3",
71
71
  "@startupjs/sharedb-mingo-memory": "^4.0.0-2",
72
- "@teamplay/backend": "^0.5.0-alpha.9",
72
+ "@teamplay/backend": "^0.5.0-alpha.13",
73
73
  "@teamplay/cache": "^0.5.0-alpha.7",
74
74
  "@teamplay/channel": "^0.5.0-alpha.10",
75
75
  "@teamplay/debug": "^0.5.0-alpha.7",
76
- "@teamplay/schema": "^0.5.0-alpha.7",
76
+ "@teamplay/schema": "^0.5.0-alpha.13",
77
77
  "@teamplay/utils": "^0.5.0-alpha.7",
78
- "babel-plugin-teamplay": "^0.5.0-alpha.7",
78
+ "babel-plugin-teamplay": "^0.5.0-alpha.13",
79
79
  "diff-match-patch": "^1.0.5",
80
80
  "events": "^3.3.0",
81
81
  "json0-ot-diff": "^1.1.2",
@@ -134,5 +134,5 @@
134
134
  ]
135
135
  },
136
136
  "license": "MIT",
137
- "gitHead": "2c23ad4108aad3dc7ac4947a987e395e933761cc"
137
+ "gitHead": "3b067b76508f175ddc12bff294f76682796185f8"
138
138
  }