@gokiteam/goki-dev 0.2.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.
Files changed (205) hide show
  1. package/README.md +478 -0
  2. package/bin/goki-dev.js +452 -0
  3. package/bin/mcp-server.js +16 -0
  4. package/bin/secrets-cli.js +302 -0
  5. package/cli/ComposeOverrideGenerator.js +226 -0
  6. package/cli/ComposeParser.js +73 -0
  7. package/cli/ConfigGenerator.js +304 -0
  8. package/cli/ConfigManager.js +46 -0
  9. package/cli/DatabaseManager.js +94 -0
  10. package/cli/DevToolsChecker.js +21 -0
  11. package/cli/DevToolsDir.js +66 -0
  12. package/cli/DevToolsManager.js +451 -0
  13. package/cli/DockerManager.js +138 -0
  14. package/cli/FunctionManager.js +95 -0
  15. package/cli/HttpProxyRewriter.js +91 -0
  16. package/cli/Logger.js +10 -0
  17. package/cli/McpConfigManager.js +123 -0
  18. package/cli/NgrokManager.js +431 -0
  19. package/cli/ProjectCLI.js +2322 -0
  20. package/cli/PubSubManager.js +129 -0
  21. package/cli/SnapshotManager.js +88 -0
  22. package/cli/UiFormatter.js +292 -0
  23. package/cli/WebhookUrlRewriter.js +32 -0
  24. package/cli/secrets/BiometricAuth.js +125 -0
  25. package/cli/secrets/SecretInjector.js +47 -0
  26. package/cli/secrets/SecretsConfig.js +141 -0
  27. package/cli/secrets/SecretsDoctor.js +384 -0
  28. package/cli/secrets/SecretsManager.js +255 -0
  29. package/client/dist/client.d.ts +332 -0
  30. package/client/dist/client.js +507 -0
  31. package/client/dist/helpers.d.ts +62 -0
  32. package/client/dist/helpers.js +122 -0
  33. package/client/dist/index.d.ts +59 -0
  34. package/client/dist/index.js +78 -0
  35. package/client/dist/package.json +1 -0
  36. package/client/dist/types.d.ts +280 -0
  37. package/client/dist/types.js +7 -0
  38. package/config.development +46 -0
  39. package/config.test +18 -0
  40. package/guidelines/CodingStyleGuideline.md +148 -0
  41. package/guidelines/CommentingGuideline.md +10 -0
  42. package/guidelines/HttpApiImplementationGuideline.md +137 -0
  43. package/guidelines/NamingGuideline.md +182 -0
  44. package/package.json +138 -0
  45. package/patterns/api/[collectionName]/Controllers.md +62 -0
  46. package/patterns/api/[collectionName]/Logic.md +154 -0
  47. package/patterns/api/[collectionName]/Permissions.md +81 -0
  48. package/patterns/api/[collectionName]/Router.md +83 -0
  49. package/patterns/api/[collectionName]/Schemas.md +197 -0
  50. package/patterns/configs/Patterns.md +7 -0
  51. package/patterns/enums/Patterns.md +24 -0
  52. package/patterns/errorHandling/Patterns.md +185 -0
  53. package/patterns/testing/Patterns.md +232 -0
  54. package/src/Server.js +238 -0
  55. package/src/api/dashboard/Controllers.js +9 -0
  56. package/src/api/dashboard/Logic.js +76 -0
  57. package/src/api/dashboard/Router.js +11 -0
  58. package/src/api/dashboard/Schemas.js +47 -0
  59. package/src/api/data/Controllers.js +26 -0
  60. package/src/api/data/Logic.js +188 -0
  61. package/src/api/data/Router.js +16 -0
  62. package/src/api/docker/Controllers.js +33 -0
  63. package/src/api/docker/Logic.js +268 -0
  64. package/src/api/docker/Router.js +15 -0
  65. package/src/api/docker/Schemas.js +80 -0
  66. package/src/api/docs/Controllers.js +15 -0
  67. package/src/api/docs/Logic.js +85 -0
  68. package/src/api/docs/Router.js +12 -0
  69. package/src/api/export/Controllers.js +30 -0
  70. package/src/api/export/Logic.js +143 -0
  71. package/src/api/export/Router.js +18 -0
  72. package/src/api/export/Schemas.js +104 -0
  73. package/src/api/firestore/Controllers.js +152 -0
  74. package/src/api/firestore/Logic.js +474 -0
  75. package/src/api/firestore/Router.js +23 -0
  76. package/src/api/functions/Controllers.js +261 -0
  77. package/src/api/functions/Logic.js +710 -0
  78. package/src/api/functions/Router.js +50 -0
  79. package/src/api/functions/Schemas.js +193 -0
  80. package/src/api/gateway/Controllers.js +72 -0
  81. package/src/api/gateway/Logic.js +74 -0
  82. package/src/api/gateway/Router.js +10 -0
  83. package/src/api/gateway/Schemas.js +19 -0
  84. package/src/api/health/Controllers.js +14 -0
  85. package/src/api/health/Logic.js +24 -0
  86. package/src/api/health/Router.js +12 -0
  87. package/src/api/httpTraffic/Controllers.js +29 -0
  88. package/src/api/httpTraffic/Logic.js +33 -0
  89. package/src/api/httpTraffic/Router.js +9 -0
  90. package/src/api/httpTraffic/Schemas.js +23 -0
  91. package/src/api/logging/Controllers.js +80 -0
  92. package/src/api/logging/Logic.js +461 -0
  93. package/src/api/logging/Router.js +24 -0
  94. package/src/api/logging/Schemas.js +43 -0
  95. package/src/api/mqtt/Controllers.js +17 -0
  96. package/src/api/mqtt/Logic.js +66 -0
  97. package/src/api/mqtt/Router.js +12 -0
  98. package/src/api/postgres/Controllers.js +97 -0
  99. package/src/api/postgres/Logic.js +221 -0
  100. package/src/api/postgres/Router.js +21 -0
  101. package/src/api/pubsub/Controllers.js +236 -0
  102. package/src/api/pubsub/Logic.js +732 -0
  103. package/src/api/pubsub/Router.js +41 -0
  104. package/src/api/pubsub/Schemas.js +355 -0
  105. package/src/api/redis/Controllers.js +63 -0
  106. package/src/api/redis/Logic.js +239 -0
  107. package/src/api/redis/Router.js +21 -0
  108. package/src/api/scheduler/Controllers.js +27 -0
  109. package/src/api/scheduler/Logic.js +49 -0
  110. package/src/api/scheduler/Router.js +16 -0
  111. package/src/api/services/Controllers.js +26 -0
  112. package/src/api/services/Logic.js +205 -0
  113. package/src/api/services/Router.js +14 -0
  114. package/src/api/services/Schemas.js +66 -0
  115. package/src/api/snapshots/Controllers.js +37 -0
  116. package/src/api/snapshots/Logic.js +797 -0
  117. package/src/api/snapshots/Router.js +15 -0
  118. package/src/api/snapshots/Schemas.js +23 -0
  119. package/src/api/webhooks/Controllers.js +49 -0
  120. package/src/api/webhooks/Logic.js +137 -0
  121. package/src/api/webhooks/Router.js +12 -0
  122. package/src/api/webhooks/Schemas.js +31 -0
  123. package/src/configs/Application.js +147 -0
  124. package/src/configs/Default.js +13 -0
  125. package/src/consumers/BlackboxLogsConsumer.js +235 -0
  126. package/src/consumers/DockerLogsConsumer.js +687 -0
  127. package/src/db/Tables.js +66 -0
  128. package/src/db/schemas/firestore.js +18 -0
  129. package/src/db/schemas/functions.js +65 -0
  130. package/src/db/schemas/httpTraffic.js +43 -0
  131. package/src/db/schemas/logging.js +74 -0
  132. package/src/db/schemas/migrations.js +64 -0
  133. package/src/db/schemas/mqtt.js +56 -0
  134. package/src/db/schemas/pubsub.js +90 -0
  135. package/src/db/schemas/pubsubRegistry.js +22 -0
  136. package/src/db/schemas/webhooks.js +28 -0
  137. package/src/emulation/awsiot/Controllers.js +91 -0
  138. package/src/emulation/awsiot/Logic.js +70 -0
  139. package/src/emulation/awsiot/Router.js +19 -0
  140. package/src/emulation/awsiot/Server.js +100 -0
  141. package/src/emulation/firestore/Server.js +136 -0
  142. package/src/emulation/logging/Controllers.js +212 -0
  143. package/src/emulation/logging/Logic.js +416 -0
  144. package/src/emulation/logging/Router.js +36 -0
  145. package/src/emulation/logging/Schemas.js +82 -0
  146. package/src/emulation/logging/Server.js +108 -0
  147. package/src/emulation/pubsub/Controllers.js +279 -0
  148. package/src/emulation/pubsub/DefaultTopics.js +162 -0
  149. package/src/emulation/pubsub/Logic.js +427 -0
  150. package/src/emulation/pubsub/README.md +309 -0
  151. package/src/emulation/pubsub/Router.js +33 -0
  152. package/src/emulation/pubsub/Server.js +104 -0
  153. package/src/emulation/pubsub/ShadowPoller.js +276 -0
  154. package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
  155. package/src/enums/ContainerNames.js +106 -0
  156. package/src/enums/ErrorReason.js +28 -0
  157. package/src/enums/FunctionStatuses.js +15 -0
  158. package/src/enums/FunctionTriggerTypes.js +15 -0
  159. package/src/enums/GatewayState.js +7 -0
  160. package/src/enums/ServiceNames.js +68 -0
  161. package/src/jobs/DatabaseMaintenance.js +184 -0
  162. package/src/jobs/MessageHistoryCleanup.js +152 -0
  163. package/src/mcp/ApiClient.js +25 -0
  164. package/src/mcp/Server.js +52 -0
  165. package/src/mcp/prompts/debugging.js +104 -0
  166. package/src/mcp/resources/platform.js +118 -0
  167. package/src/mcp/tools/data.js +84 -0
  168. package/src/mcp/tools/docker.js +166 -0
  169. package/src/mcp/tools/firestore.js +162 -0
  170. package/src/mcp/tools/functions.js +380 -0
  171. package/src/mcp/tools/httpTraffic.js +69 -0
  172. package/src/mcp/tools/logging.js +174 -0
  173. package/src/mcp/tools/mqtt.js +37 -0
  174. package/src/mcp/tools/postgres.js +130 -0
  175. package/src/mcp/tools/pubsub.js +316 -0
  176. package/src/mcp/tools/redis.js +146 -0
  177. package/src/mcp/tools/services.js +169 -0
  178. package/src/mcp/tools/snapshots.js +88 -0
  179. package/src/mcp/tools/webhooks.js +115 -0
  180. package/src/middleware/DevProxy.js +67 -0
  181. package/src/middleware/ErrorCatcher.js +35 -0
  182. package/src/middleware/HttpProxy.js +215 -0
  183. package/src/middleware/Reply.js +24 -0
  184. package/src/middleware/TraceId.js +9 -0
  185. package/src/middleware/WebhookProxy.js +234 -0
  186. package/src/protocols/mqtt/Broker.js +92 -0
  187. package/src/protocols/mqtt/Handlers.js +175 -0
  188. package/src/protocols/mqtt/PubSubBridge.js +162 -0
  189. package/src/protocols/mqtt/Server.js +116 -0
  190. package/src/runtime/FunctionRunner.js +179 -0
  191. package/src/services/AppGatewayService.js +582 -0
  192. package/src/singletons/FirestoreBroadcaster.js +367 -0
  193. package/src/singletons/FunctionTriggerDispatcher.js +456 -0
  194. package/src/singletons/FunctionsService.js +418 -0
  195. package/src/singletons/HttpProxy.js +224 -0
  196. package/src/singletons/LogBroadcaster.js +159 -0
  197. package/src/singletons/Logger.js +49 -0
  198. package/src/singletons/MemoryJsonStore.js +175 -0
  199. package/src/singletons/MessageBroadcaster.js +190 -0
  200. package/src/singletons/PostgresBroadcaster.js +367 -0
  201. package/src/singletons/PostgresClient.js +180 -0
  202. package/src/singletons/RedisClient.js +184 -0
  203. package/src/singletons/SqliteStore.js +480 -0
  204. package/src/singletons/TickService.js +151 -0
  205. package/src/singletons/WebhookProxy.js +223 -0
