teamplay 0.1.14 → 0.1.16

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 CHANGED
@@ -63,8 +63,118 @@ If you need the subscriptions to reactively update data whenever it changes (the
63
63
 
64
64
  ## Usage
65
65
 
66
- TBD
67
- ...
66
+ ### Introduction to teamplay ORM
67
+
68
+ teamplay is a powerful and easy-to-use ORM (Object-Relational Mapping) that allows you to work with your data in a natural, dot-notation style. It's designed to make data management in your app seamless and intuitive.
69
+
70
+ #### The Big Idea: Deep Signals
71
+
72
+ At the heart of teamplay is the concept of "deep signals." Think of your entire data structure as a big tree. With teamplay, you can navigate this tree using simple dot notation, just like you would access properties in a JavaScript object.
73
+
74
+ For example, to access a user's name, you might write:
75
+
76
+ ```javascript
77
+ $.users[userId].name
78
+ ```
79
+
80
+ This creates a "signal" pointing to that specific piece of data. Signals are smart pointers that know how to get and set data, and they automatically update your app when the data changes.
81
+
82
+ #### Public and Private Collections
83
+
84
+ In teamplay, data is organized into collections. There are two types:
85
+
86
+ 1. **Public Collections**: These are shared across all users of your app. They typically start with a lowercase letter (e.g., `users`, `posts`).
87
+
88
+ 2. **Private Collections**: These are specific to each user or session. They start with an underscore or dollar sign (e.g., `_session`, `$page`).
89
+
90
+ ### Basic Operations on Signals
91
+
92
+ Every signal in teamplay comes with a set of useful methods:
93
+
94
+ - `.get()`: Retrieves the current value of the signal.
95
+ - `.set(value)`: Updates the value of the signal.
96
+ - `.del()`: Deletes the value (or removes an item from an array).
97
+
98
+ Example:
99
+
100
+ ```javascript
101
+ // Get a user's name
102
+ const name = $.users[userId].name.get()
103
+
104
+ // Update a user's name
105
+ $.users[userId].name.set('Alice')
106
+
107
+ // Delete a user's profile picture
108
+ $.users[userId].profilePicture.del()
109
+ ```
110
+
111
+ ### The `$()` Function: Creating Local Signals
112
+
113
+ The `$()` function is a powerful tool for creating local, reactive values:
114
+
115
+ 1. Creating a simple value:
116
+
117
+ ```javascript
118
+ const $count = $(0)
119
+ console.log($count.get()) // Outputs: 0
120
+ $count.set(5)
121
+ console.log($count.get()) // Outputs: 5
122
+ ```
123
+
124
+ 2. Creating a computed value (similar to a calculated spreadsheet cell):
125
+
126
+ ```javascript
127
+ const $firstName = $('John')
128
+ const $lastName = $('Doe')
129
+ const $fullName = $(() => $firstName.get() + ' ' + $lastName.get())
130
+
131
+ console.log($fullName.get()) // Outputs: "John Doe"
132
+ $firstName.set('Jane')
133
+ console.log($fullName.get()) // Outputs: "Jane Doe"
134
+ ```
135
+
136
+ ### The `sub()` Function: Subscribing to Data
137
+
138
+ The `sub()` function is used to subscribe to data from the server:
139
+
140
+ 1. Subscribing to a single document:
141
+
142
+ ```javascript
143
+ const $user = await sub($.users[userId])
144
+ console.log($user.name.get())
145
+ ```
146
+
147
+ 2. Subscribing to a query (multiple documents):
148
+
149
+ ```javascript
150
+ const $activeUsers = await sub($.users, { status: 'active' })
151
+ ```
152
+
153
+ #### Working with Query Signals
154
+
155
+ Query signals are special. They behave like a collection signal, but they're also iterable:
156
+
157
+ ```javascript
158
+ // Iterate over active users
159
+ for (const $user of $activeUsers) {
160
+ console.log($user.name.get())
161
+ }
162
+
163
+ // Or use array methods
164
+ const names = $activeUsers.map($user => $user.name.get())
165
+ ```
166
+
167
+ Each `$user` in the loop is a scoped signal for that specific user document.
168
+
169
+ ### Reactivity: Keeping Your App in Sync
170
+
171
+ teamplay's reactivity system ensures that whenever data changes, any part of your app using that data updates automatically. This happens behind the scenes, so you don't have to manually track and update data dependencies.
172
+
173
+ For example, if you're displaying a user's name in your app and that name changes in the database, teamplay will automatically update your app's UI to reflect the new name.
174
+
175
+ This reactivity works for both public and private collections, local signals created with `$()`, and subscribed data from `sub()`.
176
+
177
+ By using these tools and concepts, you can build powerful, real-time applications with ease using teamplay!
68
178
 
69
179
  ## Examples
70
180
 
package/connect/index.js CHANGED
@@ -2,11 +2,8 @@ import Socket from '@teamplay/channel'
2
2
  import Connection from './sharedbConnection.cjs'
3
3
  import { connection, setConnection } from '../orm/connection.js'
4
4
 
5
- export default function connect ({
6
- baseUrl,
7
- ...options
8
- } = {}) {
5
+ export default function connect (options) {
9
6
  if (connection) return
10
- const socket = new Socket({ baseUrl, ...options })
7
+ const socket = new Socket(options)
11
8
  setConnection(new Connection(socket))
12
9
  }
package/index.js CHANGED
@@ -13,7 +13,8 @@ export { default as signal } from './orm/getSignal.js'
13
13
  export { GLOBAL_ROOT_ID } from './orm/Root.js'
14
14
  export const $ = _getRootSignal({ rootId: GLOBAL_ROOT_ID, rootFunction: universal$ })
15
15
  export default $
16
- export { default as sub } from './react/universalSub.js'
16
+ export { default as sub } from './orm/sub.js'
17
+ export { default as useSub } from './react/useSub.js'
17
18
  export { default as observer } from './react/observer.js'
18
19
  export { connection, setConnection, getConnection, fetchOnly, setFetchOnly, publicOnly, setPublicOnly } from './orm/connection.js'
19
20
  export { GUID_PATTERN, hasMany, hasOne, hasManyFlags, belongsTo, pickFormFields } from '@teamplay/schema'
@@ -25,19 +26,3 @@ export function getRootSignal (options) {
25
26
  ...options
26
27
  })
27
28
  }
