@lowdefy/api 0.0.0-experimental-20260827092002 → 0.0.0-experimental-20260827092153

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 (118) hide show
  1. package/dist/context/applySystemTrust.js +33 -0
  2. package/dist/context/createApiContext.js +18 -3
  3. package/dist/context/createAuthorizeOutcome.js +91 -0
  4. package/dist/context/createEvaluateOperators.js +2 -1
  5. package/dist/context/createSystemContext.js +78 -0
  6. package/dist/context/normalizeInjectedCaller.js +50 -0
  7. package/dist/context/resolveAuthentication.js +468 -0
  8. package/dist/context/resolveStrategyCaller.js +60 -0
  9. package/dist/email/renderAuthEmail.js +98 -0
  10. package/dist/index.js +17 -4
  11. package/dist/response/buildEndpointResult.js +33 -0
  12. package/dist/response/normalizeErrorSources.js +64 -0
  13. package/dist/response/omitErrorProps.js +51 -0
  14. package/dist/response/redactErrorResponse.js +30 -0
  15. package/dist/response/redactResponse.js +31 -0
  16. package/dist/routes/agent/prepareAgent.js +7 -11
  17. package/dist/routes/auth/buildAdminPlugin.js +33 -0
  18. package/dist/routes/auth/buildCaptchaPlugin.js +61 -0
  19. package/dist/routes/auth/buildPhoneNumberPlugin.js +57 -0
  20. package/dist/routes/auth/buildProviders.js +51 -0
  21. package/dist/routes/auth/createAsMetadataHandler.js +27 -0
  22. package/dist/routes/auth/createAuthLogger.js +31 -0
  23. package/dist/routes/auth/createSendEmail.js +49 -0
  24. package/dist/routes/auth/getAuthEnforcement.js +41 -0
  25. package/dist/routes/auth/getBetterAuth.js +87 -0
  26. package/dist/routes/auth/getBetterAuthConfig.js +600 -0
  27. package/dist/routes/auth/hooks/authHookPoints.js +204 -0
  28. package/dist/routes/auth/hooks/buildEngineHooks.js +68 -0
  29. package/dist/routes/auth/hooks/buildHooks.js +112 -0
  30. package/dist/routes/auth/{createAdapter.js → hooks/composeAfterSlot.js} +11 -9
  31. package/dist/routes/auth/hooks/composeBeforeSlot.js +41 -0
  32. package/dist/routes/auth/hooks/createHookDispatch.js +59 -0
  33. package/dist/routes/auth/hooks/createUserAfterHook.js +33 -0
  34. package/dist/routes/auth/hooks/createUserBeforeHook.js +47 -0
  35. package/dist/routes/auth/hooks/findHookUser.js +27 -0
  36. package/dist/routes/auth/{callbacks/createCallbackPlugins.js → modelNames.js} +15 -7
  37. package/dist/routes/auth/organizations/OrganizationKeyError.js +28 -0
  38. package/dist/routes/auth/organizations/authorizeRole.js +27 -0
  39. package/dist/routes/auth/organizations/buildOrganizationPlugin.js +181 -0
  40. package/dist/routes/auth/organizations/createAcceptActiveOrgGuardHook.js +57 -0
  41. package/dist/routes/auth/organizations/createActiveOrgPolicyHook.js +249 -0
  42. package/dist/routes/auth/organizations/createAdmissionGateHook.js +50 -0
  43. package/dist/routes/auth/organizations/createAfterAcceptInvitationHook.js +90 -0
  44. package/dist/routes/auth/organizations/createAutoJoinHook.js +68 -0
  45. package/dist/routes/auth/organizations/createMagicLinkSendGate.js +58 -0
  46. package/dist/routes/auth/organizations/ensureOrganization.js +141 -0
  47. package/dist/routes/auth/organizations/findPendingInvitation.js +43 -0
  48. package/dist/routes/auth/{callbacks/addUserFieldsToSession.js → organizations/getHookRequestHeaders.js} +9 -13
  49. package/dist/routes/auth/organizations/getOrganizationBinding.js +65 -0
  50. package/dist/routes/auth/organizations/isEmailAdmitted.js +89 -0
  51. package/dist/routes/auth/organizations/organizationAccessControl.js +94 -0
  52. package/dist/routes/auth/organizations/resolvePinnedOrganization.js +62 -0
  53. package/dist/routes/auth/requestHooks/beginTwoFactorChallenge.js +145 -0
  54. package/dist/routes/auth/requestHooks/buildRequestHooks.js +179 -0
  55. package/dist/routes/auth/requestHooks/createTwoFactorChallengeHook.js +59 -0
  56. package/dist/routes/auth/requestHooks/dispatchRequestHooks.js +45 -0
  57. package/dist/routes/auth/requestHooks/matchOAuthCallback.js +47 -0
  58. package/dist/routes/auth/requestHooks/redirectToChallenge.js +79 -0
  59. package/dist/routes/auth/requestHooks/resolveTwoFactorPageUrl.js +31 -0
  60. package/dist/routes/auth/resolveCookiePrefix.js +30 -0
  61. package/dist/routes/auth/sanitizeSessionResponse.js +27 -0
  62. package/dist/routes/auth/strategies/createAuthStrategies.js +77 -0
  63. package/dist/routes/auth/{events/createEventPlugins.js → strategies/getAuthStrategies.js} +16 -6
  64. package/dist/routes/connections/resolveTenantPreflight.js +172 -0
  65. package/dist/routes/endpoints/addStepResult.js +12 -2
  66. package/dist/routes/endpoints/authorizeApiEndpoint.js +18 -3
  67. package/dist/routes/endpoints/callEndpoint.js +13 -14
  68. package/dist/routes/endpoints/control/controlSetState.js +13 -2
  69. package/dist/routes/endpoints/getEndpointConfig.js +19 -3
  70. package/dist/routes/endpoints/handleAuthStep.js +204 -0
  71. package/dist/routes/endpoints/handleEndpointCall.js +14 -1
  72. package/dist/routes/endpoints/handleRenderNotification.js +1 -1
  73. package/dist/routes/endpoints/handleRequest.js +8 -1
  74. package/dist/routes/endpoints/resolveStepOrganizationId.js +64 -0
  75. package/dist/routes/endpoints/runDetachedEndpoint.js +37 -25
  76. package/dist/routes/endpoints/runRoutine.js +9 -1
  77. package/dist/routes/endpoints/runScheduledEndpoint.js +14 -20
  78. package/dist/routes/endpoints/runWebhookEndpoint.js +53 -26
  79. package/dist/routes/endpoints/runWebhookVerify.js +111 -0
  80. package/dist/routes/endpoints/test/runTest.js +3 -4
  81. package/dist/routes/mcp/createMcpServer.js +199 -0
  82. package/dist/routes/mcp/getMcpJwks.js +93 -0
  83. package/dist/routes/mcp/getMcpResourceBinding.js +41 -0
  84. package/dist/routes/mcp/getMcpUri.js +75 -0
  85. package/dist/routes/mcp/oauthResourceLifecycle.js +208 -0
  86. package/dist/routes/page/getPageConfig.js +55 -5
  87. package/dist/routes/request/authorizeRequest.js +20 -3
  88. package/dist/routes/request/callRequest.js +10 -2
  89. package/dist/routes/request/callRequestResolver.js +6 -2
  90. package/dist/routes/request/getRequestConfig.js +19 -3
  91. package/dist/routes/request/resolveTenant.js +132 -0
  92. package/dist/routes/rootConfig/menus/filterMenuList.js +5 -2
  93. package/dist/routes/websocket/authorizeWebsocket.js +12 -3
  94. package/dist/routes/websocket/createChannelRegistry.js +14 -5
  95. package/dist/routes/websocket/prepareChannel.js +13 -1
  96. package/dist/test/testContext.js +18 -8
  97. package/package.json +18 -9
  98. package/dist/context/createAuthorize.js +0 -45
  99. package/dist/routes/auth/callbacks/addUserFieldsToToken.js +0 -30
  100. package/dist/routes/auth/callbacks/createCallbacks.js +0 -46
  101. package/dist/routes/auth/callbacks/createJWTCallback.js +0 -77
  102. package/dist/routes/auth/callbacks/createRedirectCallback.js +0 -41
  103. package/dist/routes/auth/callbacks/createSessionCallback.js +0 -81
  104. package/dist/routes/auth/callbacks/createSignInCallback.js +0 -40
  105. package/dist/routes/auth/callbacks/validateSessionRoles.js +0 -33
  106. package/dist/routes/auth/createLogger.js +0 -35
  107. package/dist/routes/auth/createPrefixedCookies.js +0 -52
  108. package/dist/routes/auth/createProviders.js +0 -25
  109. package/dist/routes/auth/events/createCreateUserEvent.js +0 -41
  110. package/dist/routes/auth/events/createEvents.js +0 -56
  111. package/dist/routes/auth/events/createLinkAccountEvent.js +0 -43
  112. package/dist/routes/auth/events/createSessionEvent.js +0 -34
  113. package/dist/routes/auth/events/createSignInEvent.js +0 -46
  114. package/dist/routes/auth/events/createSignOutEvent.js +0 -43
  115. package/dist/routes/auth/events/createUpdateUserEvent.js +0 -41
  116. package/dist/routes/auth/getAuthConfig.js +0 -86
  117. package/dist/routes/auth/resolveCookies.js +0 -37
  118. /package/dist/{routes/notifications → email}/resolveThemeLogo.js +0 -0
