@dxos/functions 0.8.2-main.2f9c567 → 0.8.2-main.36232bc

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.
Files changed (168) hide show
  1. package/dist/lib/browser/bundler/index.mjs +3 -0
  2. package/dist/lib/browser/bundler/index.mjs.map +1 -1
  3. package/dist/lib/browser/chunk-2YE6S7XY.mjs +360 -0
  4. package/dist/lib/browser/chunk-2YE6S7XY.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-7CHDHCV3.mjs +482 -0
  6. package/dist/lib/browser/chunk-7CHDHCV3.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-LT4LR4VU.mjs +72 -0
  8. package/dist/lib/browser/chunk-LT4LR4VU.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-XRCXIG74.mjs +12 -0
  10. package/dist/lib/browser/chunk-XRCXIG74.mjs.map +7 -0
  11. package/dist/lib/browser/edge/index.mjs +7 -63
  12. package/dist/lib/browser/edge/index.mjs.map +4 -4
  13. package/dist/lib/browser/index.mjs +99 -367
  14. package/dist/lib/browser/index.mjs.map +4 -4
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/testing/index.mjs +670 -0
  17. package/dist/lib/browser/testing/index.mjs.map +7 -0
  18. package/dist/lib/browser/types/index.mjs +51 -0
  19. package/dist/lib/browser/types/index.mjs.map +7 -0
  20. package/dist/lib/node/bundler/index.cjs +1 -0
  21. package/dist/lib/node/bundler/index.cjs.map +1 -1
  22. package/dist/lib/node/chunk-FBIUZ7SD.cjs +496 -0
  23. package/dist/lib/node/chunk-FBIUZ7SD.cjs.map +7 -0
  24. package/dist/lib/node/chunk-JEQ2X3Z6.cjs +34 -0
  25. package/dist/lib/node/chunk-JEQ2X3Z6.cjs.map +7 -0
  26. package/dist/lib/node/chunk-NXZNXVT3.cjs +94 -0
  27. package/dist/lib/node/chunk-NXZNXVT3.cjs.map +7 -0
  28. package/dist/lib/node/chunk-SV5NRE5L.cjs +395 -0
  29. package/dist/lib/node/chunk-SV5NRE5L.cjs.map +7 -0
  30. package/dist/lib/node/edge/index.cjs +5 -65
  31. package/dist/lib/node/edge/index.cjs.map +4 -4
  32. package/dist/lib/node/index.cjs +94 -382
  33. package/dist/lib/node/index.cjs.map +4 -4
  34. package/dist/lib/node/meta.json +1 -1
  35. package/dist/lib/node/testing/index.cjs +687 -0
  36. package/dist/lib/node/testing/index.cjs.map +7 -0
  37. package/dist/lib/node/types/index.cjs +72 -0
  38. package/dist/lib/node/types/index.cjs.map +7 -0
  39. package/dist/lib/node-esm/bundler/index.mjs +1 -0
  40. package/dist/lib/node-esm/bundler/index.mjs.map +1 -1
  41. package/dist/lib/node-esm/chunk-3XMJFSID.mjs +360 -0
  42. package/dist/lib/node-esm/chunk-3XMJFSID.mjs.map +7 -0
  43. package/dist/lib/node-esm/chunk-C6YINTWG.mjs +482 -0
  44. package/dist/lib/node-esm/chunk-C6YINTWG.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-DHGBFXSZ.mjs +12 -0
  46. package/dist/lib/node-esm/chunk-DHGBFXSZ.mjs.map +7 -0
  47. package/dist/lib/node-esm/chunk-O2SXVYU5.mjs +72 -0
  48. package/dist/lib/node-esm/chunk-O2SXVYU5.mjs.map +7 -0
  49. package/dist/lib/node-esm/edge/index.mjs +6 -64
  50. package/dist/lib/node-esm/edge/index.mjs.map +4 -4
  51. package/dist/lib/node-esm/index.mjs +97 -367
  52. package/dist/lib/node-esm/index.mjs.map +4 -4
  53. package/dist/lib/node-esm/meta.json +1 -1
  54. package/dist/lib/node-esm/testing/index.mjs +670 -0
  55. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  56. package/dist/lib/node-esm/types/index.mjs +51 -0
  57. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  58. package/dist/types/src/browser/index.d.ts +2 -0
  59. package/dist/types/src/browser/index.d.ts.map +1 -0
  60. package/dist/types/src/edge/index.d.ts.map +1 -1
  61. package/dist/types/src/function/function-registry.d.ts +25 -0
  62. package/dist/types/src/function/function-registry.d.ts.map +1 -0
  63. package/dist/types/src/function/function-registry.test.d.ts +2 -0
  64. package/dist/types/src/function/function-registry.test.d.ts.map +1 -0
  65. package/dist/types/src/function/index.d.ts +2 -0
  66. package/dist/types/src/function/index.d.ts.map +1 -0
  67. package/dist/types/src/handler.d.ts +61 -12
  68. package/dist/types/src/handler.d.ts.map +1 -1
  69. package/dist/types/src/index.d.ts +3 -3
  70. package/dist/types/src/index.d.ts.map +1 -1
  71. package/dist/types/src/runtime/dev-server.d.ts +52 -0
  72. package/dist/types/src/runtime/dev-server.d.ts.map +1 -0
  73. package/dist/types/src/runtime/dev-server.test.d.ts +2 -0
  74. package/dist/types/src/runtime/dev-server.test.d.ts.map +1 -0
  75. package/dist/types/src/runtime/index.d.ts +3 -0
  76. package/dist/types/src/runtime/index.d.ts.map +1 -0
  77. package/dist/types/src/runtime/scheduler.d.ts +34 -0
  78. package/dist/types/src/runtime/scheduler.d.ts.map +1 -0
  79. package/dist/types/src/runtime/scheduler.test.d.ts +2 -0
  80. package/dist/types/src/runtime/scheduler.test.d.ts.map +1 -0
  81. package/dist/types/src/testing/functions-integration.test.d.ts +2 -0
  82. package/dist/types/src/testing/functions-integration.test.d.ts.map +1 -0
  83. package/dist/types/src/testing/index.d.ts +5 -0
  84. package/dist/types/src/testing/index.d.ts.map +1 -0
  85. package/dist/types/src/testing/manifest.d.ts +3 -0
  86. package/dist/types/src/testing/manifest.d.ts.map +1 -0
  87. package/dist/types/src/testing/plugin-init.d.ts +6 -0
  88. package/dist/types/src/testing/plugin-init.d.ts.map +1 -0
  89. package/dist/types/src/testing/setup.d.ts +15 -0
  90. package/dist/types/src/testing/setup.d.ts.map +1 -0
  91. package/dist/types/src/testing/test/handler.d.ts +4 -0
  92. package/dist/types/src/testing/test/handler.d.ts.map +1 -0
  93. package/dist/types/src/testing/test/index.d.ts +3 -0
  94. package/dist/types/src/testing/test/index.d.ts.map +1 -0
  95. package/dist/types/src/testing/types.d.ts +10 -0
  96. package/dist/types/src/testing/types.d.ts.map +1 -0
  97. package/dist/types/src/testing/util.d.ts +5 -0
  98. package/dist/types/src/testing/util.d.ts.map +1 -0
  99. package/dist/types/src/trigger/index.d.ts +3 -0
  100. package/dist/types/src/trigger/index.d.ts.map +1 -0
  101. package/dist/types/src/trigger/trigger-registry.d.ts +38 -0
  102. package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -0
  103. package/dist/types/src/trigger/trigger-registry.test.d.ts +2 -0
  104. package/dist/types/src/trigger/trigger-registry.test.d.ts.map +1 -0
  105. package/dist/types/src/trigger/type/index.d.ts +3 -0
  106. package/dist/types/src/trigger/type/index.d.ts.map +1 -0
  107. package/dist/types/src/trigger/type/subscription-trigger.d.ts +4 -0
  108. package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +1 -0
  109. package/dist/types/src/trigger/type/timer-trigger.d.ts +4 -0
  110. package/dist/types/src/trigger/type/timer-trigger.d.ts.map +1 -0
  111. package/dist/types/src/trigger/type/webhook-trigger.d.ts +4 -0
  112. package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +1 -0
  113. package/dist/types/src/types/index.d.ts +5 -0
  114. package/dist/types/src/types/index.d.ts.map +1 -0
  115. package/dist/types/src/types/schema.d.ts +53 -0
  116. package/dist/types/src/types/schema.d.ts.map +1 -0
  117. package/dist/types/src/types/trace.d.ts +146 -0
  118. package/dist/types/src/types/trace.d.ts.map +1 -0
  119. package/dist/types/src/{types.d.ts → types/types.d.ts} +49 -191
  120. package/dist/types/src/types/types.d.ts.map +1 -0
  121. package/dist/types/src/types/url.d.ts.map +1 -0
  122. package/dist/types/tools/schema.d.ts +2 -0
  123. package/dist/types/tools/schema.d.ts.map +1 -0
  124. package/package.json +36 -20
  125. package/schema/functions.json +211 -0
  126. package/src/browser/index.ts +5 -0
  127. package/src/edge/index.ts +0 -4
  128. package/src/function/function-registry.test.ts +118 -0
  129. package/src/function/function-registry.ts +104 -0
  130. package/src/function/index.ts +5 -0
  131. package/src/handler.ts +118 -14
  132. package/src/index.ts +5 -4
  133. package/src/runtime/dev-server.test.ts +79 -0
  134. package/src/runtime/dev-server.ts +240 -0
  135. package/src/runtime/index.ts +6 -0
  136. package/src/runtime/scheduler.test.ts +152 -0
  137. package/src/runtime/scheduler.ts +170 -0
  138. package/src/testing/functions-integration.test.ts +65 -0
  139. package/src/testing/index.ts +8 -0
  140. package/src/testing/manifest.ts +15 -0
  141. package/src/testing/plugin-init.ts +20 -0
  142. package/src/testing/setup.ts +109 -0
  143. package/src/testing/test/handler.ts +15 -0
  144. package/src/testing/test/index.ts +7 -0
  145. package/src/testing/types.ts +9 -0
  146. package/src/testing/util.ts +26 -0
  147. package/src/translations.ts +1 -1
  148. package/src/trigger/index.ts +6 -0
  149. package/src/trigger/trigger-registry.test.ts +278 -0
  150. package/src/trigger/trigger-registry.ts +218 -0
  151. package/src/trigger/type/index.ts +7 -0
  152. package/src/trigger/type/subscription-trigger.ts +84 -0
  153. package/src/trigger/type/timer-trigger.ts +48 -0
  154. package/src/trigger/type/webhook-trigger.ts +48 -0
  155. package/src/types/index.ts +8 -0
  156. package/src/types/schema.ts +46 -0
  157. package/src/{trace.ts → types/trace.ts} +31 -33
  158. package/src/types/types.ts +163 -0
  159. package/dist/types/src/schema.d.ts +0 -57
  160. package/dist/types/src/schema.d.ts.map +0 -1
  161. package/dist/types/src/trace.d.ts +0 -148
  162. package/dist/types/src/trace.d.ts.map +0 -1
  163. package/dist/types/src/types.d.ts.map +0 -1
  164. package/dist/types/src/url.d.ts.map +0 -1
  165. package/src/schema.ts +0 -53
  166. package/src/types.ts +0 -214
  167. /package/dist/types/src/{url.d.ts → types/url.d.ts} +0 -0
  168. /package/src/{url.ts → types/url.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/functions",
3
- "version": "0.8.2-main.2f9c567",
3
+ "version": "0.8.2-main.36232bc",
4
4
  "description": "Functions API and runtime.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -23,6 +23,16 @@
23
23
  "types": "./dist/types/src/edge/index.d.ts",
24
24
  "browser": "./dist/lib/browser/edge/index.mjs",
25
25
  "node": "./dist/lib/node-esm/edge/index.mjs"
26
+ },
27
+ "./testing": {
28
+ "types": "./dist/types/src/testing/index.d.ts",
29
+ "browser": "./dist/lib/browser/testing/index.mjs",
30
+ "node": "./dist/lib/node-esm/testing/index.mjs"
31
+ },
32
+ "./types": {
33
+ "types": "./dist/types/src/types/index.d.ts",
34
+ "browser": "./dist/lib/browser/types/index.mjs",
35
+ "node": "./dist/lib/node-esm/types/index.mjs"
26
36
  }
