keycloakify 11.4.0-rc.0 → 11.4.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 (93) hide show
  1. package/README.md +2 -0
  2. package/account/i18n/noJsx/getI18n.js +5 -7
  3. package/account/i18n/noJsx/getI18n.js.map +1 -1
  4. package/account/i18n/withJsx/useI18n.js +15 -4
  5. package/account/i18n/withJsx/useI18n.js.map +1 -1
  6. package/bin/20.index.js +0 -85
  7. package/bin/33.index.js +55 -212
  8. package/bin/356.index.js +106 -45
  9. package/bin/36.index.js +1 -2922
  10. package/bin/{526.index.js → 392.index.js} +89 -3
  11. package/bin/40.index.js +1 -86
  12. package/bin/453.index.js +85 -22
  13. package/bin/573.index.js +4 -86
  14. package/bin/653.index.js +752 -0
  15. package/bin/658.index.js +197 -0
  16. package/bin/735.index.js +343 -116
  17. package/bin/786.index.js +78 -14
  18. package/bin/805.index.js +15 -18
  19. package/bin/921.index.js +79 -18
  20. package/bin/97.index.js +64 -13
  21. package/bin/main.js +165 -86
  22. package/bin/shared/constants.d.ts +2 -0
  23. package/bin/shared/constants.js +3 -1
  24. package/bin/shared/constants.js.map +1 -1
  25. package/bin/start-keycloak/startViteDevServer.d.ts +7 -0
  26. package/bin/tools/crawlAsync.d.ts +1 -1
  27. package/bin/tools/isTrackedByGit.d.ts +3 -0
  28. package/bin/tools/runPrettier.d.ts +5 -5
  29. package/bin/tools/untrackFromGit.d.ts +3 -0
  30. package/lib/getKcClsx.js +1 -2
  31. package/lib/getKcClsx.js.map +1 -1
  32. package/login/KcContext/KcContext.d.ts +2 -2
  33. package/login/KcContext/kcContextMocks.d.ts +1 -1
  34. package/login/TemplateProps.d.ts +0 -1
  35. package/login/i18n/noJsx/getI18n.js +5 -7
  36. package/login/i18n/noJsx/getI18n.js.map +1 -1
  37. package/login/i18n/withJsx/useI18n.js +15 -4
  38. package/login/i18n/withJsx/useI18n.js.map +1 -1
  39. package/login/pages/LoginConfigTotp.js +1 -1
  40. package/login/pages/LoginConfigTotp.js.map +1 -1
  41. package/login/pages/LoginPasskeysConditionalAuthenticate.js +16 -18
  42. package/login/pages/LoginPasskeysConditionalAuthenticate.js.map +1 -1
  43. package/package.json +17 -7
  44. package/src/account/i18n/noJsx/getI18n.tsx +5 -7
  45. package/src/account/i18n/withJsx/useI18n.tsx +18 -4
  46. package/src/bin/eject-page.ts +28 -10
  47. package/src/bin/initialize-account-theme/initializeAccountTheme_singlePage.ts +3 -18
  48. package/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts +1 -2
  49. package/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl +39 -0
  50. package/src/bin/keycloakify/generateResources/generateResources.ts +6 -2
  51. package/src/bin/main.ts +51 -4
  52. package/src/bin/postinstall/getUiModuleFileSourceCodeReadyToBeCopied.ts +30 -19
  53. package/src/bin/postinstall/installUiModulesPeerDependencies.ts +3 -3
  54. package/src/bin/postinstall/managedGitignoreFile.ts +4 -3
  55. package/src/bin/postinstall/postinstall.ts +24 -2
  56. package/src/bin/postinstall/uiModuleMeta.ts +14 -9
  57. package/src/bin/shared/buildContext.ts +1 -2
  58. package/src/bin/shared/constants.ts +4 -1
  59. package/src/bin/start-keycloak/myrealm-realm-25.json +21 -34
  60. package/src/bin/start-keycloak/myrealm-realm-26.json +32 -35
  61. package/src/bin/start-keycloak/start-keycloak.ts +93 -1
  62. package/src/bin/start-keycloak/startViteDevServer.ts +39 -0
  63. package/src/bin/tools/crawlAsync.ts +6 -6
  64. package/src/bin/tools/isTrackedByGit.ts +29 -0
  65. package/src/bin/tools/listInstalledModules.ts +1 -2
  66. package/src/bin/tools/npmInstall.ts +396 -1
  67. package/src/bin/tools/runPrettier.ts +63 -14
  68. package/src/bin/tools/untrackFromGit.ts +24 -0
  69. package/src/bin/tsconfig.json +1 -1
  70. package/src/bin/update-kc-gen.ts +17 -2
  71. package/src/lib/getKcClsx.ts +1 -2
  72. package/src/login/KcContext/KcContext.ts +2 -2
  73. package/src/login/KcContext/kcContextMocks.ts +1 -1
  74. package/src/login/TemplateProps.ts +0 -1
  75. package/src/login/i18n/noJsx/getI18n.tsx +5 -7
  76. package/src/login/i18n/withJsx/useI18n.tsx +18 -4
  77. package/src/login/pages/LoginConfigTotp.tsx +18 -20
  78. package/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx +51 -52
  79. package/src/tools/StatefulObservable/StatefulObservable.ts +1 -2
  80. package/src/tools/deepAssign.ts +1 -2
  81. package/src/tools/extractLastParenthesisContent.ts +43 -0
  82. package/src/vite-plugin/vite-plugin.ts +67 -0
  83. package/stories/login/pages/Info.stories.tsx +3 -39
  84. package/tools/StatefulObservable/StatefulObservable.js +1 -2
  85. package/tools/StatefulObservable/StatefulObservable.js.map +1 -1
  86. package/tools/deepAssign.js +1 -2
  87. package/tools/deepAssign.js.map +1 -1
  88. package/tools/extractLastParenthesisContent.d.ts +6 -0
  89. package/tools/extractLastParenthesisContent.js +36 -0
  90. package/tools/extractLastParenthesisContent.js.map +1 -0
  91. package/vite-plugin/index.js +326 -186
  92. package/bin/124.index.js +0 -348
  93. package/stories/login/pages/LoginDeviceVerifyUserCode.stories.tsx +0 -18