@@ -0,0 +1,33 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import createAuthorizeOutcome from './createAuthorizeOutcome.js';
16
+ // The single writer of the trusted, caller-less system-context invariants
17
+ // (Decision 1). It sets all three together on an existing context so no caller
18
+ // can set one without the others:
19
+ // - user: null — caller-less (detectable via type.isNone)
20
+ // - system: true — the run-level trust marker every authorization layer reads
21
+ // - authorizeOutcome — derived from createAuthorizeOutcome so it reads context.system
22
+ // createSystemContext builds a FRESH context for the off-request hook path;
23
+ // this applies the same state to a request context a runner already holds
24
+ // (cron at construction, webhook once its verify gate passes, a detached run
25
+ // whose dispatcher was itself a system context). One greppable place means a
26
+ // future runner cannot reintroduce the silent-no-op bug by forgetting a line.
27
+ function applySystemTrust(context) {
28
+ context.user = null;
29
+ context.system = true;
30
+ context.authorizeOutcome = createAuthorizeOutcome(context);
31
+ return context;
32
+ }
33
+ export default applySystemTrust;
@@ -12,11 +12,14 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import createAuthorize from './createAuthorize.js';
15
+ */ import createAuthorizeOutcome from './createAuthorizeOutcome.js';
16
16
  import createReadConfigFile from './createReadConfigFile.js';