27
37
  },
28
38
  "types": "dist/types/src/index.d.ts",
@@ -33,6 +43,12 @@
33
43
  ],
34
44
  "edge": [
35
45
  "dist/types/src/edge/index.d.ts"
46
+ ],
47
+ "testing": [
48
+ "dist/types/src/testing/index.d.ts"
49
+ ],
50
+ "types": [
51
+ "dist/types/src/types/index.d.ts"
36
52
  ]
37
53
  }
38
54
  },
@@ -45,34 +61,34 @@
45
61
  "@preact/signals-core": "^1.6.0",
46
62
  "cron": "^3.1.6",
47
63
  "cron-schedule": "^5.0.4",
48
- "effect": "3.14.21",
64
+ "effect": "3.13.3",
49
65
  "esbuild-wasm": "^0.16.14",
50
66
  "express": "^4.19.2",
51
67
  "get-port-please": "^3.1.1",
52
68
  "iso-did": "^1.6.0",
53
69
  "ws": "^8.14.2",
54
- "@dxos/assistant": "0.8.2-main.2f9c567",
55
- "@dxos/async": "0.8.2-main.2f9c567",
56
- "@dxos/client": "0.8.2-main.2f9c567",
57
- "@dxos/context": "0.8.2-main.2f9c567",
58
- "@dxos/echo-db": "0.8.2-main.2f9c567",
59
- "@dxos/echo-schema": "0.8.2-main.2f9c567",
60
- "@dxos/echo-protocol": "0.8.2-main.2f9c567",
61
- "@dxos/edge-client": "0.8.2-main.2f9c567",
62
- "@dxos/crypto": "0.8.2-main.2f9c567",
63
- "@dxos/invariant": "0.8.2-main.2f9c567",
64
- "@dxos/keys": "0.8.2-main.2f9c567",
65
- "@dxos/log": "0.8.2-main.2f9c567",
66
- "@dxos/node-std": "0.8.2-main.2f9c567",
67
- "@dxos/live-object": "0.8.2-main.2f9c567",
68
- "@dxos/protocols": "0.8.2-main.2f9c567",
69
- "@dxos/schema": "0.8.2-main.2f9c567",
70
- "@dxos/util": "0.8.2-main.2f9c567"
70
+ "@dxos/assistant": "0.8.2-main.36232bc",
71
+ "@dxos/async": "0.8.2-main.36232bc",
72
+ "@dxos/client": "0.8.2-main.36232bc",
73
+ "@dxos/crypto": "0.8.2-main.36232bc",
74
+ "@dxos/echo-db": "0.8.2-main.36232bc",
75
+ "@dxos/echo-protocol": "0.8.2-main.36232bc",
76
+ "@dxos/echo-schema": "0.8.2-main.36232bc",
77
+ "@dxos/context": "0.8.2-main.36232bc",
78
+ "@dxos/keys": "0.8.2-main.36232bc",
79
+ "@dxos/invariant": "0.8.2-main.36232bc",
80
+ "@dxos/edge-client": "0.8.2-main.36232bc",
81
+ "@dxos/live-object": "0.8.2-main.36232bc",
82
+ "@dxos/node-std": "0.8.2-main.36232bc",
83
+ "@dxos/protocols": "0.8.2-main.36232bc",
84
+ "@dxos/log": "0.8.2-main.36232bc",
85
+ "@dxos/util": "0.8.2-main.36232bc",
86
+ "@dxos/schema": "0.8.2-main.36232bc"
71
87
  },