@@ -47,11 +47,25 @@ export function createUseI18n<
47
47
 
48
48
  function renderHtmlString(params: { htmlString: string; msgKey: string }): JSX.Element {
49
49
  const { htmlString, msgKey } = params;
50
+
51
+ const htmlString_sanitized = kcSanitize(htmlString);
52
+
53
+ const Element = (() => {
54
+ if (htmlString_sanitized.includes("<") && htmlString_sanitized.includes(">")) {
55
+ for (const tagName of ["div", "section", "article", "ul", "ol"]) {
56
+ if (htmlString_sanitized.includes(`<${tagName}`)) {
57
+ return "div";
58
+ }
59
+ }
60
+ }
61
+ return "span";
62
+ })();
63
+
50
64
  return (
51
- <div
65
+ <Element
52
66
  data-kc-msg={msgKey}
53
67
  dangerouslySetInnerHTML={{
54
- __html: kcSanitize(htmlString)
68
+ __html: htmlString_sanitized
55
69
  }}
56
70
  />
57
71
  );
@@ -83,7 +97,7 @@ export function createUseI18n<
83
97
  })();
84
98
 
85
99
  add_style: {
86
- const attributeName = "data-kc-i18n";
100
+ const attributeName = "data-kc-msg";
87
101
 
88
102
  // Check if already exists in head
89
103
  if (document.querySelector(`style[${attributeName}]`) !== null) {
@@ -92,7 +106,7 @@ export function createUseI18n<
92
106
 
93
107
  const styleElement = document.createElement("style");
94
108
  styleElement.attributes.setNamedItem(document.createAttribute(attributeName));
95
- styleElement.textContent = `[data-kc-msg] { display: inline-block; }`;
109
+ styleElement.textContent = `div[${attributeName}] { display: inline-block; }`;
96
110
  document.head.prepend(styleElement);
97
111
  }
98
112
 
@@ -52,28 +52,26 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
52
52
  </li>
53
53
  <li>
54
54
  <p>{msg("loginTotpManualStep3")}</p>
55
- <p>
56
- <ul>
57
- <li id="kc-totp-type">
58
- {msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
59
- </li>
60
- <li id="kc-totp-algorithm">
61
- {msg("loginTotpAlgorithm")}: {totp.policy.getAlgorithmKey()}
55
+ <ul>
56
+ <li id="kc-totp-type">
57
+ {msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
58
+ </li>
59
+ <li id="kc-totp-algorithm">
60
+ {msg("loginTotpAlgorithm")}: {totp.policy.getAlgorithmKey()}
61
+ </li>
62
+ <li id="kc-totp-digits">
63
+ {msg("loginTotpDigits")}: {totp.policy.digits}
64
+ </li>
65
+ {totp.policy.type === "totp" ? (
66
+ <li id="kc-totp-period">
67
+ {msg("loginTotpInterval")}: {totp.policy.period}
62
68
  </li>
63
- <li id="kc-totp-digits">
64
- {msg("loginTotpDigits")}: {totp.policy.digits}
69
+ ) : (
70
+ <li id="kc-totp-counter">
71
+ {msg("loginTotpCounter")}: {totp.policy.initialCounter}
65
72
  </li>
66
- {totp.policy.type === "totp" ? (
67
- <li id="kc-totp-period">
68
- {msg("loginTotpInterval")}: {totp.policy.period}
69
- </li>
70
- ) : (
71
- <li id="kc-totp-counter">
72
- {msg("loginTotpCounter")}: {totp.policy.initialCounter}
73
- </li>
74
- )}
75
- </ul>
76
- </p>
73
+ )}
74
+ </ul>
77
75
  </li>
78
76
  </>
79
77
  ) : (
@@ -115,62 +115,61 @@ export default function LoginPasskeysConditionalAuthenticate(
115
115
  </div>
116
116
  </>
117
117
  )}
118
- <div id="kc-form">
119
- <div id="kc-form-wrapper">
120
- {realm.password && (
121
- <form
122
- id="kc-form-passkey"
123
- action={url.loginAction}
124
- method="post"
125
- style={{ display: "none" }}
126
- onSubmit={event => {
127
- try {
128
- // @ts-expect-error
129
- event.target.login.disabled = true;
130
- } catch {}
118
+ </>
119
+ )}
120
+ <div id="kc-form">
121
+ <div id="kc-form-wrapper">
122
+ {realm.password && (
123
+ <form
124
+ id="kc-form-login"
125
+ action={url.loginAction}
126
+ method="post"
127
+ style={{ display: "none" }}
128
+ onSubmit={event => {
129
+ try {
130
+ // @ts-expect-error
131
+ event.target.login.disabled = true;
132
+ } catch {}
131
133
 
132
- return true;
133
- }}
134
- >
135
- {!usernameHidden && (
136
- <div className={kcClsx("kcFormGroupClass")}>
137
- <label htmlFor="username" className={kcClsx("kcLabelClass")}>
138
- {msg("passkey-autofill-select")}
139
- </label>
140
- <input
141
- tabIndex={1}
142
- id="username"
143
- aria-invalid={messagesPerField.existsError("username")}
144
- className={kcClsx("kcInputClass")}
145
- name="username"
146
- defaultValue={login.username ?? ""}
147
- //autoComplete="username webauthn"
148
- type="text"
149
- autoFocus
150
- autoComplete="off"
151
- />
152
- {messagesPerField.existsError("username") && (
153
- <span id="input-error-username" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
154
- {messagesPerField.get("username")}
155
- </span>
156
- )}
157
- </div>
134
+ return true;
135
+ }}
136
+ >
137
+ {!usernameHidden && (
138
+ <div className={kcClsx("kcFormGroupClass")}>
139
+ <label htmlFor="username" className={kcClsx("kcLabelClass")}>
140
+ {msg("passkey-autofill-select")}
141
+ </label>
142
+ <input
143
+ tabIndex={1}
144
+ id="username"
145
+ aria-invalid={messagesPerField.existsError("username")}
146
+ className={kcClsx("kcInputClass")}
147
+ name="username"
148
+ defaultValue={login.username ?? ""}
149
+ autoComplete="username webauthn"
150
+ type="text"
151
+ autoFocus
152
+ />
153
+ {messagesPerField.existsError("username") && (
154
+ <span id="input-error-username" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
155
+ {messagesPerField.get("username")}
156
+ </span>
158
157
  )}
159
- </form>
158
+ </div>
160
159
  )}
161
- <div id="kc-form-passkey-button" className={kcClsx("kcFormButtonsClass")} style={{ display: "none" }}>
162
- <input
163
- id={authButtonId}
164
- type="button"
165
- autoFocus
166
- value={msgStr("passkey-doAuthenticate")}
167
- className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
168
- />
169
- </div>
170
- </div>
160
+ </form>
161
+ )}
162
+ <div id="kc-form-passkey-button" className={kcClsx("kcFormButtonsClass")} style={{ display: "none" }}>
163
+ <input
164
+ id={authButtonId}
165
+ type="button"
166
+ autoFocus
167
+ value={msgStr("passkey-doAuthenticate")}
168
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
169
+ />
171
170
  </div>
172
- </>
173
- )}
171
+ </div>
172
+ </div>
174
173
  </div>
175
174
  </Template>
176
175
  );
@@ -1,5 +1,4 @@
1
- import { assert } from "tsafe/assert";
2
- import { is } from "tsafe/is";
1
+ import { assert, is } from "tsafe/assert";
3
2
 
4
3
  export type StatefulObservable<T> = {
5
4
  current: T;
@@ -1,5 +1,4 @@
1
- import { assert } from "tsafe/assert";
2
- import { is } from "tsafe/is";
1
+ import { assert, is } from "tsafe/assert";
3
2
  import { structuredCloneButFunctions } from "./structuredCloneButFunctions";
4
3
 
5
4
  /** NOTE: Array a copied over, not merged. */
@@ -0,0 +1,43 @@
1
+ /**
2
+ * "Hello (world)" => "world"
3
+ * "Hello (world) (foo)" => "foo"
4
+ * "Hello (world (foo))" => "world (foo)"
5
+ */
6
+ export function extractLastParenthesisContent(str: string): string | undefined {
7
+ const chars: string[] = [];
8
+
9
+ for (const char of str) {
10
+ chars.push(char);
11
+ }
12
+
13
+ const extractedChars: string[] = [];
14
+ let openingCount = 0;
15
+
16
+ loop_through_char: for (let i = chars.length - 1; i >= 0; i--) {
17
+ const char = chars[i];
18
+
19
+ if (i === chars.length - 1) {
20
+ if (char !== ")") {
21
+ return undefined;
22
+ }
23
+
24
+ continue;
25
+ }
26
+
27
+ switch (char) {
28
+ case ")":
29
+ openingCount++;
30
+ break;
31
+ case "(":
32
+ if (openingCount === 0) {
33
+ return extractedChars.join("");
34
+ }
35
+ openingCount--;
36
+ break;
37
+ }
38
+
39
+ extractedChars.unshift(char);
40
+ }
41
+
42
+ return undefined;
43
+ }
@@ -212,6 +212,73 @@ export function keycloakify(params: keycloakify.Params) {
212
212
  force: true
213
213
  }
214
214
  );
215
+ },
216
+ transformIndexHtml: html => {
217
+ const doReadKcContextFromUrl =
218
+ process.env.NODE_ENV === "development" &&
219
+ process.env[
220
+ VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.READ_KC_CONTEXT_FROM_URL
221
+ ] === "true";
222
+
223
+ if (!doReadKcContextFromUrl) {
224
+ return html;
225
+ }
226
+
227
+ const scriptContent = `
228
+ (()=>{
229
+
230
+ const kcContext = (()=>{
231
+
232
+ const paramName= "kcContext";
233
+
234
+ read_from_url_case: {
235
+
236
+ const url = new URL(window.location.href);
237
+
238
+ const paramValue = url.searchParams.get(paramName);
239
+
240
+ if( paramValue === null ){
241
+ break read_from_url_case;
242
+ }
243
+
244
+ url.searchParams.delete(paramName);
245
+
246
+ window.history.replaceState({}, "", url);
247
+
248
+ const kcContext = JSON.parse(decodeURIComponent(paramValue));
249
+
250
+ sessionStorage.setItem(paramName, JSON.stringify(kcContext));
251
+
252
+ return kcContext;
253
+
254
+ }
255
+
256
+ read_from_session_storage_case: {
257
+
258
+ const paramValue = sessionStorage.getItem(paramName);
259
+
260
+ if( paramValue === null ){
261
+ break read_from_session_storage_case;
262
+ }
263
+
264
+ return JSON.parse(paramValue);
265
+
266
+ }
267
+
268
+ return undefined;
269
+
270
+ })();
271
+
272
+ if( kcContext === undefined ){
273
+ return;
274
+ }
275
+
276
+ window.kcContext = kcContext;
277
+
278
+ })();
279
+ `;
280
+
281
+ return html.replace(/<head>/, `<head><script>${scriptContent}</script>`);
215
282
  }
