@lowdefy/client 6.0.0 → 7.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 (51) hide show
  1. package/dist/Client.js +54 -17
  2. package/dist/Context.js +30 -12
  3. package/dist/HtmlOverlay.js +127 -0
  4. package/dist/IconProvider.js +24 -0
  5. package/dist/MountEvents.js +3 -3
  6. package/dist/adapters/createRouter.js +9 -3
  7. package/dist/adapters/url.js +1 -7
  8. package/dist/auth/createAuthMethods.js +1034 -41
  9. package/dist/block/Block.js +6 -3
  10. package/dist/block/CategorySwitch.js +6 -0
  11. package/dist/block/Container.js +3 -0
  12. package/dist/block/InputContainer.js +8 -2
  13. package/dist/block/List.js +3 -0
  14. package/dist/block/LoadingBlock.js +6 -12
  15. package/dist/block/LoadingContainer.js +6 -1
  16. package/dist/block/LoadingList.js +6 -1
  17. package/dist/block/loadingBlockMethods.js +26 -0
  18. package/dist/claimCallerReload.js +38 -0
  19. package/dist/createAntdIconConfig.js +91 -0
  20. package/dist/createCallAPI.js +6 -2
  21. package/dist/createCallRequest.js +6 -2
  22. package/dist/createHandleError.js +21 -11
  23. package/dist/createIcon.js +138 -36
  24. package/dist/createPageLifecycleManager.js +172 -0
  25. package/dist/formatIconTitle.js +26 -0
  26. package/dist/getActiveLocale.js +27 -0
  27. package/dist/getContextKey.js +29 -0
  28. package/dist/getMessageIcon.js +43 -0
  29. package/dist/hasOtherRoles.js +30 -0
  30. package/dist/index.js +1 -0
  31. package/dist/initLowdefyContext.js +103 -4
  32. package/dist/isOtherCaller.js +29 -0
  33. package/dist/recordCallerStamp.js +31 -0
  34. package/dist/renderIconNodes.js +46 -0
  35. package/dist/request.js +74 -6
  36. package/dist/setLowdefyUser.js +32 -0
  37. package/dist/setupLink.js +19 -13
  38. package/dist/shouldReloadForAuthError.js +39 -0
  39. package/dist/shouldReloadForBuild.js +44 -0
  40. package/dist/shouldReloadForCaller.js +50 -0
  41. package/dist/storage/clearUserStorage.js +36 -0
  42. package/dist/storage/clearUserStorageOnOwnerChange.js +41 -0
  43. package/dist/storage/createStorage.js +48 -0
  44. package/dist/storage/listUserStorageKeys.js +36 -0
  45. package/dist/storage/readStorageItem.js +31 -0
  46. package/dist/storage/userStorageKey.js +24 -0
  47. package/dist/storage/userStoragePrefix.js +25 -0
  48. package/dist/useDarkMode.js +17 -4
  49. package/dist/useLocale.js +18 -5
  50. package/dist/websocket/createWebSocketClient.js +239 -100
  51. package/package.json +11 -10