72
88
  "devDependencies": {
73
89
  "@types/express": "^4.17.17",
74
90
  "@types/ws": "^7.4.0",
75
- "@dxos/agent": "0.8.2-main.2f9c567"
91
+ "@dxos/agent": "0.8.2-main.36232bc"
76
92
  },
77
93
  "publishConfig": {
78
94
  "access": "public"
@@ -0,0 +1,211 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": [],
5
+ "properties": {
6
+ "functions": {
7
+ "type": "array",
8
+ "items": {
9
+ "type": "object",
10
+ "required": [
11
+ "uri",
12
+ "route",
13
+ "handler"
14
+ ],
15
+ "properties": {
16
+ "uri": {
17
+ "type": "string",
18
+ "description": "a string",
19
+ "title": "string"
20
+ },
21
+ "route": {
22
+ "type": "string",
23
+ "description": "a string",
24
+ "title": "string"
25
+ },
26
+ "handler": {
27
+ "type": "string",
28
+ "description": "a string",
29
+ "title": "string"
30
+ },
31
+ "description": {
32
+ "type": "string",
33
+ "description": "a string",
34
+ "title": "string"
35
+ }
36
+ },
37
+ "additionalProperties": false
38
+ }
39
+ },
40
+ "triggers": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "object",
44
+ "required": [
45
+ "function",
46
+ "spec"
47
+ ],
48
+ "properties": {
49
+ "function": {
50
+ "type": "string",
51
+ "description": "Function",
52
+ "title": "string"
53
+ },
54
+ "enabled": {
55
+ "type": "boolean",
56
+ "description": "a boolean",
57
+ "title": "boolean"
58
+ },
59
+ "meta": {
60
+ "$ref": "#/$defs/object"
61
+ },
62
+ "spec": {
63
+ "anyOf": [
64
+ {
65
+ "type": "object",
66
+ "required": [
67
+ "type",
68
+ "cron"
69
+ ],
70
+ "properties": {
71
+ "type": {
72
+ "const": "timer"
73
+ },
74
+ "cron": {
75
+ "type": "string",
76
+ "description": "a string",
77
+ "title": "string"
78
+ }
79
+ },
80
+ "additionalProperties": false
81
+ },
82
+ {
83
+ "type": "object",
84
+ "required": [
85
+ "type",
86
+ "method"
87
+ ],
88
+ "properties": {
89
+ "type": {
90
+ "const": "webhook"
91
+ },
92
+ "method": {
93
+ "type": "string",
94
+ "description": "a string",
95
+ "title": "string"
96
+ },
97
+ "port": {
98
+ "type": "number",
99
+ "description": "a number",
100
+ "title": "number"
101
+ }
102
+ },
103
+ "additionalProperties": false
104
+ },
105
+ {
106
+ "type": "object",
107
+ "required": [
108
+ "type",
109
+ "url"
110
+ ],
111
+ "properties": {
112
+ "type": {
113
+ "const": "websocket"
114
+ },
115
+ "url": {
116
+ "type": "string",
117
+ "description": "a string",
118
+ "title": "string"
119
+ },
120
+ "init": {
121
+ "type": "object",
122
+ "required": [],
123
+ "properties": {},
124
+ "additionalProperties": {
125
+ "$id": "/schemas/any",
126
+ "title": "any"
127
+ }
128
+ }
129
+ },
130
+ "additionalProperties": false
131
+ },
132
+ {
133
+ "type": "object",
134
+ "required": [
135
+ "type",
136
+ "filter"
137
+ ],
138
+ "properties": {
139
+ "type": {
140
+ "const": "subscription"
141
+ },
142
+ "filter": {
143
+ "type": "array",
144
+ "items": {
145
+ "type": "object",
146
+ "required": [
147
+ "type"
148
+ ],
149
+ "properties": {
150
+ "type": {
151
+ "type": "string",
152
+ "description": "a string",
153
+ "title": "string"
154
+ },
155
+ "props": {
156
+ "type": "object",
157
+ "required": [],
158
+ "properties": {},
159
+ "additionalProperties": {
160
+ "$id": "/schemas/any",
161
+ "title": "any"
162
+ }
163
+ }
164
+ },
165
+ "additionalProperties": false
166
+ }
167
+ },
168
+ "options": {
169
+ "type": "object",
170
+ "required": [],
171
+ "properties": {
172
+ "deep": {
173
+ "type": "boolean",
174
+ "description": "a boolean",
175
+ "title": "boolean"
176
+ },
177
+ "delay": {
178
+ "type": "number",
179
+ "description": "a number",
180
+ "title": "number"
181
+ }
182
+ },
183
+ "additionalProperties": false
184
+ }
185
+ },
186
+ "additionalProperties": false
187
+ }
188
+ ]
189
+ }
190
+ },
191
+ "additionalProperties": false
192
+ }
193
+ }
194
+ },
195
+ "additionalProperties": false,
196
+ "$defs": {
197
+ "object": {
198
+ "$id": "/schemas/object",
199
+ "oneOf": [
200
+ {
201
+ "type": "object"
202
+ },
203
+ {
204
+ "type": "array"
205
+ }
206
+ ],
207
+ "description": "an object in the TypeScript meaning, i.e. the `object` type",
208
+ "title": "object"
209
+ }
210
+ }
211
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from '../types';
package/src/edge/index.ts CHANGED
@@ -2,8 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- // NOTE: This module is separate because it uses the edge client.
6
- // This means it cannot be used on edge itself.
7
- // TODO(wittjosiah): Factor out.
8
-
9
5
  export * from './functions';