216
283
  } satisfies Plugin;
217
284
 
@@ -17,6 +17,7 @@ export const Default: Story = {
17
17
  render: () => (
18
18
  <KcPageStory
19
19
  kcContext={{
20
+ messageHeader: "Message header",
20
21
  message: {
21
22
  summary: "Server info message"
22
23
  }
@@ -29,6 +30,7 @@ export const WithLinkBack: Story = {
29
30
  render: () => (
30
31
  <KcPageStory
31
32
  kcContext={{
33
+ messageHeader: "Message header",
32
34
  message: {
33
35
  summary: "Server message"
34
36
  },
@@ -42,6 +44,7 @@ export const WithRequiredActions: Story = {
42
44
  render: () => (
43
45
  <KcPageStory
44
46
  kcContext={{
47
+ messageHeader: "Message header",
45
48
  message: {
46
49
  summary: "Required actions: "
47
50
  },
@@ -55,42 +58,3 @@ export const WithRequiredActions: Story = {
55
58
  />
56
59
  )
57
60
  };
58
- export const WithPageRedirect: Story = {
59
- render: () => (
60
- <KcPageStory
61
- kcContext={{
62
- message: { summary: "You will be redirected shortly." },
63
- pageRedirectUri: "https://example.com"
64
- }}
65
- />
66
- )
67
- };
68
- export const WithoutClientBaseUrl: Story = {
69
- render: () => (
70
- <KcPageStory
71
- kcContext={{
72
- message: { summary: "No client base URL defined." },
73
- client: { baseUrl: undefined }
74
- }}
75
- />
76
- )
77
- };
78
- export const WithMessageHeader: Story = {
79
- render: () => (
80
- <KcPageStory
81
- kcContext={{
82
- messageHeader: "Important Notice",
83
- message: { summary: "This is an important message." }
84
- }}
85
- />
86
- )
87
- };
88
- export const WithAdvancedMessage: Story = {
89
- render: () => (
90
- <KcPageStory
91
- kcContext={{
92
- message: { summary: "Please take note of this <strong>important</strong> information." }
93
- }}
94
- />
95
- )
96
- };
@@ -1,5 +1,4 @@
1
- import { assert } from "tsafe/assert";
2
- import { is } from "tsafe/is";
1
+ import { assert, is } from "tsafe/assert";
3
2
  export function createStatefulObservable(getInitialValue) {
4
3
  const nextFunctions = [];
5
4
  const { get, set } = (() => {
@@ -1 +1 @@
1
- {"version":3,"file":"StatefulObservable.js","sourceRoot":"","sources":["../../src/tools/StatefulObservable/StatefulObservable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAW9B,MAAM,UAAU,wBAAwB,CACpC,eAAwB;IAExB,MAAM,aAAa,GAA0B,EAAE,CAAC;IAEhD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE;QACvB,IAAI,YAAY,GAAoB,SAAS,CAAC;QAE9C,SAAS,GAAG,CAAC,IAAO;YAChB,YAAY,GAAG,CAAC,IAAI,CAAC,CAAC;YAEtB,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO;YACH,GAAG,EAAE,GAAG,EAAE;gBACN,IAAI,YAAY,KAAK,SAAS,EAAE;oBAC5B,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;oBACvB,MAAM,CAAC,CAAC,EAAE,CAAY,YAAY,CAAC,CAAC,CAAC;iBACxC;gBACD,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,GAAG;SACN,CAAC;IACN,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,MAAM,CAAC,cAAc,CACxB;QACI,OAAO,EAAE,IAAgB;QACzB,SAAS,EAAE,CAAC,IAAuB,EAAE,EAAE;YACnC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEzB,OAAO;gBACH,WAAW,EAAE,GAAG,EAAE,CACd,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC3D,CAAC;QACN,CAAC;KACJ,EACD,SAAS,EACT;QACI,UAAU,EAAE,IAAI;QAChB,GAAG;QACH,GAAG;KACN,CACJ,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"StatefulObservable.js","sourceRoot":"","sources":["../../src/tools/StatefulObservable/StatefulObservable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAW1C,MAAM,UAAU,wBAAwB,CACpC,eAAwB;IAExB,MAAM,aAAa,GAA0B,EAAE,CAAC;IAEhD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE;QACvB,IAAI,YAAY,GAAoB,SAAS,CAAC;QAE9C,SAAS,GAAG,CAAC,IAAO;YAChB,YAAY,GAAG,CAAC,IAAI,CAAC,CAAC;YAEtB,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO;YACH,GAAG,EAAE,GAAG,EAAE;gBACN,IAAI,YAAY,KAAK,SAAS,EAAE;oBAC5B,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;oBACvB,MAAM,CAAC,CAAC,EAAE,CAAY,YAAY,CAAC,CAAC,CAAC;iBACxC;gBACD,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,GAAG;SACN,CAAC;IACN,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,MAAM,CAAC,cAAc,CACxB;QACI,OAAO,EAAE,IAAgB;QACzB,SAAS,EAAE,CAAC,IAAuB,EAAE,EAAE;YACnC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEzB,OAAO;gBACH,WAAW,EAAE,GAAG,EAAE,CACd,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC3D,CAAC;QACN,CAAC;KACJ,EACD,SAAS,EACT;QACI,UAAU,EAAE,IAAI;QAChB,GAAG;QACH,GAAG;KACN,CACJ,CAAC;AACN,CAAC"}
@@ -1,5 +1,4 @@
1
- import { assert } from "tsafe/assert";
2
- import { is } from "tsafe/is";
1
+ import { assert, is } from "tsafe/assert";
3
2
  import { structuredCloneButFunctions } from "./structuredCloneButFunctions";
4
3
  /** NOTE: Array a copied over, not merged. */
5
4
  export function deepAssign(params) {
@@ -1 +1 @@
1
- {"version":3,"file":"deepAssign.js","sourceRoot":"","sources":["../src/tools/deepAssign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,MAG1B;IACG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAElC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC9B,IAAI,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAErC,IAAI,kBAAkB,KAAK,SAAS,EAAE;YAClC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YACnB,OAAO;SACV;QAED,IAAI,kBAAkB,YAAY,IAAI,EAAE;YACpC,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,IAAI,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;aAChD,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IAAI,kBAAkB,YAAY,KAAK,EAAE;YACrC,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,2BAA2B,CAAC,kBAAkB,CAAC;aACzD,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IACI,kBAAkB,YAAY,QAAQ;YACtC,CAAC,CAAC,kBAAkB,YAAY,MAAM,CAAC,EACzC;YACE,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,kBAAkB;aAC5B,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,EAAE;YAClC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;SACpB;QAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEvC,MAAM,CAAC,EAAE,CAA0B,kBAAkB,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,CAA0B,kBAAkB,CAAC,CAAC,CAAC;QAExD,UAAU,CAAC;YACP,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,kBAAkB;SAC7B,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,MAAM,CAAC,MAIf;IACG,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEtC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE;QAC/B,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,YAAY,EAAE,IAAI;QAClB,KAAK;KACR,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"deepAssign.js","sourceRoot":"","sources":["../src/tools/deepAssign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,MAG1B;IACG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAElC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC9B,IAAI,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAErC,IAAI,kBAAkB,KAAK,SAAS,EAAE;YAClC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YACnB,OAAO;SACV;QAED,IAAI,kBAAkB,YAAY,IAAI,EAAE;YACpC,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,IAAI,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;aAChD,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IAAI,kBAAkB,YAAY,KAAK,EAAE;YACrC,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,2BAA2B,CAAC,kBAAkB,CAAC;aACzD,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IACI,kBAAkB,YAAY,QAAQ;YACtC,CAAC,CAAC,kBAAkB,YAAY,MAAM,CAAC,EACzC;YACE,MAAM,CAAC;gBACH,MAAM;gBACN,GAAG;gBACH,KAAK,EAAE,kBAAkB;aAC5B,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,EAAE;YAClC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;SACpB;QAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEvC,MAAM,CAAC,EAAE,CAA0B,kBAAkB,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,CAA0B,kBAAkB,CAAC,CAAC,CAAC;QAExD,UAAU,CAAC;YACP,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,kBAAkB;SAC7B,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,MAAM,CAAC,MAIf;IACG,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEtC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE;QAC/B,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,YAAY,EAAE,IAAI;QAClB,KAAK;KACR,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * "Hello (world)" => "world"
3
+ * "Hello (world) (foo)" => "foo"
4
+ * "Hello (world (foo))" => "world (foo)"
5
+ */
6
+ export declare function extractLastParenthesisContent(str: string): string | undefined;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * "Hello (world)" => "world"
3
+ * "Hello (world) (foo)" => "foo"
4
+ * "Hello (world (foo))" => "world (foo)"
5
+ */
6
+ export function extractLastParenthesisContent(str) {
7
+ const chars = [];
8
+ for (const char of str) {
9
+ chars.push(char);
10
+ }
11
+ const extractedChars = [];
12
+ let openingCount = 0;
13
+ loop_through_char: for (let i = chars.length - 1; i >= 0; i--) {
14
+ const char = chars[i];
15
+ if (i === chars.length - 1) {
16
+ if (char !== ")") {
17
+ return undefined;
18
+ }
19
+ continue;
20
+ }
21
+ switch (char) {
22
+ case ")":
23
+ openingCount++;
24
+ break;
25
+ case "(":
26
+ if (openingCount === 0) {
27
+ return extractedChars.join("");
28
+ }
29
+ openingCount--;
30
+ break;
31
+ }
32
+ extractedChars.unshift(char);
33
+ }
34
+ return undefined;
35
+ }
36
+ //# sourceMappingURL=extractLastParenthesisContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractLastParenthesisContent.js","sourceRoot":"","sources":["../src/tools/extractLastParenthesisContent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,6BAA6B,CAAC,GAAW;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE;QACpB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KACpB;IAED,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,iBAAiB,EAAE,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtB,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;YACxB,IAAI,IAAI,KAAK,GAAG,EAAE;gBACd,OAAO,SAAS,CAAC;aACpB;YAED,SAAS;SACZ;QAED,QAAQ,IAAI,EAAE;YACV,KAAK,GAAG;gBACJ,YAAY,EAAE,CAAC;gBACf,MAAM;YACV,KAAK,GAAG;gBACJ,IAAI,YAAY,KAAK,CAAC,EAAE;oBACpB,OAAO,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;iBAClC;gBACD,YAAY,EAAE,CAAC;gBACf,MAAM;SACb;QAED,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAChC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC"}