jxp 3.0.0 → 4.0.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 (219) hide show
  1. package/.env.sample +31 -6
  2. package/README.md +27 -13
  3. package/dist/bin/init_db.d.ts +3 -0
  4. package/dist/bin/init_db.d.ts.map +1 -0
  5. package/dist/bin/init_db.js +48 -0
  6. package/dist/bin/init_db.js.map +1 -0
  7. package/dist/bin/server.d.ts +4 -0
  8. package/dist/bin/server.d.ts.map +1 -0
  9. package/dist/bin/server.js +132 -0
  10. package/dist/bin/server.js.map +1 -0
  11. package/dist/bin/setup.d.ts +3 -0
  12. package/dist/bin/setup.d.ts.map +1 -0
  13. package/dist/bin/setup.js +185 -0
  14. package/dist/bin/setup.js.map +1 -0
  15. package/dist/globals.d.ts +14 -0
  16. package/dist/globals.d.ts.map +1 -0
  17. package/dist/globals.js +3 -0
  18. package/dist/globals.js.map +1 -0
  19. package/dist/libs/aggregate_guard.d.ts +5 -0
  20. package/dist/libs/aggregate_guard.d.ts.map +1 -0
  21. package/dist/libs/aggregate_guard.js +58 -0
  22. package/dist/libs/aggregate_guard.js.map +1 -0
  23. package/dist/libs/bulkwrite_guard.d.ts +5 -0
  24. package/dist/libs/bulkwrite_guard.d.ts.map +1 -0
  25. package/dist/libs/bulkwrite_guard.js +44 -0
  26. package/dist/libs/bulkwrite_guard.js.map +1 -0
  27. package/dist/libs/cache.d.ts +2 -0
  28. package/dist/libs/cache.d.ts.map +1 -0
  29. package/dist/libs/cache.js +94 -0
  30. package/dist/libs/cache.js.map +1 -0
  31. package/dist/libs/call_guard.d.ts +3 -0
  32. package/dist/libs/call_guard.d.ts.map +1 -0
  33. package/dist/libs/call_guard.js +17 -0
  34. package/dist/libs/call_guard.js.map +1 -0
  35. package/dist/libs/connection_string.d.ts +3 -0
  36. package/dist/libs/connection_string.d.ts.map +1 -0
  37. package/dist/libs/connection_string.js +4 -0
  38. package/dist/libs/connection_string.js.map +1 -0
  39. package/dist/libs/datamunging.d.ts +2 -0
  40. package/dist/libs/datamunging.d.ts.map +1 -0
  41. package/dist/libs/datamunging.js +320 -0
  42. package/dist/libs/datamunging.js.map +1 -0
  43. package/dist/libs/docs.d.ts +2 -0
  44. package/dist/libs/docs.d.ts.map +1 -0
  45. package/dist/libs/docs.js +307 -0
  46. package/dist/libs/docs.js.map +1 -0
  47. package/dist/libs/groups.d.ts +2 -0
  48. package/dist/libs/groups.d.ts.map +1 -0
  49. package/dist/libs/groups.js +136 -0
  50. package/dist/libs/groups.js.map +1 -0
  51. package/dist/libs/jxp.d.ts +4 -0
  52. package/dist/libs/jxp.d.ts.map +1 -0
  53. package/dist/libs/jxp.js +1157 -0
  54. package/dist/libs/jxp.js.map +1 -0
  55. package/dist/libs/link_index.d.ts +14 -0
  56. package/dist/libs/link_index.d.ts.map +1 -0
  57. package/dist/libs/link_index.js +37 -0
  58. package/dist/libs/link_index.js.map +1 -0
  59. package/dist/libs/load-config.d.ts +16 -0
  60. package/dist/libs/load-config.d.ts.map +1 -0
  61. package/dist/libs/load-config.js +155 -0
  62. package/dist/libs/load-config.js.map +1 -0
  63. package/dist/libs/login.d.ts +2 -0
  64. package/dist/libs/login.d.ts.map +1 -0
  65. package/dist/libs/login.js +244 -0
  66. package/dist/libs/login.js.map +1 -0
  67. package/dist/libs/modeldir.d.ts +5 -0
  68. package/dist/libs/modeldir.d.ts.map +1 -0
  69. package/dist/libs/modeldir.js +15 -0
  70. package/dist/libs/modeldir.js.map +1 -0
  71. package/dist/libs/query_limits.d.ts +2 -0
  72. package/dist/libs/query_limits.d.ts.map +1 -0
  73. package/dist/libs/query_limits.js +84 -0
  74. package/dist/libs/query_limits.js.map +1 -0
  75. package/dist/libs/query_manipulation.d.ts +3 -0
  76. package/dist/libs/query_manipulation.d.ts.map +1 -0
  77. package/{libs → dist/libs}/query_manipulation.js +22 -21
  78. package/dist/libs/query_manipulation.js.map +1 -0
  79. package/dist/libs/query_sanitize.d.ts +5 -0
  80. package/dist/libs/query_sanitize.d.ts.map +1 -0
  81. package/dist/libs/query_sanitize.js +109 -0
  82. package/dist/libs/query_sanitize.js.map +1 -0
  83. package/dist/libs/response_sanitize.d.ts +7 -0
  84. package/dist/libs/response_sanitize.d.ts.map +1 -0
  85. package/dist/libs/response_sanitize.js +57 -0
  86. package/dist/libs/response_sanitize.js.map +1 -0
  87. package/dist/libs/safe_error.d.ts +3 -0
  88. package/dist/libs/safe_error.d.ts.map +1 -0
  89. package/dist/libs/safe_error.js +14 -0
  90. package/dist/libs/safe_error.js.map +1 -0
  91. package/dist/libs/schema.d.ts +14 -0
  92. package/dist/libs/schema.d.ts.map +1 -0
  93. package/dist/libs/schema.js +98 -0
  94. package/dist/libs/schema.js.map +1 -0
  95. package/dist/libs/schema_description.d.ts +3 -0
  96. package/dist/libs/schema_description.d.ts.map +1 -0
  97. package/{libs → dist/libs}/schema_description.js +12 -21
  98. package/dist/libs/schema_description.js.map +1 -0
  99. package/dist/libs/security.d.ts +2 -0
  100. package/dist/libs/security.d.ts.map +1 -0
  101. package/dist/libs/security.js +428 -0
  102. package/dist/libs/security.js.map +1 -0
  103. package/dist/libs/setup.d.ts +2 -0
  104. package/dist/libs/setup.d.ts.map +1 -0
  105. package/dist/libs/setup.js +87 -0
  106. package/dist/libs/setup.js.map +1 -0
  107. package/dist/libs/startup-deprecations.d.ts +2 -0
  108. package/dist/libs/startup-deprecations.d.ts.map +1 -0
  109. package/dist/libs/startup-deprecations.js +21 -0
  110. package/dist/libs/startup-deprecations.js.map +1 -0
  111. package/dist/libs/startup.d.ts +16 -0
  112. package/dist/libs/startup.d.ts.map +1 -0
  113. package/dist/libs/startup.js +63 -0
  114. package/dist/libs/startup.js.map +1 -0
  115. package/dist/libs/ws.d.ts +2 -0
  116. package/dist/libs/ws.d.ts.map +1 -0
  117. package/{libs → dist/libs}/ws.js +91 -73
  118. package/dist/libs/ws.js.map +1 -0
  119. package/dist/models/apikey_model.d.ts +10 -0
  120. package/dist/models/apikey_model.d.ts.map +1 -0
  121. package/dist/models/apikey_model.js +14 -0
  122. package/dist/models/apikey_model.js.map +1 -0
  123. package/dist/models/link_model.d.ts +9 -0
  124. package/dist/models/link_model.d.ts.map +1 -0
  125. package/dist/models/link_model.js +20 -0
  126. package/dist/models/link_model.js.map +1 -0
  127. package/dist/models/refreshtoken_model.d.ts +11 -0
  128. package/dist/models/refreshtoken_model.d.ts.map +1 -0
  129. package/dist/models/refreshtoken_model.js +22 -0
  130. package/dist/models/refreshtoken_model.js.map +1 -0
  131. package/dist/models/test_model.d.ts +24 -0
  132. package/dist/models/test_model.d.ts.map +1 -0
  133. package/dist/models/test_model.js +44 -0
  134. package/dist/models/test_model.js.map +1 -0
  135. package/dist/models/token_model.d.ts +14 -0
  136. package/dist/models/token_model.d.ts.map +1 -0
  137. package/dist/models/token_model.js +25 -0
  138. package/dist/models/token_model.js.map +1 -0
  139. package/dist/models/user_model.d.ts +15 -0
  140. package/dist/models/user_model.d.ts.map +1 -0
  141. package/dist/models/user_model.js +38 -0
  142. package/dist/models/user_model.js.map +1 -0
  143. package/dist/models/usergroups_model.d.ts +9 -0
  144. package/dist/models/usergroups_model.d.ts.map +1 -0
  145. package/dist/models/usergroups_model.js +18 -0
  146. package/dist/models/usergroups_model.js.map +1 -0
  147. package/dist/types/jxp-config.d.ts +134 -0
  148. package/dist/types/jxp-config.d.ts.map +1 -0
  149. package/dist/types/jxp-config.js +3 -0
  150. package/dist/types/jxp-config.js.map +1 -0
  151. package/dist/types/schema-fields.d.ts +43 -0
  152. package/dist/types/schema-fields.d.ts.map +1 -0
  153. package/dist/types/schema-fields.js +3 -0
  154. package/dist/types/schema-fields.js.map +1 -0
  155. package/docs/aggregations.md +40 -21
  156. package/docs/api.md +55 -15
  157. package/docs/authentication.md +60 -37
  158. package/docs/bulk_writes.md +11 -8
  159. package/docs/caching.md +28 -11
  160. package/docs/changelog.md +237 -31
  161. package/docs/configuration.md +99 -118
  162. package/docs/hooks.md +12 -8
  163. package/docs/index.md +17 -17
  164. package/docs/installation.md +23 -18
  165. package/docs/permissions.md +33 -19
  166. package/docs/queries.md +9 -5
  167. package/docs/schemas.md +21 -10
  168. package/docs/special.md +4 -4
  169. package/docs/typescript.md +86 -0
  170. package/docs/websocket.md +99 -1
  171. package/mkdocs.yml +1 -0
  172. package/package.json +121 -69
  173. package/templates/api.pug +32 -0
  174. package/templates/app-server.js +43 -0
  175. package/templates/app.env.sample +8 -0
  176. package/templates/assets/api-console.js +133 -0
  177. package/templates/assets/docs.css +227 -0
  178. package/templates/index.pug +62 -2
  179. package/templates/layout.pug +17 -11
  180. package/templates/md.pug +1 -1
  181. package/templates/model.pug +117 -68
  182. package/templates/navbar.pug +14 -21
  183. package/templates/sidebar.pug +30 -0
  184. package/.env +0 -9
  185. package/.eslintrc.json +0 -20
  186. package/.jshintrc +0 -4
  187. package/.vscode/launch.json +0 -34
  188. package/.vscode/settings.json +0 -3
  189. package/Dockerfile +0 -7
  190. package/bin/init_db.js +0 -54
  191. package/bin/server.js +0 -131
  192. package/bin/setup.js +0 -190
  193. package/config_sample.json +0 -10
  194. package/docker-compose.yml +0 -32
  195. package/libs/cache.js +0 -92
  196. package/libs/connection_string.js +0 -24
  197. package/libs/datamunging.js +0 -335
  198. package/libs/docs.js +0 -162
  199. package/libs/groups.js +0 -133
  200. package/libs/jxp.js +0 -1208
  201. package/libs/login.js +0 -222
  202. package/libs/modeldir.js +0 -14
  203. package/libs/schema.js +0 -99
  204. package/libs/security.js +0 -410
  205. package/libs/setup.js +0 -90
  206. package/models/apikey_model.js +0 -10
  207. package/models/link_model.js +0 -17
  208. package/models/refreshtoken_model.js +0 -20
  209. package/models/test_model.js +0 -50
  210. package/models/token_model.js +0 -23
  211. package/models/user_model.js +0 -38
  212. package/models/usergroups_model.js +0 -15
  213. package/setup.sh +0 -3
  214. package/test/.eslintrc.json +0 -13
  215. package/test/auth.js +0 -190
  216. package/test/init.js +0 -92
  217. package/test/setup.test.js +0 -62
  218. package/test/test.js +0 -1527
  219. package/test/wstest.js.old +0 -124
