@jskit-ai/users-core 0.1.172 → 0.1.173

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 (58) hide show
  1. package/package.json +41 -253
  2. package/patterns/user-administration-server/PATTERN.md +64 -0
  3. package/patterns/user-administration-server/example/packages/users/package.json +42 -0
  4. package/patterns/user-administration-server/example/packages/users/src/server/UsersFeature.js +13 -0
  5. package/patterns/user-administration-server/example/packages/users/src/shared/index.js +1 -0
  6. package/patterns/user-administration-server/example/packages/users-workspace/package.json +43 -0
  7. package/patterns/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +22 -0
  8. package/patterns/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +56 -0
  9. package/src/server/UsersExtensionsProvider.js +14 -0
  10. package/src/server/UsersFeature.js +119 -0
  11. package/src/server/UsersIdentityProvider.js +57 -0
  12. package/src/server/accountNotifications/accountNotificationsActions.js +17 -5
  13. package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +4 -6
  14. package/src/server/accountPreferences/accountPreferencesActions.js +17 -5
  15. package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +4 -6
  16. package/src/server/accountProfile/accountProfileActions.js +28 -14
  17. package/src/server/accountProfile/bootAccountProfileRoutes.js +11 -10
  18. package/src/server/accountSecurity/accountSecurityActions.js +27 -17
  19. package/src/server/accountSecurity/bootAccountSecurityRoutes.js +4 -7
  20. package/src/server/common/services/authProfileSyncService.js +11 -4
  21. package/src/server/common/support/realtimeServiceEvents.js +8 -11
  22. package/src/server/usersBootstrapContributor.js +4 -28
  23. package/src/server/usersExtensions.js +65 -0
  24. package/test/authProfileSyncService.test.js +2 -2
  25. package/test/exportsContract.test.js +6 -1
  26. package/test/featureRuntime.test.js +78 -0
  27. package/test/usersBootstrapContributor.test.js +1 -43
  28. package/test/usersPackageScaffoldContract.test.js +58 -204
  29. package/test/usersRouteRequestInputValidator.test.js +19 -43
  30. package/src/server/UsersCoreServiceProvider.js +0 -56
  31. package/src/server/accountNotifications/registerAccountNotifications.js +0 -37
  32. package/src/server/accountPreferences/registerAccountPreferences.js +0 -37
  33. package/src/server/accountProfile/registerAccountProfile.js +0 -56
  34. package/src/server/accountSecurity/registerAccountSecurity.js +0 -29
  35. package/src/server/common/registerCommonRepositories.js +0 -23
  36. package/src/server/common/registerSharedApi.js +0 -9
  37. package/src/server/profileSyncLifecycleContributorRegistry.js +0 -56
  38. package/src/server/registerUsersBootstrap.js +0 -20
  39. package/src/server/registerUsersCore.js +0 -22
  40. package/templates/packages/users/package.json +0 -82
  41. package/templates/packages/users/src/server/UsersProvider.js +0 -90
  42. package/templates/packages/users/src/server/actions.js +0 -68
  43. package/templates/packages/users/src/server/registerRoutes.js +0 -101
  44. package/templates/packages/users/src/server/repository.js +0 -51
  45. package/templates/packages/users/src/server/service.js +0 -32
  46. package/templates/packages/users-workspace/package.json +0 -83
  47. package/templates/packages/users-workspace/src/server/UsersProvider.js +0 -91
  48. package/templates/packages/users-workspace/src/server/actions.js +0 -77
  49. package/templates/packages/users-workspace/src/server/registerRoutes.js +0 -111
  50. package/test/providerLifecycle.test.js +0 -63
  51. package/test/registerCommonRepositories.test.js +0 -57
  52. package/test/registerServiceRealtimeEvents.test.js +0 -57
  53. package/test/registerUsersCore.test.js +0 -53
  54. /package/{templates/migrations → migrations}/users_core_generic_initial.cjs +0 -0
  55. /package/{templates/migrations → migrations}/users_core_profile_updated_at.cjs +0 -0
  56. /package/{templates/migrations → migrations}/users_core_profile_username.cjs +0 -0
  57. /package/{templates → patterns/user-administration-server/example}/packages/users/src/shared/userResource.js +0 -0
  58. /package/{templates/packages/users → patterns/user-administration-server/example/packages/users-workspace}/src/shared/index.js +0 -0