@@ -0,0 +1,118 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest';
6
+
7
+ import { Trigger } from '@dxos/async';
8
+ import { type Client } from '@dxos/client';
9
+ import { TestBuilder } from '@dxos/client/testing';
10
+ import { Context } from '@dxos/context';
11
+ import { Filter } from '@dxos/echo-db';
12
+ import { live } from '@dxos/live-object';
13
+ import { range } from '@dxos/util';
14
+
15
+ import { FunctionRegistry } from './function-registry';
16
+ import { createInitializedClients } from '../testing';
17
+ import { FunctionDef, type FunctionManifest } from '../types';
18
+
19
+ const testManifest: FunctionManifest = {
20
+ functions: [
21
+ {
22
+ uri: 'dxos.functions.test/hello',
23
+ route: '/hello',
24
+ handler: 'test',
25
+ },
26
+ ],
27
+ };
28
+
29
+ describe('function registry', () => {
30
+ let ctx: Context;
31
+ let testBuilder: TestBuilder;
32
+
33
+ beforeEach(async () => {
34
+ ctx = new Context();
35
+ testBuilder = new TestBuilder();
36
+ });
37
+
38
+ afterEach(async () => {
39
+ await ctx.dispose();
40
+ await testBuilder.destroy();
41
+ });
42
+
43
+ test('getUniqueByUri', async () => {
44
+ const client = (await createInitializedClients(testBuilder))[0];
45
+ const registry = createRegistry(client);
46
+ await registry.open();
47
+ for (let i = 0; i < 2; i++) {
48
+ const space = await client.spaces.create();
49
+ await registry.register(space, testManifest.functions);
50
+ }
51
+ const definitions = registry.getUniqueByUri();
52
+ expect(definitions.length).to.eq(testManifest.functions?.length);
53
+ });
54
+
55
+ describe('register', () => {
56
+ test('creates new functions', async () => {
57
+ const client = (await createInitializedClients(testBuilder))[0];
58
+ const registry = createRegistry(client);
59
+ const space = await client.spaces.create();
60
+ await registry.register(space, testManifest.functions);
61
+ const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
62
+ expect(definitions.length).to.eq(1);
63
+ expect(definitions[0].uri).to.eq(testManifest.functions?.[0]?.uri);
64
+ });
65
+
66
+ test('de-duplicates by function URI', async () => {
67
+ const client = (await createInitializedClients(testBuilder))[0];
68
+ const registry = createRegistry(client);
69
+ const space = await client.spaces.create();
70
+ const existing = space.db.add(live(FunctionDef, testManifest.functions![0]));
71
+ await registry.register(space, testManifest.functions);
72
+ const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
73
+ expect(definitions.length).to.eq(1);
74
+ expect(definitions[0].uri).to.eq(existing.uri);
75
+ });
76
+ });
77
+
78
+ describe('onFunctionsRegistered', () => {
79
+ test('called with all registered when opened', async () => {
80
+ const client = (await createInitializedClients(testBuilder))[0];
81
+ const registry = createRegistry(client);
82
+ const space = await client.spaces.create();
83
+ const definitions = range(3, () => live(FunctionDef, testManifest.functions![0]));
84
+ definitions.forEach((def) => space.db.add(def));
85
+
86
+ const functionRegistered = new Trigger<FunctionDef[]>();
87
+ registry.registered.on((fn) => {
88
+ functionRegistered.wake(fn.added);
89
+ });
90
+ void registry.open(ctx);
91
+ const functions = await functionRegistered.wait();
92
+ const expected = definitions.map((def) => def.uri).sort();
93
+ expect(functions.map((fn) => fn.uri).sort()).to.deep.eq(expected);
94
+ });
95
+
96
+ test('called when a new functions is added', async () => {
97
+ const client = (await createInitializedClients(testBuilder))[0];
98
+ const registry = createRegistry(client);
99
+ const space = await client.spaces.create();
100
+
101
+ const functionRegistered = new Trigger<FunctionDef>();
102
+ registry.registered.on((fn) => {
103
+ expect(fn.added.length).to.eq(1);
104
+ functionRegistered.wake(fn.added[0]);
105
+ });
106
+ await registry.open(ctx);
107
+ await registry.register(space, testManifest.functions);
108
+ const registered = await functionRegistered.wait();
109
+ expect(registered.uri).to.eq(testManifest.functions![0].uri);
110
+ });
111
+ });
112
+
113
+ const createRegistry = (client: Client) => {
114
+ const registry = new FunctionRegistry(client);
115
+ ctx.onDispose(() => registry.close());
116
+ return registry;
117
+ };
118
+ });
@@ -0,0 +1,104 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Event } from '@dxos/async';
6
+ import { type Client } from '@dxos/client';
7
+ import { live, Filter, type Space } from '@dxos/client/echo';
8
+ import { type Context, Resource } from '@dxos/context';
9
+ import { PublicKey } from '@dxos/keys';
10
+ import { log } from '@dxos/log';
11
+ import { ComplexMap, diff } from '@dxos/util';
12
+
13
+ import { FunctionDef, type FunctionManifest } from '../types';
14
+
15
+ export type FunctionsRegisteredEvent = {
16
+ space: Space;
17
+ added: FunctionDef[];
18
+ };
19
+
20
+ export class FunctionRegistry extends Resource {
21
+ private readonly _functionBySpaceKey = new ComplexMap<PublicKey, FunctionDef[]>(PublicKey.hash);
22
+
23
+ public readonly registered = new Event<FunctionsRegisteredEvent>();
24
+
25
+ constructor(private readonly _client: Client) {
26
+ super();
27
+ }
28
+
29
+ public getFunctions(space: Space): FunctionDef[] {
30
+ return this._functionBySpaceKey.get(space.key) ?? [];
31
+ }
32
+
33
+ public getUniqueByUri(): FunctionDef[] {
34
+ const uniqueByUri = [...this._functionBySpaceKey.values()]
35
+ .flatMap((defs) => defs)
36
+ .reduce((acc, v) => {
37
+ acc.set(v.uri, v);
38
+ return acc;
39
+ }, new Map<string, FunctionDef>());
40
+ return [...uniqueByUri.values()];
41
+ }
42
+
43
+ /**
44
+ * Loads function definitions from the manifest into the space.
45
+ * We first load all the definitions from the space to deduplicate by functionId.
46
+ */
47
+ public async register(space: Space, functions: FunctionManifest['functions']): Promise<void> {
48
+ log('register', { space: space.key, functions: functions?.length ?? 0 });
49
+ if (!functions?.length) {
50
+ return;
51
+ }
52
+ if (!space.db.graph.schemaRegistry.hasSchema(FunctionDef)) {
53
+ space.db.graph.schemaRegistry.addSchema([FunctionDef]);
54
+ }
55
+
56
+ // Sync definitions.
57
+ const { objects: existing } = await space.db.query(Filter.schema(FunctionDef)).run();
58
+ const { added } = diff(existing, functions, (a, b) => a.uri === b.uri);
59
+ // TODO(burdon): Update existing templates.
60
+ added.forEach((def) => space.db.add(live(FunctionDef, def)));
61
+
62
+ if (added.length > 0) {
63
+ await space.db.flush({ indexes: true, updates: true });
64
+ }
65
+ }
66
+
67
+ protected override async _open(): Promise<void> {
68
+ log.info('opening...');
69
+ const spacesSubscription = this._client.spaces.subscribe(async (spaces) => {
70
+ for (const space of spaces) {
71
+ if (this._functionBySpaceKey.has(space.key)) {
72
+ continue;
73
+ }
74
+
75
+ const registered: FunctionDef[] = [];
76
+ this._functionBySpaceKey.set(space.key, registered);
77
+ await space.waitUntilReady();
78
+ if (this._ctx.disposed) {
79
+ break;
80
+ }
81
+
82
+ // Subscribe to updates.
83
+ this._ctx.onDispose(
84
+ space.db.query(Filter.schema(FunctionDef)).subscribe(({ objects }) => {
85
+ const { added } = diff(registered, objects, (a, b) => a.uri === b.uri);
86
+ // TODO(burdon): Update and remove.
87
+ if (added.length > 0) {
88
+ registered.push(...added);
89
+ this.registered.emit({ space, added });
90
+ }
91
+ }),
92
+ );
93
+ }
94
+ });
95
+
96
+ // TODO(burdon): API: Normalize unsubscribe methods.
97
+ this._ctx.onDispose(() => spacesSubscription.unsubscribe());
98
+ }
99
+
100
+ protected override async _close(_: Context): Promise<void> {
101
+ log.info('closing...');
102
+ this._functionBySpaceKey.clear();
103
+ }
104
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './function-registry';