@@ -0,0 +1,137 @@
1
+ # HTTP API Implementation Guideline
2
+
3
+ We don't follow REST. We only use `POST` method in the APIs. The document ID and other fields should be sent in the request's **body**.
4
+
5
+ ## Endpoints
6
+
7
+ ```plain
8
+ create POST /<version>/<collection>/create
9
+ update POST /<version>/<collection>/update
10
+ details POST /<version>/<collection>/details
11
+ list POST /<version>/<collection>/list
12
+ delete POST /<version>/<collection>/delete
13
+ batch create POST /<version>/<collection>/batchCreate
14
+ batch update POST /<version>/<collection>/batchUpdate
15
+ batch details POST /<version>/<collection>/batchDetails
16
+ batch delete POST /<version>/<collection>/batchDelete
17
+ batch action POST /<version>/<collection>/batch<action>
18
+ action POST /<version>/<collection>/<action>
19
+ ```
20
+
21
+ ## Request
22
+ ### API query param convention
23
+ The following table shows the common parameters to be used in API request.
24
+
25
+ | **Field** | **Type** | **Description** |
26
+ |-----------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
27
+ | id | Any | the ID of the resource |
28
+ | ids | Any[] | the list of IDs of the resource |
29
+ | query | String | will be globally searched in the resource |
30
+ | searchOn | String\[\] | the fields on which the "query" can be searched on (if the "query" is present) |
31
+ | filter | Object | an object with predefined keys (filters) |
32
+ | page | Object | an object with two (limit, offset) keys that are used for pagination |
33
+ | sort | Object\[{ field: String, order: 'asc' \| 'desc' }\] | an ordered list of keys that indicates how the list should be sorted |
34
+ | extend | String\[\] | a list of related objects which should be added to each item in the list. Singular or Plural form depends on the response which is an array or a single object. |
35
+ | join | String\[\] | a list of un-related objects which should be added to each item in the list |
36
+ | mask | Object | specifies what fields should be included in the response documents. It's an **object** with the singular name of the documents. |
37
+ | notFilter | Object | an object with predefined keys (not filters) |
38
+
39
+ ## Response
40
+ It should always be **an object** with the name of the resources it returns. If the resource is an array, it's plural and if it's a single object, it's singular.
41
+ In the listing APIs, a field named `total` can be presented which indicates the count of the main resource in the DB.
42
+ ### Structure
43
+ If the API returns a single resource
44
+
45
+ ```json
46
+ {
47
+ "success": true,
48
+ "status": 200,
49
+ "message": "The request succeeded.",
50
+ "data": { "[RESOURCE]": RESOURCE }
51
+ }
52
+ ```
53
+
54
+ If the API returns an array of resources
55
+
56
+ ```json
57
+ {
58
+ "success": true,
59
+ "status": 200,
60
+ "message": "The request succeeded.",
61
+ "data": {
62
+ "[RESOURCE]s": RESOURCE[],
63
+ "total": NUMBER
64
+ }
65
+ }
66
+ ```
67
+
68
+ If the API returns a resource and extends other resources
69
+
70
+ ```json
71
+ {
72
+ "success": true,
73
+ "status": 200,
74
+ "message": "The request succeeded.",
75
+ "data": {
76
+ "[RESOURCE]": RESOURCE,
77
+ "[EXTENDED_RESOURCE]": EXTENDED_RESOURCE,
78
+ "[EXTENDED_RESOURCE]s": EXTENDED_RESOURCE[]
79
+ }
80
+ }
81
+ ```
82
+
83
+ If the API returns an array of resources and extends other resources
84
+
85
+ ```json
86
+ {
87
+ "success": true,
88
+ "status": 200,
89
+ "message": "The request succeeded.",
90
+ "data": {
91
+ "[RESOURCE]s": RESOURCE[],
92
+ "total": NUMBER,
93
+ "[EXTENDED_RESOURCE]": EXTENDED_RESOURCE,
94
+ "[EXTENDED_RESOURCE]s": EXTENDED_RESOURCE[]
95
+ }
96
+ }
97
+ ```
98
+
99
+ If the API throws an error (data may include some other fields related to the error)
100
+
101
+ ```json
102
+ {
103
+ "success": false,
104
+ "status": 5XX | 4XX,
105
+ "errorReason": "REASON",
106
+ "message": "MESSAGE",
107
+ "data": {
108
+ "traceId": "ID",
109
+ "[METADATA]": METADATA,
110
+ ...
111
+ }
112
+ }
113
+ ```
114
+
115
+ **NOTE:** If the API is not defined for retrieving data (not details and list), its response may have a specific structure.
116
+
117
+ ### **Status codes**
118
+
119
+ ```plain
120
+ ----- Success -----
121
+ 200 - The request is successfully done, and data is returned in response
122
+ 202 - THe request is accepted (it doesn't mean done because maybe it's async or background process. The requester can be notified in 2 ways (polling, socket))
123
+ 204 - The request is successfully done, and there is no data to return in response
124
+
125
+ ----- Client Errors -----
126
+ 400 - validation failed
127
+ 401 - unauthorized (the JWT, api-key, etc. is not valid)
128
+ 403 - have no permission to call the endpoint (auth token is valid but permission is not granted)
129
+ 404 - The ID is not found
130
+ 406 - the request logically is not allowed
131
+ 409 - the resource already exists
132
+ 423 - the resource is locked
133
+ 429 - too many requests (this is rate-limiting status)
134
+
135
+ ----- Server Errors -----
136
+ 500 - an unknown internal error occurred
137
+ ```
@@ -0,0 +1,182 @@
1
+ # Naming Guideline
2
+
3
+ ## Variables & Classes
4
+
5
+ 1. All imported modules (third-party NPM libraries or local modules) names should be added with pascal-case.
6
+
7
+ ```javascript
8
+ import Moment from 'moment'
9
+ import Fs from 'fs'
10
+ import Lodash from 'lodash'
11
+ import { find as Find } from 'lodash' // not recommended but conventionally is correct
12
+ import lodash from 'lodash' // wrong
13
+ import { find } from 'lodash' // wrong
14
+ ```
15
+
16
+ 2. All variables should be in camel-case even if they contain abbreviations. For example **apiKey**.
17
+ 3. The variables which contain a list of objects with the same type should be named in plural form. For example, an array of **hostel** objects should be named **hostels**.
18
+ 4. The class/enum/interface name should be a pascal-case.
19
+ 5. For boolean variables, you need to start with **is**, **has**, or **does** prefixes. For example **isMale** and **hasChild**.
20
+
21
+ ## Methods
22
+
23
+ Names are a combination of words, we have 6 basic actions:
24
+ 1. get - when you want to get a single object
25
+ 2. list - when you list a few objects
26
+ 3. create - when you create an object
27
+ 4. update - when you update an existing object (The updated properties should exist)
28
+ 5. set - when you update an existing object (You don’t know if the properties exist or not)
29
+ 6. remove - when you remove an existing object
30
+ As an example, if you are in a class called User and you want to get a user object your method name is:
31
+
32
+ ```javascript
33
+ class User {
34
+ get() {}
35
+ }
36
+ ```
37
+
38
+ And if you are in the same class but want to get a users profile and get a list of permissions
39
+
40
+ ```javascript
41
+ class User {
42
+ get() {},
43
+ getProfile() {},
44
+ listPermissions() {}
45
+ }
46
+ ```
47
+
48
+ As you can see we do not postfix the method with its own namespace but anything else will be prefixed by their name.
49
+ If you are doing multiple **create**, **update**, and **remove** in a method you should prefix it with **batch**.
50
+
51
+ ```javascript
52
+ batchCreate () {}
53
+ batchRemove () {}
54
+ batchUpdate () {}
55
+ ```
56
+
57
+ If it is a background job or a delayed job to process actions it is postfixed with "job".
58
+
59
+ ```javascript
60
+ // a job to create an object
61
+ createJob () {}
62
+
63
+
64
+ // a job to create many objects in batch
65
+ batchCreateJob () {}
66
+ ```
67
+
68
+ If you have methods that are doing two actions, such as (create or update) or (create and delete), we order the names by the sequence of the actions.
69
+ If you are using the OR operation between actions, they should be ordered by the (**get, list, create, update, set, remove**).
70
+
71
+ ```javascript
72
+ class User {
73
+ //create a user or update a user based on a condition
74
+ createOrUpdate () {}
75
+
76
+
77
+ //first remove actions in batch then update user
78
+ batchRemoveActionsAndUpdate () {}
79
+
80
+
81
+ //Remove actions in batch in background then update user
82
+ batchRemoveActionsJobAndUpdate () {}
83
+ }
84
+ ```
85
+
86
+ If there are methods that are answering a question and returning a boolean, they should be prefixed by "is", "has" or "does".
87
+
88
+ ```plain
89
+ isMale() {}
90
+ hasChild() {}
91
+ ```
92
+
93
+ If there are methods that add or remove items in a field of type array, they should be prefixed with "add" or "remove".
94
+
95
+ ```plain
96
+ addImage() {}
97
+ removeCard() {}
98
+ ```
99
+
100
+ For any other method that doesn’t match the previous rules, the name of the method should start with action.
101
+
102
+ ```plain
103
+ calculateDebt() {} //debtCalculation() is wrong
104
+ notifyTravelers() {} //travelersNotification() is wrong
105
+ batchPublish() {} //batchPublishing() is wrong
106
+ ```
107
+
108
+ ## Directories & Files
109
+
110
+ Directories should be named in camel-case style, and files should be named in pascal-case. Each file should export a class, function, or object with a name exactly matching the file name.
111
+
112
+ ## Pub/Sub Topics & Subscriptions
113
+
114
+ ### **Topic name**
115
+ The topic name should represent an event inside the publisher service. For example, if a reservation is updated inside the microservice, the topic name for this event would be: "**reservationUpdated"**. The topic name should be in the past tense and camel-case.
116
+ ### **Subscription name**
117
+ The subscription name is a composition of multiple parts. All parts in the name should be camel-case:
118
+ **`<topic name>-<microservice name>-sub`**
119
+ For example**,** if the **pms** microservice wants to subscribe to the "**reservationUpdated"** topic, its subscription name would be "**reservationUpdated-pms-sub**".
120
+
121
+ Add `dev-` and `test-` prefixes for development and test environments. For example:
122
+ - Topic Name: `dev-reservationUpdated`
123
+ - Subscription Name: `dev-reservationUpdated-pms-sub`
124
+
125
+ ## Redis Keys & Redlock resource names
126
+
127
+ A Redis key or Redlock resource name should be:
128
+ - Unique
129
+ - Predictable
130
+ - Not overlapping (for example, with other microservices)
131
+ - Can be a combination of multiple identifiers and literals, separated with a ":".
132
+ - Follow this convention:
133
+ - Redis key: `<key>:<microservice name>`
134
+ - Redlock resource name: `<name>:<microservice name>:lock`
135
+
136
+ Examples:
137
+ ```javascript
138
+ // microservice name is "bunny"
139
+ const redisKey = `passportNumber:${userId}:bunny`
140
+ const redlockResourceName = `order:${orderId}:bunny:lock`
141
+ ```
142
+
143
+ ## Collections & Models
144
+
145
+ 1. The database collection, table, or index should be in the plural form.
146
+ ```
147
+ # Bad 😖
148
+ user
149
+ staff
150
+ hostel
151
+
152
+ # Good 😌
153
+ users
154
+ staffs
155
+ hostels
156
+ ```
157
+
158
+ 2. The firestore collections should be prefixed with the environment short name.
159
+ ```
160
+ # Bad 😖
161
+ users
162
+ staffs
163
+ hostels
164
+
165
+ # Good 😌
166
+ dev_users
167
+ stg_staffs
168
+ prd_hostels
169
+ ```
170
+
171
+ 3. The models should be in the singular form
172
+ ```
173
+ # Bad 😖
174
+ Users
175
+ Staffs
176
+ Hostels
177
+
178
+ # Good 😌
179
+ User
180
+ Staff
181
+ Hostel
182
+ ```
package/package.json ADDED
@@ -0,0 +1,138 @@
1
+ {
2
+ "name": "@gokiteam/goki-dev",
3
+ "version": "0.2.0",
4
+ "description": "Unified local development platform for Goki services",
5
+ "type": "module",
6
+ "main": "./client/dist/index.js",
7
+ "types": "./client/dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./client/dist/index.d.ts",
11
+ "import": "./client/dist/index.js",
12
+ "require": "./client/dist/index.js"
13
+ }
14
+ },
15
+ "typesVersions": {
16
+ "*": {
17
+ ".": [
18
+ "./client/dist/index.d.ts"
19
+ ]
20
+ }
21
+ },
22
+ "bin": {
23
+ "goki-dev": "./bin/goki-dev.js",
24
+ "goki-dev-mcp": "./bin/mcp-server.js",
25
+ "goki-secrets": "./bin/secrets-cli.js"
26
+ },
27
+ "files": [
28
+ "bin/",
29
+ "cli/",
30
+ "src/",
31
+ "client/dist/",
32
+ "config.development",
33
+ "config.test",
34
+ "guidelines/",
35
+ "patterns/"
36
+ ],
37
+ "scripts": {
38
+ "start": "dotenv -e config.development node src/Server.js",
39
+ "dev": "dotenv -e config.development node src/Server.js",
40
+ "dev:watch": "dotenv -e config.development node --watch src/Server.js",
41
+ "dev:ui": "concurrently \"npm run dev\" \"cd ui && npm start\" --names \"backend,frontend\" --prefix-colors \"blue,green\"",
42
+ "ui:start": "cd ui && npm start",
43
+ "ui:build": "cd ui && npm run build",
44
+ "build:client": "cd client && npx tsc && echo '{\"type\":\"commonjs\"}' > dist/package.json",
45
+ "prepublishOnly": "npm run build:client",
46
+ "test": "dotenv -e config.test mocha 'tests/**/*.test.js'",
47
+ "test:api": "dotenv -e config.test mocha 'tests/api/**/*.test.js'",
48
+ "test:emulation": "dotenv -e config.test mocha 'tests/emulation/**/*.test.js'",
49
+ "test:integration": "dotenv -e config.test mocha 'tests/integration/**/*.test.js' --timeout 10000",
50
+ "test:all": "npm run test:integration",
51
+ "test:e2e": "playwright test",
52
+ "test:e2e:ui": "playwright test tests/e2e/ui",
53
+ "test:e2e:workflows": "playwright test tests/e2e/workflows",
54
+ "test:e2e:headed": "playwright test --headed",
55
+ "test:e2e:debug": "playwright test --debug",
56
+ "lint": "standard --fix",
57
+ "docker:up": "docker-compose up -d",
58
+ "docker:up:build": "docker-compose up -d --build",
59
+ "docker:down": "docker-compose down",
60
+ "docker:logs": "docker-compose logs -f",
61
+ "docker:services": "docker-compose -f docker-compose.services.yml up -d",
62
+ "mcp": "node bin/mcp-server.js",
63
+ "cleanup:jobs": "node scripts/cleanup-bull-jobs.js"
64
+ },
65
+ "keywords": [
66
+ "goki",
67
+ "dev-tools",
68
+ "pubsub",
69
+ "mqtt",
70
+ "emulator",
71
+ "local-development"
72
+ ],
73
+ "author": "Goki Team",
74
+ "license": "UNLICENSED",
75
+ "engines": {
76
+ "node": ">=18.0.0"
77
+ },
78
+ "publishConfig": {
79
+ "access": "public"
80
+ },
81
+ "dependencies": {
82
+ "@gokiteam/koa": "^3.0.6",
83
+ "@gokiteam/oops": "*",
84
+ "@google-cloud/functions-framework": "^5.0.2",
85
+ "@koa/cors": "^5.0.0",
86
+ "@modelcontextprotocol/sdk": "^1.27.0",
87
+ "@portalteam/protocols": "latest",
88
+ "@zowe/secrets-for-zowe-sdk": "^8.29.4",
89
+ "aedes": "^0.51.0",
90
+ "axios": "^1.6.0",
91
+ "better-sqlite3": "^12.6.2",
92
+ "bull": "^3.29.3",
93
+ "chalk": "^5.3.0",
94
+ "commander": "^11.0.0",
95
+ "concurrently": "^9.2.1",
96
+ "dockerode": "^3.3.5",
97
+ "dotenv": "^16.4.0",
98
+ "execa": "^8.0.1",
99
+ "firebase-admin": "^13.6.1",
100
+ "http-proxy": "^1.18.1",
101
+ "inquirer": "^9.2.0",
102
+ "ioredis": "^5.9.2",
103
+ "joi": "^17.13.0",
104
+ "jsonpath-plus": "^10.4.0",
105
+ "koa": "^2.15.0",
106
+ "koa-bodyparser": "^4.4.1",
107
+ "koa-router": "^12.0.1",
108
+ "koa-static": "^5.0.0",
109
+ "macos-touchid": "^1.0.3",
110
+ "moment": "^2.30.0",
111
+ "mqtt-packet": "^9.0.0",
112
+ "node-cron": "^4.2.1",
113
+ "ora": "^7.0.1",
114
+ "pg": "^8.18.0",
115
+ "socket.io": "^4.7.0",
116
+ "uuid": "^9.0.1"
117
+ },
118
+ "devDependencies": {
119
+ "@google-cloud/logging": "^11.2.1",
120
+ "@playwright/test": "^1.58.1",
121
+ "@types/better-sqlite3": "^7.6.13",
122
+ "chai": "^5.1.0",
123
+ "dotenv-cli": "^7.4.0",
124
+ "eventsource": "^4.1.0",
125
+ "mocha": "^10.3.0",
126
+ "mqtt": "^5.15.0",
127
+ "playwright": "^1.58.1",
128
+ "standard": "^17.1.0",
129
+ "supertest": "^6.3.0",
130
+ "typescript": "^5.9.3"
131
+ },
132
+ "standard": {
133
+ "env": [
134
+ "mocha"
135
+ ]
136
+ },
137
+ "packageManager": "yarn@3.8.7+sha512.bbe7e310ff7fd20dc63b111110f96fe18192234bb0d4f10441fa6b85d2b644c8923db8fbe6d7886257ace948440ab1f83325ad02af457a1806cdc97f03d2508e"
138
+ }
@@ -0,0 +1,62 @@
1
+ ## API Controller definition
2
+ For each API endpoints:
3
+ - There should be a controller function which is a koa middleware
4
+ - The controller exports data from request, then calls the endpoint logic, then attaches response/status to koa context
5
+
6
+ The common way of defining controller is like:
7
+ ```javascript
8
+ import { Logic } from './Logic.js'
9
+
10
+ export const Controllers = {
11
+ // other api endpoints controllers...
12
+ async create (ctx) {
13
+ const { traceId } = ctx.custom
14
+ const { body: data } = ctx.request
15
+ ctx.body = await Logic.create({ data, traceId })
16
+ }
17
+ }
18
+ ```
19
+
20
+ If the endpoint doesn't return data on API response:
21
+ ```javascript
22
+ import { Logic } from './Logic.js'
23
+
24
+ export const Controllers = {
25
+ // other api endpoints controllers...
26
+ async create (ctx) {
27
+ const { traceId } = ctx.custom
28
+ const { body: data } = ctx.request
29
+ ctx.status = 204
30
+ }
31
+ }
32
+ ```
33
+
34
+ If the endpoint returns data on response with a status other than 200:
35
+ ```javascript
36
+ import { Logic } from './Logic.js'
37
+
38
+ export const Controllers = {
39
+ // other api endpoints controllers...
40
+ async create (ctx) {
41
+ const { traceId } = ctx.custom
42
+ const { body: data } = ctx.request
43
+ ctx.body = await Logic.create({ data, traceId })
44
+ ctx.status = 202
45
+ }
46
+ }
47
+ ```
48
+
49
+ If the response should bypass the default response formatting rules:
50
+ ```javascript
51
+ import { Logic } from './Logic.js'
52
+
53
+ export const Controllers = {
54
+ // other api endpoints controllers...
55
+ async create (ctx) {
56
+ const { traceId } = ctx.custom
57
+ const { body: data } = ctx.request
58
+ ctx.body = await Logic.create({ data, traceId })
59
+ ctx.custom.isRawBody = true
60
+ }
61
+ }
62
+ ```
@@ -0,0 +1,154 @@
1
+ ## API Logic definition
2
+ For each API endpoint:
3
+ - There is logic function in "src/api/[collection]/Logic.js"
4
+ - The logic implementation includes (in order)
5
+ - Checking for all logical edge cases and throw Oops errors with proper reasons
6
+ - Check if the supplied entity IDs in the request exist
7
+ - Check value boundaries, logical relationships, limits enforced by configs, etc.
8
+ - The edge cases should be added to "src/api/[collection]/Schemas.js" as error responses
9
+ - All reasons should be defined in "src/enums/ErrorReason.js" enum
10
+ - DB operations, including ACID transaction
11
+ - Call shared logic from `src/logic` directory if required
12
+ - Publish pub/sub messages if required
13
+ - If there is an operator in the request, add
14
+ - Return data for controller to be set in API response
15
+
16
+ Coding pattern:
17
+ ```javascript
18
+ import { ErrorReason } from '../../enums/ErrorReason.js'
19
+ import { TryPublishUserCreatedMessage } from '../../logic/TryPublishUserCreatedMessage.js'
20
+ import { User } from '../../models/User.js'
21
+
22
+ export const Logic = {
23
+ // other endpoints logic implementation...
24
+ async create (params) {
25
+ const { data, traceId } = params || {}
26
+ const { firstName, lastName, email } = data
27
+ const logicName = Logic.create.name
28
+ const exists = await User.doesExistCompositeId({ email })
29
+ if (exists) {
30
+ throw Oops.notAcceptable('The email already exists')
31
+ .reason(ErrorReason.userAlreadyExists)
32
+ .resource(logicName)
33
+ .meta({ traceId, email })
34
+ }
35
+ const user = new User({ firstName, lastName, email })
36
+ await user.create()
37
+ return { user: user.mask() }
38
+ }
39
+ }
40
+ ```
41
+
42
+ If an endpoint needs to update multiple models atomically, use transactions:
43
+
44
+ ```javascript
45
+ import { PostgresTransactionHandler } from '@gokiteam/odm'
46
+ import { PostgresPool } from '../../singletons/PostgresPool.js'
47
+ import { Device } from '../../models/Device.js'
48
+ import { DeviceStatus } from '../../models/DeviceStatus.js'
49
+
50
+ export const Logic = {
51
+ // other endpoints logic implementation...
52
+ async create (params) {
53
+ const { data, traceId } = params || {}
54
+ const { propertyId, macAddress, model } = data
55
+ const logicName = Logic.create.name
56
+ const exists = await Device.doesExist(macAddress)
57
+ if (exists) {
58
+ throw Oops.notAcceptable('Device already exists')
59
+ .reason(ErrorReason.deviceAlreadyExists)
60
+ .resource(logicName)
61
+ .meta({ traceId, macAddress })
62
+ }
63
+ const device = new Device({ propertyId, macAddress, model })
64
+ const deviceStatus = new DeviceStatus({ deviceId: macAddress })
65
+ const handler = new PostgresTransactionHandler(PostgresPool)
66
+ await handler
67
+ .create(device)
68
+ .create(deviceStatus)
69
+ .run()
70
+ return { device: device.mask() }
71
+ }
72
+ }
73
+ ```
74
+
75
+ For more transaction patterns, refer to `patterns/transactions/Patterns.md`.
76
+
77
+ If a request includes an "operator" data, a pub/sub message should be published like this:
78
+
79
+ ```javascript
80
+ import Moment from 'moment'
81
+ import { v4 as IdGenerator } from 'uuid'
82
+
83
+ import { Application } from '../../configs/Application.js'
84
+ import { ErrorReason } from '../../enums/ErrorReason.js'
85
+ import { TryPublishBatchEventLogRequired } from '../../logic/TryPublishBatchEventLogRequired.js'
86
+ import { User } from '../../models/User.js'
87
+
88
+ export const Logic = {
89
+ // other endpoints logic implementation...
90
+ async create (params) {
91
+ const { data, traceId } = params || {}
92
+ const { propertyId, firstName, lastName, email, operator } = data
93
+ const logicName = Logic.create.name
94
+ const exists = await User.doesExistCompositeId({ email })
95
+ if (exists) {
96
+ throw Oops.notAcceptable('The email already exists')
97
+ .reason(ErrorReason.userAlreadyExists)
98
+ .resource(logicName)
99
+ .meta({ traceId, email })
100
+ }
101
+ const user = new User({ firstName, lastName, email })
102
+ await user.create()
103
+ TryPublishBatchEventLogRequired({
104
+ propertyId,
105
+ records: [
106
+ {
107
+ operator,
108
+ targetId: user.id,
109
+ targetType: EventTargetType.user,
110
+ data: { new: user.mask(), old: {} },
111
+ event: Event.userCreated
112
+ }
113
+ ],
114
+ traceId
115
+ })
116
+ return { user: user.mask() }
117
+ }
118
+ }
119
+ ```
120
+
121
+ For complex Logic files with shared logic between multiple endpoints, use internal methods object:
122
+
123
+ ```javascript
124
+ const methods = {
125
+ async createDevice (params) {
126
+ const { propertyId, macAddress, model, supportedProtocols } = params || {}
127
+ const logicName = methods.createDevice.name
128
+ // ... shared logic implementation
129
+ const device = new Device({ propertyId, macAddress, model })
130
+ const deviceStatus = new DeviceStatus({ deviceId: macAddress })
131
+ const handler = new PostgresTransactionHandler(PostgresPool)
132
+ await handler.create(device).create(deviceStatus).run()
133
+ return { device: device.mask(), deviceStatus: deviceStatus.mask() }
134
+ }
135
+ }
136
+
137
+ export const Logic = {
138
+ // other endpoints logic implementation...
139
+ async create (params) {
140
+ const { data, traceId } = params || {}
141
+ return methods.createDevice({ ...data, traceId })
142
+ },
143
+ async createWithModel (params) {
144
+ const { data, traceId } = params || {}
145
+ const { model } = data
146
+ return methods.createDevice({ ...data, model, traceId })
147
+ }
148
+ }
149
+ ```
150
+
151
+ This pattern is useful when:
152
+ - Multiple endpoints share similar logic
153
+ - You need to call the same logic from different entry points
154
+ - You want to keep the exported Logic object clean and focused on endpoint routing