@@ -1,56 +0,0 @@
1
- import { registerTaggedSingleton, resolveTaggedEntries } from "@jskit-ai/kernel/server/registries";
2
-
3
- const PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG = "jskit.users.profileSync.lifecycleContributors";
4
-
5
- function normalizeProfileSyncLifecycleContributor(entry) {
6
- if (typeof entry === "function") {
7
- return Object.freeze({
8
- contributorId: String(entry.name || "anonymous"),
9
- order: 0,
10
- afterIdentityProfileSynced: entry
11
- });
12
- }
13
-
14
- if (!entry || typeof entry !== "object" || typeof entry.afterIdentityProfileSynced !== "function") {
15
- return null;
16
- }
17
-
18
- const contributorId = String(entry.contributorId || "anonymous");
19
- const order = Number.isFinite(entry.order) ? Number(entry.order) : 0;
20
-
21
- return Object.freeze({
22
- ...entry,
23
- contributorId,
24
- order,
25
- afterIdentityProfileSynced: entry.afterIdentityProfileSynced
26
- });
27
- }
28
-
29
- function registerProfileSyncLifecycleContributor(app, token, factory) {
30
- registerTaggedSingleton(app, token, factory, PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG, {
31
- context: "registerProfileSyncLifecycleContributor"
32
- });
33
- }
34
-
35
- function resolveProfileSyncLifecycleContributors(scope) {
36
- return resolveTaggedEntries(scope, PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG)
37
- .map((entry, index) => ({
38
- contributor: normalizeProfileSyncLifecycleContributor(entry),
39
- index
40
- }))
41
- .filter((entry) => Boolean(entry.contributor))
42
- .sort((left, right) => {
43
- if (left.contributor.order !== right.contributor.order) {
44
- return left.contributor.order - right.contributor.order;
45
- }
46
-
47
- return left.index - right.index;
48
- })
49
- .map((entry) => entry.contributor);
50
- }
51
-
52
- export {
53
- PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG,
54
- registerProfileSyncLifecycleContributor,
55
- resolveProfileSyncLifecycleContributors
56
- };
@@ -1,20 +0,0 @@
1
- import { registerBootstrapPayloadContributor } from "@jskit-ai/kernel/server/runtime";
2
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
3
- import { createUsersBootstrapContributor } from "./usersBootstrapContributor.js";
4
-
5
- function registerUsersBootstrap(app) {
6
- if (!app || typeof app.singleton !== "function") {
7
- throw new Error("registerUsersBootstrap requires application singleton().");
8
- }
9
-
10
- registerBootstrapPayloadContributor(app, "users.core.bootstrap.payloadContributor", (scope) => {
11
- return createUsersBootstrapContributor({
12
- userProfilesRepository: scope.make("internal.repository.user-profiles"),
13
- userSettingsRepository: scope.make("internal.repository.user-settings"),
14
- appConfig: resolveAppConfig(scope),
15
- authService: scope.make("authService")
16
- });
17
- });
18
- }
19
-
20
- export { registerUsersBootstrap };
@@ -1,22 +0,0 @@
1
- import { createService as createAuthProfileSyncService } from "./common/services/authProfileSyncService.js";
2
- import { resolveProfileSyncLifecycleContributors } from "./profileSyncLifecycleContributorRegistry.js";
3
-
4
- function registerUsersCore(app) {
5
- if (!app || typeof app.singleton !== "function" || typeof app.has !== "function") {
6
- throw new Error("registerUsersCore requires application singleton()/has().");
7
- }
8
-
9
- app.singleton("users.profile.sync.service", (scope) => {
10
- return createAuthProfileSyncService({
11
- userProfilesRepository: scope.make("internal.repository.user-profiles"),
12
- userSettingsRepository: scope.make("internal.repository.user-settings"),
13
- lifecycleContributors: resolveProfileSyncLifecycleContributors(scope)
14
- });
15
- });
16
-
17
- if (!app.has("auth.profile.projector")) {
18
- app.singleton("auth.profile.projector", (scope) => scope.make("users.profile.sync.service"));
19
- }
20
- }
21
-
22
- export { registerUsersCore };
@@ -1,82 +0,0 @@
1
- {
2
- "name": "@local/users",
3
- "version": "0.1.0",
4
- "private": true,
5
- "type": "module",
6
- "exports": {
7
- "./shared": "./src/shared/index.js"
8
- },
9
- "description": "App-local CRUD package (users).",
10
- "jskit": {
11
- "kind": "runtime",
12
- "capabilities": {
13
- "provides": [
14
- "crud.users"
15
- ],
16
- "requires": [
17
- "runtime.actions",
18
- "runtime.database",
19
- "auth.policy",
20
- "json-rest-api.core"
21
- ]
22
- },
23
- "runtime": {
24
- "server": {
25
- "providers": [
26
- {
27
- "entrypoint": "src/server/UsersProvider.js",
28
- "export": "UsersProvider"
29
- }
30
- ]
31
- }
32
- },
33
- "metadata": {
34
- "jskit": {
35
- "scaffoldShape": "users-core-crud-v1",
36
- "tableOwnership": {
37
- "tables": [
38
- {
39
- "tableName": "users",
40
- "provenance": "users-core-template",
41
- "ownerKind": "baseline-crud"
42
- }
43
- ]
44
- }
45
- },
46
- "apiSummary": {
47
- "surfaces": [
48
- {
49
- "subpath": "./shared",
50
- "summary": "App-local CRUD shared resource."
51
- }
52
- ],
53
- "containerTokens": {
54
- "server": [
55
- "repository.users",
56
- "crud.users"
57
- ]
58
- }
59
- }
60
- },
61
- "mutations": {
62
- "dependencies": {
63
- "runtime": {},
64
- "dev": {}
65
- },
66
- "packageJson": {
67
- "scripts": {}
68
- },
69
- "procfile": {},
70
- "files": []
71
- }
72
- },
73
- "dependencies": {
74
- "@jskit-ai/auth-core": "0.1.157",
75
- "@jskit-ai/crud-core": "0.1.170",
76
- "@jskit-ai/database-runtime": "0.1.159",
77
- "@jskit-ai/http-runtime": "0.1.157",
78
- "@jskit-ai/json-rest-api-core": "0.1.103",
79
- "@jskit-ai/resource-crud-core": "0.1.101",
80
- "@jskit-ai/users-core": "0.1.172"
81
- }
82
- }
@@ -1,90 +0,0 @@
1
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
2
- import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
3
- import {
4
- INTERNAL_JSON_REST_API,
5
- addResourceIfMissing,
6
- createJsonRestResourceScopeOptions
7
- } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
8
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
9
- import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
10
- import { createRepository } from "./repository.js";
11
- import { createService } from "./service.js";
12
- import { createActions } from "./actions.js";
13
- import { registerRoutes } from "./registerRoutes.js";
14
- import { resource } from "../shared/userResource.js";
15
-
16
- const CRUD_MODULE_CONFIG = Object.freeze({
17
- namespace: "users",
18
- surface: "home",
19
- ownershipFilter: "public",
20
- relativePath: "/users"
21
- });
22
-
23
- function resolveCrudPolicyFromApp(app) {
24
- return resolveCrudSurfacePolicyFromAppConfig(CRUD_MODULE_CONFIG, resolveAppConfig(app), {
25
- context: "UsersProvider"
26
- });
27
- }
28
-
29
- class UsersProvider {
30
- static id = "crud.users";
31
-
32
- static startsAfter = ["json-rest-api.core", "local.main", "runtime.actions"];
33
-
34
- register(app) {
35
- const crudPolicy = resolveCrudPolicyFromApp(app);
36
-
37
- app.singleton("repository.users", (scope) => {
38
- const api = scope.make(INTERNAL_JSON_REST_API);
39
- const knex = scope.make("jskit.database.knex");
40
- return createRepository({
41
- api,
42
- knex
43
- });
44
- });
45
-
46
- app.service(
47
- "crud.users",
48
- (scope) => {
49
- return createService({
50
- usersRepository: scope.make("repository.users")
51
- });
52
- }
53
- );
54
-
55
- app.actions(
56
- withActionDefaults(
57
- createActions({
58
- surface: crudPolicy.surfaceId
59
- }),
60
- {
61
- domain: "crud",
62
- dependencies: {
63
- usersService: "crud.users"
64
- }
65
- }
66
- )
67
- );
68
- }
69
-
70
- async boot(app) {
71
- const crudPolicy = resolveCrudPolicyFromApp(app);
72
- const api = app.make(INTERNAL_JSON_REST_API);
73
- await addResourceIfMissing(
74
- api,
75
- "users",
76
- createJsonRestResourceScopeOptions(resource, {
77
- writeSerializers: {
78
- "datetime-utc": toDatabaseDateTimeUtc
79
- }
80
- })
81
- );
82
- registerRoutes(app, {
83
- routeOwnershipFilter: crudPolicy.ownershipFilter,
84
- routeSurface: crudPolicy.surfaceId,
85
- routeRelativePath: crudPolicy.relativePath
86
- });
87
- }
88
- }
89
-
90
- export { UsersProvider };
@@ -1,68 +0,0 @@
1
- import {
2
- composeSchemaDefinitions,
3
- recordIdParamsValidator
4
- } from "@jskit-ai/kernel/shared/validators";
5
- import {
6
- createStandardCrudListQueryValidators,
7
- createStandardCrudViewQueryValidators
8
- } from "@jskit-ai/crud-core/server/listQueryValidators";
9
- import { resource } from "../shared/userResource.js";
10
-
11
- const authenticatedPermission = Object.freeze({
12
- require: "authenticated"
13
- });
14
-
15
- function createActions({ surface } = {}) {
16
- return Object.freeze([
17
- {
18
- id: "crud.users.list",
19
- version: 1,
20
- kind: "query",
21
- channels: ["api", "automation", "internal"],
22
- surfaces: [surface],
23
- permission: authenticatedPermission,
24
- input: composeSchemaDefinitions([
25
- ...createStandardCrudListQueryValidators({ resource })
26
- ]),
27
- output: null,
28
- idempotency: "none",
29
- audit: {
30
- actionName: "crud.users.list"
31
- },
32
- observability: {},
33
- async execute(input, context, deps) {
34
- return deps.usersService.queryDocuments(input || {}, {
35
- context,
36
- visibilityContext: context?.visibilityContext
37
- });
38
- }
39
- },
40
- {
41
- id: "crud.users.view",
42
- version: 1,
43
- kind: "query",
44
- channels: ["api", "automation", "internal"],
45
- surfaces: [surface],
46
- permission: authenticatedPermission,
47
- input: composeSchemaDefinitions([
48
- recordIdParamsValidator,
49
- ...createStandardCrudViewQueryValidators()
50
- ]),
51
- output: null,
52
- idempotency: "none",
53
- audit: {
54
- actionName: "crud.users.view"
55
- },
56
- observability: {},
57
- async execute(input, context, deps) {
58
- const { recordId, ...query } = input || {};
59
- return deps.usersService.getDocumentById(recordId, query, {
60
- context,
61
- visibilityContext: context?.visibilityContext
62
- });
63
- }
64
- }
65
- ]);
66
- }
67
-
68
- export { createActions };
@@ -1,101 +0,0 @@
1
- import { createJsonApiResourceRouteContract } from "@jskit-ai/http-runtime/shared/validators/jsonApiRouteTransport";
2
- import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
3
- import {
4
- createCrudCursorPaginationQueryValidator,
5
- listSearchQueryValidator
6
- } from "@jskit-ai/crud-core/server/listQueryValidators";
7
- import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
8
- import { checkRouteVisibility } from "@jskit-ai/kernel/shared/support/visibility";
9
- import {
10
- composeSchemaDefinitions,
11
- recordIdParamsValidator
12
- } from "@jskit-ai/kernel/shared/validators";
13
- import { resource } from "../shared/userResource.js";
14
-
15
- const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator({
16
- orderBy: resource.defaultSort
17
- });
18
- const listRouteQueryValidator = composeSchemaDefinitions([
19
- listCursorPaginationQueryValidator,
20
- listSearchQueryValidator
21
- ]);
22
- const RESOURCE_ROUTE_CONTRACT_TYPE = resource.namespace;
23
- const listRouteContract = createJsonApiResourceRouteContract({
24
- type: RESOURCE_ROUTE_CONTRACT_TYPE,
25
- query: listRouteQueryValidator,
26
- output: resource.operations.view.output,
27
- outputKind: "collection"
28
- });
29
- const viewRouteContract = createJsonApiResourceRouteContract({
30
- type: RESOURCE_ROUTE_CONTRACT_TYPE,
31
- output: resource.operations.view.output,
32
- outputKind: "record"
33
- });
34
-
35
- function registerRoutes(
36
- app,
37
- {
38
- routeOwnershipFilter = "public",
39
- routeSurface = "",
40
- routeRelativePath = ""
41
- } = {}
42
- ) {
43
- const router = app.make("jskit.http.router");
44
- const normalizedRouteSurface = normalizeSurfaceId(routeSurface);
45
- const routeBase = resolveScopedApiBasePath({
46
- routeBase: "/",
47
- relativePath: routeRelativePath,
48
- strictParams: false
49
- });
50
-
51
- router.register(
52
- "GET",
53
- routeBase,
54
- {
55
- auth: "required",
56
- surface: normalizedRouteSurface,
57
- visibility: checkRouteVisibility(routeOwnershipFilter),
58
- meta: {
59
- tags: ["crud"],
60
- summary: "List users."
61
- },
62
- ...listRouteContract
63
- },
64
- async function (request, reply) {
65
- const response = await request.executeAction({
66
- actionId: "crud.users.list",
67
- input: {
68
- ...(request.input.query || {})
69
- }
70
- });
71
- reply.code(200).send(response);
72
- }
73
- );
74
-
75
- router.register(
76
- "GET",
77
- `${routeBase}/:recordId`,
78
- {
79
- auth: "required",
80
- surface: normalizedRouteSurface,
81
- visibility: checkRouteVisibility(routeOwnershipFilter),
82
- meta: {
83
- tags: ["crud"],
84
- summary: "View a user."
85
- },
86
- ...viewRouteContract,
87
- params: recordIdParamsValidator
88
- },
89
- async function (request, reply) {
90
- const response = await request.executeAction({
91
- actionId: "crud.users.view",
92
- input: {
93
- recordId: request.input.params.recordId
94
- }
95
- });
96
- reply.code(200).send(response);
97
- }
98
- );
99
- }
100
-
101
- export { registerRoutes };
@@ -1,51 +0,0 @@
1
- import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
2
- import {
3
- buildJsonRestQueryParams,
4
- createJsonRestContext,
5
- returnBadRequestWhenJsonRestFieldsetInvalid,
6
- returnNullWhenJsonRestResourceMissing
7
- } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
8
- import { resource } from "../shared/userResource.js";
9
-
10
- const RESOURCE_TYPE = resource.namespace;
11
-
12
- function createRepository({ api, knex } = {}) {
13
- const withTransaction = createWithTransaction(knex);
14
-
15
- async function queryDocuments(query = {}, options = {}) {
16
- return returnBadRequestWhenJsonRestFieldsetInvalid(() =>
17
- api.resources.users.query(
18
- {
19
- queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, query),
20
- transaction: options?.trx || null,
21
- simplified: false
22
- },
23
- createJsonRestContext(options?.context || null)
24
- )
25
- );
26
- }
27
-
28
- async function getDocumentById(recordId, query = {}, options = {}) {
29
- return returnBadRequestWhenJsonRestFieldsetInvalid(() =>
30
- returnNullWhenJsonRestResourceMissing(() =>
31
- api.resources.users.get(
32
- {
33
- id: recordId,
34
- queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, query),
35
- transaction: options?.trx || null,
36
- simplified: false
37
- },
38
- createJsonRestContext(options?.context || null)
39
- )
40
- )
41
- );
42
- }
43
-
44
- return Object.freeze({
45
- withTransaction,
46
- queryDocuments,
47
- getDocumentById
48
- });
49
- }
50
-
51
- export { createRepository };
@@ -1,32 +0,0 @@
1
- import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
2
- import { returnJsonApiDocument } from "@jskit-ai/http-runtime/shared";
3
-
4
- function return404IfNotFound(document = null) {
5
- if (!document) {
6
- throw new AppError(404, "Document not found.");
7
- }
8
- return document;
9
- }
10
-
11
- function createService({ usersRepository } = {}) {
12
- if (!usersRepository) {
13
- throw new TypeError("createService requires usersRepository.");
14
- }
15
-
16
- return Object.freeze({
17
- async queryDocuments(query = {}, options = {}) {
18
- return returnJsonApiDocument(await usersRepository.queryDocuments(query, {
19
- trx: options?.trx || null,
20
- context: options?.context || null
21
- }));
22
- },
23
- async getDocumentById(recordId, query = {}, options = {}) {
24
- return returnJsonApiDocument(return404IfNotFound(await usersRepository.getDocumentById(recordId, query, {
25
- trx: options?.trx || null,
26
- context: options?.context || null
27
- })));
28
- }
29
- });
30
- }
31
-
32
- export { createService };
@@ -1,83 +0,0 @@
1
- {
2
- "name": "@local/users",
3
- "version": "0.1.0",
4
- "private": true,
5
- "type": "module",
6
- "exports": {
7
- "./shared": "./src/shared/index.js"
8
- },
9
- "description": "App-local CRUD package (users).",
10
- "jskit": {
11
- "kind": "runtime",
12
- "capabilities": {
13
- "provides": [
14
- "crud.users"
15
- ],
16
- "requires": [
17
- "runtime.actions",
18
- "runtime.database",
19
- "auth.policy",
20
- "json-rest-api.core"
21
- ]
22
- },
23
- "runtime": {
24
- "server": {
25
- "providers": [
26
- {
27
- "entrypoint": "src/server/UsersProvider.js",
28
- "export": "UsersProvider"
29
- }
30
- ]
31
- }
32
- },
33
- "metadata": {
34
- "jskit": {
35
- "scaffoldShape": "users-core-crud-v1",
36
- "tableOwnership": {
37
- "tables": [
38
- {
39
- "tableName": "users",
40
- "provenance": "users-core-template",
41
- "ownerKind": "baseline-crud"
42
- }
43
- ]
44
- }
45
- },
46
- "apiSummary": {
47
- "surfaces": [
48
- {
49
- "subpath": "./shared",
50
- "summary": "App-local CRUD shared resource."
51
- }
52
- ],
53
- "containerTokens": {
54
- "server": [
55
- "repository.users",
56
- "crud.users"
57
- ]
58
- }
59
- }
60
- },
61
- "mutations": {
62
- "dependencies": {
63
- "runtime": {},
64
- "dev": {}
65
- },
66
- "packageJson": {
67
- "scripts": {}
68
- },
69
- "procfile": {},
70
- "files": []
71
- }
72
- },
73
- "dependencies": {
74
- "@jskit-ai/auth-core": "0.1.157",
75
- "@jskit-ai/crud-core": "0.1.170",
76
- "@jskit-ai/database-runtime": "0.1.159",
77
- "@jskit-ai/http-runtime": "0.1.157",
78
- "@jskit-ai/json-rest-api-core": "0.1.103",
79
- "@jskit-ai/resource-crud-core": "0.1.101",
80
- "@jskit-ai/users-core": "0.1.172",
81
- "@jskit-ai/workspaces-core": "0.1.137"
82
- }
83
- }