@@ -0,0 +1,41 @@
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 clearUserStorage from './clearUserStorage.js';
16
+ import listUserStorageKeys from './listUserStorageKeys.js';
17
+ // The person signed in (or nobody) may not be the one who left data on this browser: another
18
+ // person signed in on it, or a session ended without Logout running. Their keys go before any
19
+ // block reads storage.
20
+ function clearUserStorageOnOwnerChange({ basePath, slug, userId, window }) {
21
+ let entries;
22
+ try {
23
+ entries = listUserStorageKeys({
24
+ basePath,
25
+ slug,
26
+ storage: window.localStorage
27
+ });
28
+ } catch {
29
+ // Storage the browser blocks holds nothing to clear.
30
+ return;
31
+ }
32
+ if (entries.some((entry)=>entry.userId !== userId)) {
33
+ clearUserStorage({
34
+ basePath,
35
+ keepUserId: userId,
36
+ slug,
37
+ window
38
+ });
39
+ }
40
+ }
41
+ export default clearUserStorageOnOwnerChange;
@@ -0,0 +1,48 @@
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 { appStorageKey } from '@lowdefy/helpers';
16
+ import clearUserStorage from './clearUserStorage.js';
17
+ import clearUserStorageOnOwnerChange from './clearUserStorageOnOwnerChange.js';
18
+ import userStorageKey from './userStorageKey.js';
19
+ // Storage keys for this app, reached by blocks and actions through their methods. getUserId is
20
+ // read on every call, so a user key always names the person signed in now.
21
+ function createStorage({ basePath, getUserId, slug, window }) {
22
+ return {
23
+ appKey: (name)=>appStorageKey({
24
+ basePath,
25
+ name,
26
+ slug
27
+ }),
28
+ clearUserStorage: ({ keepUserId })=>clearUserStorage({
29
+ basePath,
30
+ keepUserId,
31
+ slug,
32
+ window
33
+ }),
34
+ clearUserStorageOnOwnerChange: ()=>clearUserStorageOnOwnerChange({
35
+ basePath,
36
+ slug,
37
+ userId: getUserId(),
38
+ window
39
+ }),
40
+ userKey: (name)=>userStorageKey({
41
+ basePath,
42
+ name,
43
+ slug,
44
+ userId: getUserId()
45
+ })
46
+ };
47
+ }
48
+ export default createStorage;
@@ -0,0 +1,36 @@
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 userStoragePrefix from './userStoragePrefix.js';
16
+ // Every user key of this app in storage, with its owner's user id (null for an anonymous
17
+ // visitor). Keys are read before any is removed, since removing shifts storage indexes.
18
+ function listUserStorageKeys({ basePath, slug, storage }) {
19
+ const prefix = userStoragePrefix({
20
+ basePath,
21
+ slug
22
+ });
23
+ const entries = [];
24
+ for(let index = 0; index < storage.length; index += 1){
25
+ const key = storage.key(index);
26
+ if (key.startsWith(prefix)) {
27
+ const owner = key.slice(prefix.length, key.indexOf(':', prefix.length));
28
+ entries.push({
29
+ key,
30
+ userId: owner === '' ? null : decodeURIComponent(owner)
31
+ });
32
+ }
33
+ }
34
+ return entries;
35
+ }
36
+ export default listUserStorageKeys;
@@ -0,0 +1,31 @@
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
+ // Reads key, moving a value left under legacyKey (written before keys were kept per app) to it
17
+ // the first time. Returns null when neither holds a value or the browser blocks storage.
18
+ function readStorageItem({ key, legacyKey, storage }) {
19
+ try {
20
+ const value = storage.getItem(key);
21
+ if (!type.isNone(value)) return value;
22
+ const legacyValue = storage.getItem(legacyKey);
23
+ if (type.isNone(legacyValue)) return null;
24
+ storage.setItem(key, legacyValue);
25
+ storage.removeItem(legacyKey);
26
+ return legacyValue;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ export default readStorageItem;
@@ -0,0 +1,24 @@
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 userStoragePrefix from './userStoragePrefix.js';
16
+ // The user id is encoded so the owner always ends at the next ":", whatever the id holds. An
17
+ // anonymous visitor owns the empty id.
18
+ function userStorageKey({ basePath, name, slug, userId }) {
19
+ return `${userStoragePrefix({
20
+ basePath,
21
+ slug
22
+ })}${encodeURIComponent(userId ?? '')}:${name}`;
23
+ }
24
+ export default userStorageKey;
@@ -0,0 +1,25 @@
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 { appStorageKey } from '@lowdefy/helpers';
16
+ // A person's data sits under one name in the app's namespace, then the owner's user id. Keys set
17
+ // with SetLocalStorage can not start with "lowdefy_", so no app key reads as a user key.
18
+ function userStoragePrefix({ basePath, slug }) {
19
+ return appStorageKey({
20
+ basePath,
21
+ name: 'lowdefy_user:',
22
+ slug
23
+ });
24
+ }
25
+ export default userStoragePrefix;
@@ -14,6 +14,7 @@
14
14
  limitations under the License.
15
15
  */ import { useCallback, useEffect, useMemo, useState } from 'react';
16
16
  import { theme as antdTheme } from 'antd';
17
+ import readStorageItem from './storage/readStorageItem.js';
17
18
  const algorithmMap = {
18
19
  default: antdTheme.defaultAlgorithm,
19
20
  dark: antdTheme.darkAlgorithm,
@@ -67,11 +68,16 @@ function mergeComponents(shared, mode) {
67
68
  });
68
69
  return merged;
69
70
  }
70
- function useDarkMode({ antd, configDarkMode }) {
71
+ // storageKey is the app key the preference is kept under (see @lowdefy/helpers appStorageKey).
72
+ function useDarkMode({ antd, configDarkMode, storageKey }) {
71
73
  const baseAlgorithm = antd?.algorithm;
72
74
  const cleanAlgorithm = stripDarkFromAlgorithm(baseAlgorithm);
73
75
  const [userPreference, setUserPreference] = useState(()=>{
74
- return window.localStorage?.getItem('lowdefy_darkMode') ?? 'system';
76
+ return readStorageItem({
77
+ key: storageKey,
78
+ legacyKey: 'lowdefy_darkMode',
79
+ storage: window.localStorage
80
+ }) ?? 'system';
75
81
  });
76
82
  const [systemIsDark, setSystemIsDark] = useState(()=>{
77
83
  return window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false;
@@ -85,10 +91,17 @@ function useDarkMode({ antd, configDarkMode }) {
85
91
  return ()=>mql.removeEventListener('change', handler);
86
92
  }, []);
87
93
  const setPreference = useCallback((newPref)=>{
88
- window.localStorage?.setItem('lowdefy_darkMode', newPref);
94
+ window.localStorage?.setItem(storageKey, newPref);
95
+ // Set now, not on the next render: SetDarkMode updates the engine straight after, and
96
+ // _media: darkModePreference reads it then.
97
+ window.__lowdefy_darkModePreference = newPref;
89
98
  setUserPreference(newPref);
90
- }, []);
99
+ }, [
100
+ storageKey
101
+ ]);
91
102
  window.__lowdefy_setDarkMode = setPreference;
103
+ // SetDarkMode, _media and the header toggles read the preference here, not from storage.
104
+ window.__lowdefy_darkModePreference = userPreference;
92
105
  const isDark = resolveIsDark({
93
106
  configDarkMode,
94
107
  userPreference,
package/dist/useLocale.js CHANGED
@@ -14,6 +14,10 @@
14
14
  limitations under the License.
15
15
  */ import { useCallback, useEffect, useMemo, useState } from 'react';
16
16
  import dayjs from 'dayjs';
17
+ import antdEnUS from 'antd/locale/en_US.js';
18
+ import readStorageItem from './storage/readStorageItem.js';
19
+ // antd shows its en_US strings until an app locale loads, and with none configured.
20
+ const defaultAntdLocale = antdEnUS.default ?? antdEnUS;
17
21
  function pickBest(candidates, supported) {
18
22
  for (const candidate of candidates ?? []){
19
23
  if (supported.includes(candidate)) return candidate;
@@ -40,12 +44,17 @@ async function loadAntdXLocale({ active, antdXLocaleLoaders }) {
40
44
  const mod = await antdXLocaleLoaders[active]();
41
45
  return mod?.default ?? mod;
42
46
  }
43
- function useLocale({ i18n, antdLocaleLoaders, antdXLocaleLoaders, dayjsLocaleMap }) {
47
+ // storageKey is the app key the preference is kept under (see @lowdefy/helpers appStorageKey).
48
+ function useLocale({ i18n, antdLocaleLoaders, antdXLocaleLoaders, dayjsLocaleMap, storageKey }) {
44
49
  const supportedCodes = useMemo(()=>(i18n?.locales ?? []).map((l)=>l.code), [
45
50
  i18n?.locales
46
51
  ]);
47
52
  const [userPreference, setUserPreference] = useState(()=>{
48
- return window.localStorage?.getItem('lowdefy_locale') ?? 'auto';
53
+ return readStorageItem({
54
+ key: storageKey,
55
+ legacyKey: 'lowdefy_locale',
56
+ storage: window.localStorage
57
+ }) ?? 'auto';
49
58
  });
50
59
  const browserLocale = useMemo(()=>{
51
60
  if (supportedCodes.length === 0) return undefined;
@@ -58,12 +67,14 @@ function useLocale({ i18n, antdLocaleLoaders, antdXLocaleLoaders, dayjsLocaleMap
58
67
  ]);
59
68
  const setPreference = useCallback((code)=>{
60
69
  if (code === 'auto') {
61
- window.localStorage?.removeItem('lowdefy_locale');
70
+ window.localStorage?.removeItem(storageKey);
62
71
  } else {
63
- window.localStorage?.setItem('lowdefy_locale', code);
72
+ window.localStorage?.setItem(storageKey, code);
64
73
  }
65
74
  setUserPreference(code ?? 'auto');
66
- }, []);
75
+ }, [
76
+ storageKey
77
+ ]);
67
78
  window.__lowdefy_setLocale = setPreference;
68
79
  const active = resolveActive({
69
80
  userPreference,
@@ -75,6 +86,8 @@ function useLocale({ i18n, antdLocaleLoaders, antdXLocaleLoaders, dayjsLocaleMap
75
86
  window.__lowdefy_supported_locales = i18n?.locales ?? [];
76
87
  const [antdLocale, setAntdLocale] = useState(null);
77
88
  const [antdXLocale, setAntdXLocale] = useState(null);
89
+ // The engine trace reads the antd strings in use as config text; it has no antd dependency.
90
+ window.__lowdefy_antd_locale = antdLocale ?? defaultAntdLocale;
78
91
  useEffect(()=>{
79
92
  let cancelled = false;
80
93
  loadAntdLocale({