28
-
29
- // the following are react-specific hook alternatives to $() and sub() functions.
30
- // In future we might want to expose them, but at the current time they are not needed
31
- // and instead just the regular $() and sub() functions are used since they are universal
32
- //
33
- // export function use$ (value) {
34
- // // TODO: maybe replace all non-letter/digit characters with underscores
35
- // const id = useId() // eslint-disable-line react-hooks/rules-of-hooks
36
- // return $(value, id)
37
- // }
38
-
39
- // export function useSub (...args) {
40
- // const promiseOrSignal = sub(...args)
41
- // if (promiseOrSignal.then) throw promiseOrSignal
42
- // return promiseOrSignal
43
- // }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamplay",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Full-stack signals ORM with multiplayer",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -23,12 +23,12 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@nx-js/observer-util": "^4.1.3",
26
- "@teamplay/backend": "^0.1.14",
27
- "@teamplay/cache": "^0.1.14",
28
- "@teamplay/channel": "^0.1.14",
29
- "@teamplay/debug": "^0.1.14",
30
- "@teamplay/schema": "^0.1.14",
31
- "@teamplay/utils": "^0.1.14",
26
+ "@teamplay/backend": "^0.1.16",
27
+ "@teamplay/cache": "^0.1.16",
28
+ "@teamplay/channel": "^0.1.16",
29
+ "@teamplay/debug": "^0.1.16",
30
+ "@teamplay/schema": "^0.1.16",
31
+ "@teamplay/utils": "^0.1.16",
32
32
  "diff-match-patch": "^1.0.5",
33
33
  "events": "^3.3.0",
34
34
  "json0-ot-diff": "^1.1.2",
@@ -63,5 +63,5 @@
63
63
  ]
64
64
  },
65
65
  "license": "MIT",
66
- "gitHead": "edc77f39753ed2532266bbfea646cb3cb7ca6786"
66
+ "gitHead": "f6a404f330ec3b88d498fa08fbcdc65cb80e8bb4"
67
67
  }
@@ -1,3 +1,7 @@
1
+ // NOTE: this is not used currently since using an explicit useSub()
2
+ // hook is easier to understand in a React context.
3
+ // Having the same sub() function working with either await or without it
4
+ // is confusing. It's better to have a separate function for the hook.
1
5
  import { useRef } from 'react'
2
6
  import sub from '../orm/sub.js'
3
7
  import executionContextTracker from './executionContextTracker.js'
@@ -0,0 +1,13 @@
1
+ import { useRef } from 'react'
2
+ import sub from '../orm/sub.js'
3
+
4
+ // version of sub() which works as a react hook and throws promise for Suspense
5
+ export default function useSub (...args) {
6
+ const promiseOrSignal = sub(...args)
7
+ // 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish
8
+ if (promiseOrSignal.then) throw promiseOrSignal
9
+ // 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
10
+ const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
11
+ if ($signalRef.current !== promiseOrSignal) $signalRef.current = promiseOrSignal
12
+ return promiseOrSignal
13
+ }