@@ -4,27 +4,31 @@ JXP would typically run as a stand-alone server, although you can include it as
4
4
 
5
5
  ## Requirements
6
6
 
7
- JXP runs on [Node JS](https://nodejs.org/en/). We support Node v10 and above, and recommend Node v16. It has been tested up to Node v17.
7
+ JXP runs on [Node.js](https://nodejs.org/en/). **Node.js 22 or newer is required** (`engines.node` is `>=22.0.0` in the package). We recommend the current Node 22 LTS release.
8
8
 
9
- JXP requires a [Mongo](https://www.mongodb.com/) database server to connect to. You can host your own, or we also support connecting to [MongoDB Atlas](https://www.mongodb.com/cloud/atlas), which has a free tier if you don't want to have Mongo running locally.
9
+ JXP requires a [MongoDB](https://www.mongodb.com/) database server to connect to. You can host your own, or connect to [MongoDB Atlas](https://www.mongodb.com/cloud/atlas), which has a free tier if you don't want Mongo running locally.
10
10
 
11
- JXP will also take advantage of Memcache if you have it installed, although it's not a requirement. We can use a local or external Memcache server.
11
+ Optional in-process response caching is available via environment variables (see [Caching](caching.md)). No external Memcache server is required.
12
12
 
13
- If you want to send out forgotten password links, you'll need an SMTP server you can connect to.
13
+ If you want to send forgotten-password links, configure SMTP on your `JXP()` options object (see [Configuration](configuration.md#smtp-and-password-recovery)).
14
+
15
+ ### JXP 4
16
+
17
+ JXP 4 is implemented in TypeScript and published as compiled JavaScript in `dist/`. The npm package includes a prebuilt `dist/`; `npm install jxp` does not compile from source. Cloning the repo and running `npm install` builds automatically when `src/` is present. Consumer apps still define models as `*_model.js` files in `MODEL_DIR`.
14
18
 
15
19
  ## Running on Docker
16
20
 
17
- You can run JXP on Docker using Docker Compose. Just run `docker-compose up -d` and it should Just Work (tm).
21
+ The repository includes `Dockerfile` and `docker-compose.yml`. Review those files and align environment variables with [Configuration](configuration.md) (`MONGO_CONNECTION_STRING` or `MONGODB_*` vars) before running `docker compose up -d`.
18
22
 
19
23
  ## Installing the easy way
20
24
 
21
- JXP has a helper that will set up and configure an instance for you.
25
+ JXP has a helper that will set up and configure an instance for you.
22
26
 
23
27
  First, install JXP globally:
24
28
 
25
29
  `npm install --global jxp`
26
30
 
27
- Now run `jxp-setup <directory>` and follow the prompts. This will install the necessary files, give you a few models to get started, and help you set up an admin username and password.
31
+ Now run `jxp-setup <directory>` and follow the prompts. This will install the necessary files, give you a few models to get started, and help you set up an admin username and password.
28
32
 
29
33
  Once the setup is complete, use `npm start` to start the server.
30
34
 
@@ -42,7 +46,8 @@ Then include in your project:
42
46
 
43
47
  NOTE: All the `/setup` endpoints will only run if the user table is empty to ensure that you can't overwrite an existing installation.
44
48
 
45
- You can set up a first user using the `/setup` endpoint, with the following default properties that you can override:
49
+ You can set up a first user using the `/setup` endpoint (GET or POST), with the following default properties that you can override:
50
+
46
51
  ```js
47
52
  {
48
53
  email: "admin@example.com",
@@ -51,7 +56,8 @@ You can set up a first user using the `/setup` endpoint, with the following defa
51
56
  }
52
57
  ```
53
58
 
54
- Response:
59
+ Response:
60
+
55
61
  ```js
56
62
  {
57
63
  status: "success",
@@ -61,7 +67,7 @@ Response:
61
67
  }
62
68
  ```
63
69
 
64
- You can scaffold an entire system by using the `/setup/data` endpoint. This writes directly to the database, and doesn't go through the API, so be careful -- features like the automatic password encryption will not take effect. You also need to use the collection names, not the model names, eg. the `user` model becomes `users`.
70
+ You can scaffold an entire system by using the `/setup/data` endpoint. This writes directly to the database, and doesn't go through the API, so be careful features like automatic password encryption will not take effect. You also need to use the collection names, not the model names, eg. the `user` model becomes `users`.
65
71
 
66
72
  ```js
67
73
  {
@@ -76,22 +82,21 @@ You can scaffold an entire system by using the `/setup/data` endpoint. This writ
76
82
  ```
77
83
 
78
84
  Response:
85
+
79
86
  ```js
80
87
  {
81
- status: 'success',
88
+ status: "success",
82
89
  results: {
83
90
  users: {
84
- result: [Object],
85
- ops: [Array],
91
+ acknowledged: true,
86
92
  insertedCount: 2,
87
- insertedIds: [Object]
93
+ insertedIds: { ... }
88
94
  },
89
95
  tests: {
90
- result: [Object],
91
- ops: [Array],
96
+ acknowledged: true,
92
97
  insertedCount: 1,
93
- insertedIds: [Object]
98
+ insertedIds: { ... }
94
99
  }
95
100
  }
96
101
  }
97
- ```
102
+ ```
@@ -3,29 +3,46 @@
3
3
  ## Access Control
4
4
 
5
5
  You can set permissions on each model for user groups which you can define yourself. There are also a few special groups:
6
+
6
7
  * `all` -- All users, whether they authenticate or not.
7
- * `owner` -- The user who created an item. This requires the model to have a `_owner_id` property (see the above example).
8
+ * `owner` -- The user who created an item. This requires the model to have a `_owner_id` property (added automatically by JXPSchema).
8
9
  * `user` -- Any authenticated user.
9
10
  * `admin` -- Any admin user.
10
11
 
11
12
  Each group can have one, some or all of the following permissions:
13
+
12
14
  * `c` -- Create -- the ability to create a new record (a POST operation)
13
15
  * `r` -- Retrieve -- the ability to read a record or all records (a GET operation)
14
16
  * `u` -- Update -- the ability to update an existing record (a PUT operation)
15
- * `d` -- Delete -- the ability to delete an existin record (a DELETE operation)
17
+ * `d` -- Delete -- the ability to delete an existing record (a DELETE operation)
16
18
 
17
- The permissions are defined in the model as follows:
19
+ The permissions are defined in the schema constructor options (preferred):
20
+
21
+ ```js
22
+ const TestSchema = new JXPSchema({
23
+ foo: String,
24
+ }, {
25
+ perms: {
26
+ admin: "crud",
27
+ owner: "rud",
28
+ user: "cr",
29
+ all: "r",
30
+ },
31
+ });
32
+ ```
33
+
34
+ Alternatively, after creating the schema:
18
35
 
19
36
  ```js
20
37
  TestSchema.set("_perms", {
21
- admin: "crud", // CRUD = Create, Retrieve, Update and Delete
38
+ admin: "crud",
22
39
  owner: "rud",
23
40
  user: "cr",
24
- all: "r" // Unauthenticated users will be able to read from test, but that is all
41
+ all: "r",
25
42
  });
26
43
  ```
27
44
 
28
- In this case, the admin and record owner have full permissions. (We don't need to set "create" for the owner, obvz.) An authenticated user can create and retrive records. Everyone can read everything.
45
+ In this case, the admin and record owner have full permissions. (We don't need to set "create" for the owner, obvz.) An authenticated user can create and retrieve records. Everyone can read everything.
29
46
 
30
47
  To make a model completely private, just don't set the perms.
31
48
 
@@ -36,32 +53,29 @@ You can add and remove groups to a user with the `/groups/:user_id` endpoint. Th
36
53
  * GET gets all the groups for the user
37
54
  * PUT adds a group
38
55
  * POST rewrites the user's groups
39
- * DELETE deletes the matching group
56
+ * DELETE deletes the matching group
40
57
 
41
58
  The field needs to be named `group`. You can even have an array of groups, eg. `group[0]`, `group[1]` etc.
42
59
 
43
60
  ***Example***
44
61
 
45
- Note that you'll need to authenticate as an admin through one of the methods described for these examples
62
+ Note that you'll need to authenticate as an admin through one of the methods described in [Authentication](authentication.md).
46
63
 
47
64
  Set the user's group to `test`
48
65
 
49
66
  ```
50
- curl -X POST -F "group=test" "http://localhost:3001/groups/5485bd62fbad8791660d2658"
67
+ curl -X POST -F "group=test" "http://localhost:4001/groups/5485bd62fbad8791660d2658"
51
68
  ```
52
69
 
53
- Add the groups `test1` and `test2`
70
+ Add the groups `test0` and `test1`
54
71
 
55
72
  ```
56
- curl -X PUT -F "group[0]=test0" -F "group[1]=test1" "http://localhost:3001/groups/5485bd62fbad8791660d2658"
73
+ curl -X PUT -F "group[0]=test0" -F "group[1]=test1" "http://localhost:4001/groups/5485bd62fbad8791660d2658"
57
74
  ```
58
75
 
59
76
  ### Adding custom permission logic
60
77
 
61
- Maybe you want to do some more checks on permissions than the "crud" we offer. You can catch
62
- the user object in your model as a virtual attribute. (I suppose you could use a real Mixed attribute too.)
63
-
64
- Eg.
78
+ Maybe you want to do more checks on permissions than the built-in `crud` strings. You can capture the user object in your model as a virtual attribute (JXP sets `__user` on documents during API operations):
65
79
 
66
80
  ```js
67
81
  var sender;
@@ -71,10 +85,10 @@ LedgerSchema.virtual("__user").set(function(usr) {
71
85
  });
72
86
  ```
73
87
 
74
- And then later, say in your pre- or post-save...
88
+ And then later, say in your pre- or post-save:
75
89
 
76
90
  ```js
77
- (!sender.admin)) {
78
- return next(new Error( "Verboten!"));
91
+ if (!sender.admin) {
92
+ return next(new Error("Verboten!"));
79
93
  }
80
- ```
94
+ ```
package/docs/queries.md CHANGED
@@ -2,24 +2,28 @@
2
2
 
3
3
  You can post complex queries through the `/query/<modelname>` endpoint, such as doing `$and` or `$or` queries, or regex expressions.
4
4
 
5
- Wrap it all in a "query" variable and POST it as JSON.
5
+ Dangerous operators (e.g. `$where`, `$function`) are rejected. Per-model opt-out: `advanced_queries: { query: false }` in schema options.
6
+
7
+ Wrap your MongoDB filter in a `"query"` object and POST it as JSON. URL query parameters for `GET /api/<model>` also work here: `limit`, `page`, `sort`, `populate`, `filter`, `autopopulate`, and `fields`. See [The Restful API](api.md) for those features.
6
8
 
7
9
  Eg.
8
10
 
9
- ```JSON
11
+ ```json
10
12
  {
11
13
  "query": {
12
14
  "$and": [
13
- {
15
+ {
14
16
  "foo": {
15
17
  "$regex": "foo",
16
18
  "$options": "i"
17
19
  }
18
20
  },
19
- {
21
+ {
20
22
  "bar": "Bar"
21
23
  }
22
24
  ]
23
25
  }
24
26
  }
25
- ```
27
+ ```
28
+
29
+ Note: the top-level `search` query parameter does not apply to `/query` — express search conditions inside the posted `query` object instead.
package/docs/schemas.md CHANGED
@@ -10,9 +10,9 @@ The schemas are based on Mongoose, so anything you can do with a [Mongoose schem
10
10
 
11
11
  ## Defining a schema
12
12
 
13
- Schemas are all found in the `/models` directory, and have the format `/models/<name>_model.js`. Each schema represents a collection in Mongo, with the Mongo collection name being the plural of the schema name. Eg. the data for `user_model.js` is stored in the `users` collection in Mongo.
13
+ Schemas are loaded from `MODEL_DIR` (default `./dist/models` on the sample server). Each file is named `<name>_model.js`. Each schema represents a collection in Mongo, with the Mongo collection name being the plural of the schema name. Eg. the data for `user_model.js` is stored in the `users` collection in Mongo.
14
14
 
15
- A typical schema looks like `/models/test_model.js`:
15
+ A typical schema looks like `test_model.js` in your model directory:
16
16
  ```javascript
17
17
  /* global JXPSchema ObjectId Mixed */
18
18
 
@@ -108,6 +108,12 @@ _owner_id: ObjectId
108
108
 
109
109
  This is a link to the `user` that originally created the document. This is used for permissioning the `owner` rights.
110
110
 
111
+ ```javascript
112
+ _updated_by_id: ObjectId
113
+ ```
114
+
115
+ Set on PUT to the user who last updated the document (populated as `_updated_by` when using `map_to`).
116
+
111
117
  ```javascript
112
118
  createdAt: Date,
113
119
  updatedAt: Date
@@ -116,25 +122,28 @@ updatedAt: Date
116
122
  We always store the created and updated timestamp.
117
123
 
118
124
  ```javascript
119
- _v: Number
125
+ __v: Number
120
126
  ```
121
127
 
122
- The version is tracked.
128
+ Mongoose version key.
123
129
 
124
130
  ## Stored Procedures
125
131
 
126
- You can write your own stored procs in your schemas to do whatever you want. You can access these through the `/call` endpoint. Declare these as a static Mongoose function.
127
-
128
- `/call/test/test`
132
+ You can expose selected static methods via the `/call` endpoint. Declare the static on the schema and list its name in **`callable_statics`** (only listed methods are callable; all others return 403).
129
133
 
130
134
  ```js
135
+ const TestSchema = new JXPSchema({ ... }, {
136
+ perms: { ... },
137
+ callable_statics: ["test"],
138
+ });
139
+
131
140
  TestSchema.statics.test = function(data) {
132
141
  console.log(data);
133
142
  return "Testing OKAY!";
134
143
  };
135
144
  ```
136
145
 
137
- You can GET your static, or POST data to it.
146
+ `/call/test/test` GET or POST. Unlisted statics are rejected.
138
147
 
139
148
  You can even prepopulate the function with an item:
140
149
 
@@ -147,8 +156,10 @@ TestSchema.statics.testItem = function(item, data) {
147
156
  };
148
157
  ```
149
158
 
150
- The static call will include the user's data as `data.__user`. [NOTE: This will probably change to "sender" before v2 is finalised.]
159
+ The static call will include the authenticated user on `data.__user` when a user is logged in.
160
+
161
+ You can also set permissions after construction with `TestSchema.set("_perms", { ... })`, but the usual pattern is the `perms` option in the constructor (see above).
151
162
 
152
- ## Pre- and post-fuctions
163
+ ## Pre- and post-functions
153
164
 
154
165
  You can use [Mongoose pre- and post-middleware](https://mongoosejs.com/docs/middleware.html#post) to do advanced validation or to return a calculated field with your results.
package/docs/special.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Special Features
2
2
 
3
- ## We allways encrypt passwords
3
+ ## We always encrypt passwords
4
4
 
5
- You should note that if you send through anything called "password", it will automagically encrypt using bcrypt, unless you send the parameter `password_override=1`.
5
+ If you send anything called `password`, it is encrypted with bcrypt automatically. The query parameter `password_override=1` skips hashing but **requires an admin user** (for setting a pre-computed hash).
6
6
 
7
7
  ## Reflection/navel gazing
8
8
 
@@ -12,7 +12,7 @@ The endpoint `/model/modelname` gives us a description of a model.
12
12
 
13
13
  ## Cache
14
14
 
15
- The `/cache` endpoint should show us some info about the state of our Memcached server.
15
+ When caching is enabled, `GET /cache/stats` returns in-process cache statistics (see [Caching](caching.md)). Use `GET /cache/clear` to flush the cache.
16
16
 
17
17
  ## Downloading data as CSV
18
18
 
@@ -30,4 +30,4 @@ You can permanently delete a document by passing the parameter `?_permaDelete=1`
30
30
 
31
31
  You can delete all of the rows that reference a row you're deleting by adding `?_cascade=1` to your endpoint.
32
32
 
33
- You can combine permanent and cascade deleting.
33
+ You can combine permanent and cascade deleting.
@@ -0,0 +1,86 @@
1
+ # TypeScript (v4)
2
+
3
+ JXP 4 is written in TypeScript and published as compiled JavaScript plus `.d.ts` declaration files.
4
+
5
+ ## Building from source
6
+
7
+ ```bash
8
+ git clone https://github.com/WorkSpaceMan/jxp.git
9
+ cd jxp
10
+ npm install --legacy-peer-deps
11
+ npm run build # compiles src/ → dist/
12
+ npm test # build + mocha (requires MongoDB for integration tests)
13
+ npm start # runs dist/bin/server.js
14
+ ```
15
+
16
+ During development:
17
+
18
+ ```bash
19
+ npm run dev # rebuild on src/ changes and restart the sample server
20
+ ```
21
+
22
+ ## Consuming JXP from JavaScript
23
+
24
+ No changes are required for existing `*_model.js` apps:
25
+
26
+ ```javascript
27
+ /* global JXPSchema ObjectId Mixed */
28
+ const MySchema = new JXPSchema({ name: String }, { perms: { admin: "crud", all: "r" } });
29
+ module.exports = JXPSchema.model("MyModel", MySchema);
30
+ ```
31
+
32
+ `require("jxp")` resolves to the compiled entry at `dist/libs/jxp.js`.
33
+
34
+ Deep imports remain supported, e.g. `require("jxp/libs/query_manipulation")`.
35
+
36
+ ## Editor types for JS models
37
+
38
+ Add a dev dependency on `jxp@^4` and reference globals in model files:
39
+
40
+ ```javascript
41
+ /// <reference types="jxp/globals" />
42
+ /* global JXPSchema ObjectId Mixed */
43
+ ```
44
+
45
+ ## Authoring models in TypeScript
46
+
47
+ ```typescript
48
+ import JXPSchema from "jxp/libs/schema"; // or relative path when in-repo
49
+ import type { Types } from "mongoose";
50
+
51
+ export interface IWidget {
52
+ _id?: Types.ObjectId;
53
+ name?: string;
54
+ }
55
+
56
+ const WidgetSchema = new JXPSchema(
57
+ { name: String },
58
+ { perms: { admin: "crud", all: "r" } }
59
+ );
60
+
61
+ const Widget = JXPSchema.model<IWidget>("Widget", WidgetSchema);
62
+ export default Widget;
63
+ ```
64
+
65
+ Compile app models to `*_model.js` in your `model_dir`, or extend the loader in a future release to accept `_model.ts` directly.
66
+
67
+ ## Built-in document types
68
+
69
+ JXP ships typed built-in models (import from your compiled `dist/models` path when developing JXP itself):
70
+
71
+ | Model | Interface |
72
+ |-------|-----------|
73
+ | User | `IUser` |
74
+ | Token | `IToken` |
75
+ | RefreshToken | `IRefreshToken` |
76
+ | APIKey | `IAPIKey` |
77
+ | Usergroup | `IUserGroup` |
78
+ | Link | `ILink` |
79
+ | Test | `ITest` |
80
+
81
+ ## Breaking changes in v4
82
+
83
+ - **Published package** — npm installs ship prebuilt `dist/` (not `src/`). Building from source (`npm run build`) is only required when developing from a git clone.
84
+ - **Entry point** — `main` is `dist/libs/jxp.js`; `types` points to matching `.d.ts`.
85
+ - **Node** — requires Node.js 22+.
86
+ - **Relative `model_dir`** — resolved from `process.cwd()` (typical for npm scripts), not from the server script path.
package/docs/websocket.md CHANGED
@@ -1,3 +1,101 @@
1
1
  # Websockets
2
2
 
3
- Documentation coming soon...
3
+ JXP exposes a WebSocket server on the same HTTP port as the REST API (upgrade on the shared Restify server). Clients receive real-time notifications when documents are created, updated, or deleted, subject to the same permission checks as the REST API.
4
+
5
+ ## Connecting
6
+
7
+ Connect to the WebSocket URL for your server (same host and port as the API, `ws://` or `wss://` scheme).
8
+
9
+ ## Message format
10
+
11
+ Send JSON messages with:
12
+
13
+ | Field | Description |
14
+ |-------|-------------|
15
+ | `action` | One of `ping`, `auth`, `subscribe`, `unsubscribe` |
16
+ | `msg_id` | Client-chosen id echoed in the response (required) |
17
+ | `data` | Action-specific payload (optional object) |
18
+
19
+ Responses include `status` (`okay` or `error`), `msg_id`, `client_id`, and optionally `result` or `message`.
20
+
21
+ ### ping
22
+
23
+ Request:
24
+
25
+ ```json
26
+ { "action": "ping", "msg_id": "1", "data": {} }
27
+ ```
28
+
29
+ Response `result`: `"pong!"`
30
+
31
+ ### auth
32
+
33
+ Authenticate before subscribing. Provide one of:
34
+
35
+ - `data.email` and `data.password`
36
+ - `data.apikey`
37
+ - `data.token` (Bearer access token)
38
+
39
+ Request:
40
+
41
+ ```json
42
+ {
43
+ "action": "auth",
44
+ "msg_id": "2",
45
+ "data": { "email": "user@example.com", "password": "secret" }
46
+ }
47
+ ```
48
+
49
+ ### subscribe
50
+
51
+ Subscribe to changes on a model. Requires prior `auth`.
52
+
53
+ **Collection-wide** (post, put, delete on any document):
54
+
55
+ ```json
56
+ {
57
+ "action": "subscribe",
58
+ "msg_id": "3",
59
+ "data": { "model": "test" }
60
+ }
61
+ ```
62
+
63
+ **Single document** (put and delete on one id):
64
+
65
+ ```json
66
+ {
67
+ "action": "subscribe",
68
+ "msg_id": "4",
69
+ "data": { "model": "test", "id": "5eb7cf838c9fba641e0e9dcb" }
70
+ }
71
+ ```
72
+
73
+ Optional `data.filter` limits which events are delivered (field values must match on the changed document).
74
+
75
+ ### unsubscribe
76
+
77
+ Same `data` shape as `subscribe` (`model`, optional `id`).
78
+
79
+ ## Events pushed to the client
80
+
81
+ When a subscribed change occurs, the server sends JSON with a `data` object such as:
82
+
83
+ ```json
84
+ {
85
+ "client_id": "<ws client id>",
86
+ "data": {
87
+ "event": "put",
88
+ "modelname": "test",
89
+ "item": { ... },
90
+ "user_id": "...",
91
+ "user_name": "...",
92
+ "user_email": "..."
93
+ }
94
+ }
95
+ ```
96
+
97
+ `event` is one of `post`, `put`, or `del`.
98
+
99
+ ## Hooks
100
+
101
+ Server `pre_hooks` on `post`, `put`, and `delete` run in the same process as the WebSocket emitter. Use hooks to enforce extra rules; subscribers only receive events the acting user was allowed to perform.
package/mkdocs.yml CHANGED
@@ -3,6 +3,7 @@ nav:
3
3
  - Home: index.md
4
4
  - Installation: installation.md
5
5
  - Configuration: configuration.md
6
+ - TypeScript: typescript.md
6
7
  - Authentication: authentication.md
7
8
  - Schemas: schemas.md
8
9
  - Restful API: api.md