17
+ import getAuthEnforcement from '../routes/auth/getAuthEnforcement.js';
18
+ import getOrganizationBinding from '../routes/auth/organizations/getOrganizationBinding.js';
17
19
  import resolveLocale from './resolveLocale.js';
18
20
  function createApiContext(context) {
19
- context.user = context?.session?.user;
21
+ // context.user is written by resolveAuthentication (or a pre-resolved
22
+ // dev/e2e caller) before this runs - never assigned here.
20
23
  if (context.i18n?.defaultLocale) {
21
24
  const active = resolveLocale({
22
25
  i18n: context.i18n,
@@ -29,7 +32,19 @@ function createApiContext(context) {
29
32
  } else {
30
33
  context.i18n = undefined;
31
34
  }
32
- context.authorize = createAuthorize(context);
35
+ // The retained organizations state ({ policy, pinned }) - read by the
36
+ // _organization operator and the org-scoped admin steps; null when auth
37
+ // (or organizations) is not configured.
38
+ context.organization = getOrganizationBinding({
39
+ auth: context.auth ?? null
40
+ });
41
+ // The retained build-time authorization facts - the enrolment floor, the exempt
42
+ // enrolment page, and the unlisted-page-id default. null when auth is not
43
+ // configured, in which case nothing is protected and nothing is required.
44
+ context.authEnforcement = getAuthEnforcement({
45
+ auth: context.auth ?? null
46
+ });
47
+ context.authorizeOutcome = createAuthorizeOutcome(context);
33
48
  context.readConfigFile = createReadConfigFile(context);
34
49
  }
35
50
  export default createApiContext;
@@ -0,0 +1,91 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { type } from '@lowdefy/helpers';
16
+ import { ConfigError } from '@lowdefy/errors';
17
+ // createAuthorizeOutcome is called with the context object on every path
18
+ // (createApiContext, applySystemTrust, and the runners), so `system` is read
19
+ // straight off context.system - the single run-level trust marker every
20
+ // authorization layer reads.
21
+ //
22
+ // It returns an OUTCOME, not a boolean: 'allow', 'deny', or 'enrol_required'.
23
+ // The rename is load-bearing. Every previous caller was a truthiness test, and
24
+ // every non-empty string is truthy, so a caller left on `authorize` would read
25
+ // 'deny' as authorized and stop enforcing auth entirely on that surface. There
26
+ // is deliberately NO boolean authorize left beside this - a missed call site
27
+ // must throw TypeError, which also catches callers outside this repo.
28
+ //
29
+ // The ordering is security-critical and written here exactly once. Enrolment is
30
+ // checked LAST, after roles: checking it first hands an unenrolled caller an
31
+ // enrol redirect for every protected page that EXISTS and /404 for those that do
32
+ // not, which is a working page-enumeration oracle over pages they have no access
33
+ // to. Checked last, they see the enrol outcome only for pages they would have
34
+ // been let into - which tells them exactly what an enrolled caller learns by
35
+ // getting the page.
36
+ function createAuthorizeOutcome({ authEnforcement, system, user }) {
37
+ const authenticated = !type.isNone(user);
38
+ // A caller awaiting an organization is signed in and holds no membership -
39
+ // under tenant, the invited user before they accept (resolveAuthentication).
40
+ // They are known so the always-public accept page can address them, and
41
+ // refused everywhere auth.public is false, roles or not: empty roles is not a
42
+ // wall (user-model Decision 2), so authenticated alone must not admit them to
43
+ // a role-less protected page. Strategy callers (apiKey, jwt) also hold no
44
+ // organization but sit deliberately outside the membership boundary, so the
45
+ // test is this explicit marker, never the absence of an organization. The
46
+ // caller is the normalizeCaller-snaked record, so the key is snake_case here.
47
+ const awaitingOrganization = user?.awaiting_organization === true;
48
+ const roles = user?.roles ?? [];
49
+ if (!Array.isArray(roles) || roles.some((role)=>!type.isString(role))) {
50
+ throw new ConfigError('user.roles must be an array of strings.', {
51
+ received: roles
52
+ });
53
+ }
54
+ const enrolmentRequired = authEnforcement?.twoFactorRequired === true;
55
+ const enrolPageId = authEnforcement?.twoFactorEnrolPageId ?? null;
56
+ // pageId is passed only by the page route. Requests, endpoints, websockets and
57
+ // menu items pass none, so the enrolment-page exemption cannot be reached by a
58
+ // request id that happens to collide with the enrol page's id.
59
+ function authorizeOutcome(config, { pageId } = {}) {
60
+ // A system context has no session and no caller, so "does this caller's
61
+ // session carry the required roles?" is undefined, not denied - and it holds
62
+ // no factor either, so the enrolment floor is undefined for it too.
63
+ if (system === true) return 'allow';
64
+ const { auth } = config;
65
+ if (auth.public === true) return 'allow';
66
+ if (auth.public === false) {
67
+ if (!authenticated) return 'deny';
68
+ if (awaitingOrganization) return 'deny';
69
+ if (auth.roles && !auth.roles.some((role)=>roles.includes(role))) return 'deny';
70
+ // Enrolment last, after roles. Strictly === false: the key is present only
71
+ // on a caller resolved from a session, so API strategy callers and the
72
+ // dev/e2e injected callers pass untouched by inheritance rather than by a
73
+ // carve-out (Decision 10). `!user.two_factor_enrolled` would redirect every
74
+ // one of them.
75
+ if (enrolmentRequired && user.two_factor_enrolled === false) {
76
+ // The enrolment page itself is exempt from the enrolment check alone -
77
+ // it is a protected page and stays one; this is not a public-ness
78
+ // exemption (Decision 8).
79
+ if (!type.isNone(pageId) && pageId === enrolPageId) return 'allow';
80
+ return 'enrol_required';
81
+ }
82
+ return 'allow';
83
+ }
84
+ throw new ConfigError('auth.public must be true or false.', {
85
+ received: auth.public,
86
+ configKey: config['~k']
87
+ });
88
+ }
89
+ return authorizeOutcome;
90
+ }
91
+ export default createAuthorizeOutcome;
@@ -14,12 +14,13 @@
14
14
  limitations under the License.
15
15
  */ import { ServerParser } from '@lowdefy/operators';
16
16
  function createEvaluateOperators(context) {
17
- const { appMeta, i18n, jsMap, operators, secrets, user } = context;
17
+ const { appMeta, i18n, jsMap, operators, organization, secrets, user } = context;
18
18
  const operatorsParser = new ServerParser({
19
19
  i18n,
20
20
  jsMap,
21
21
  lowdefyApp: appMeta,
22
22
  operators,
23
+ organization,
23
24
  secrets,
24
25
  user
25
26
  });
@@ -0,0 +1,78 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import applySystemTrust from './applySystemTrust.js';
16
+ import createEvaluateOperators from './createEvaluateOperators.js';
17
+ import createReadConfigFile from './createReadConfigFile.js';
18
+ import getAuthEnforcement from '../routes/auth/getAuthEnforcement.js';
19
+ import getOrganizationBinding from '../routes/auth/organizations/getOrganizationBinding.js';
20
+ // Builds a fresh off-request context for a trusted, caller-less system context -
21
+ // an auth hook fire. The caller is the auth engine, not a user: `user` is null
22
+ // so the run is detectable as caller-less (`type.isNone(context.user)`) and
23
+ // `_user` resolves to nothing (the hook's subject is in `_payload`). The run is
24
+ // trusted at construction (`context.system = true`), the single trust marker
25
+ // every authorization layer reads (Decision 1); its `authorizeOutcome` is derived
26
+ // from createAuthorizeOutcome like every other layer, so endpoint auth is not
27
+ // re-checked against a session (Decision 2).
28
+ function createSystemContext({ agents, appMeta, auth, buildDirectory, config, configDirectory, connections, createHandleError, fileCache, i18n, jsMap, logger, notifications, operators, rid, secrets, steps, websockets }) {
29
+ const context = {
30
+ rid,
31
+ agents,
32
+ appMeta,
33
+ auth,
34
+ buildDirectory,
35
+ config,
36
+ configDirectory,
37
+ connections,
38
+ fileCache,
39
+ headers: {},
40
+ i18n: i18n?.defaultLocale ? {
41
+ ...i18n,
42
+ active: i18n.defaultLocale
43
+ } : undefined,
44
+ jsMap,
45
+ logger,
46
+ notifications,
47
+ operators,
48
+ req: {
49
+ url: 'system:auth-hook',
50
+ method: null,
51
+ hostname: null
52
+ },
53
+ secrets,
54
+ steps,
55
+ websockets
56
+ };
57
+ // Hook routines read the retained organizations state too - a step or
58
+ // _organization inside a hook-bound endpoint resolves the same pinned org.
59
+ context.organization = getOrganizationBinding({
60
+ auth: auth ?? null
61
+ });
62
+ // A system context bypasses the enrolment check outright (system === true
63
+ // returns early), so this exists for shape consistency, not a live read.
64
+ context.authEnforcement = getAuthEnforcement({
65
+ auth: auth ?? null
66
+ });
67
+ // Trusted, caller-less system context (Decisions 1, 2): user: null,
68
+ // system: true, and authorizeOutcome derived from createAuthorizeOutcome - the
69
+ // same invariant bundle the caller-less runners apply, set in one place.
70
+ applySystemTrust(context);
71
+ context.handleError = createHandleError({
72
+ context
73
+ });
74
+ context.readConfigFile = createReadConfigFile(context);
75
+ context.evaluateOperators = createEvaluateOperators(context);
76
+ return context;
77
+ }
78
+ export default createSystemContext;
@@ -0,0 +1,50 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { normalizeCaller, type } from '@lowdefy/helpers';
16
+ // The single writer of the pre-resolved-caller shape. A dev mock user, the dev
17
+ // headless renderer cookie, and the e2e test cookie each substitute for the
18
+ // whole resolveAuthentication step, and must match the shape it produces -
19
+ // including its snake_case keys, so an injected caller may be authored either
20
+ // way and still reads like a resolved one. That output always carries roles as
21
+ // an array and attributes as an object (a whole-bag { _user: 'attributes' }
22
+ // read gives {} on a real caller), so both are floored here. profile,
23
+ // contact_id, active_organization_id and two_factor_enrolled are only ever
24
+ // produced by a real user-row/member read, so they are left exactly as the
25
+ // caller carries them - absent stays absent, and a bare _user.profile read is
26
+ // null-safe.
27
+ // organization_id (the tenant wall's org value) mirrors resolveAuthentication,
28
+ // where it always equals active_organization_id - an injected caller declaring
29
+ // either key gets both, so mock/e2e callers pass walled connections without
30
+ // duplicating the org under two names. A caller with neither stays org-less
31
+ // and fails closed on walled connections, same as a strategy caller.
32
+ //
33
+ // The floors read the raw user: roles, attributes and profile are single words
34
+ // that snake to themselves, so an authored key and its normalized name are the
35
+ // same key. The org floor reads the normalized object, where either authored
36
+ // spelling has already collapsed onto one name.
37
+ function normalizeInjectedCaller(user) {
38
+ const normalized = {
39
+ ...normalizeCaller(user),
40
+ roles: user.roles ?? [],
41
+ attributes: user.attributes ?? {}
42
+ };
43
+ const organizationId = normalized.organization_id ?? normalized.active_organization_id;
44
+ if (!type.isNone(organizationId)) {
45
+ normalized.organization_id = organizationId;
46
+ normalized.active_organization_id = normalized.active_organization_id ?? organizationId;
47
+ }
48
+ return normalized;
49
+ }
50
+ export default normalizeInjectedCaller;