@wral/studio.mods.auth 1.0.0 → 2.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.
package/dist/lib.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(e){return new Promise((t,n)=>{(!e||!e.dispatchEvent)&&n(new Error("getToken must be called with a DOM element")),e.dispatchEvent(new CustomEvent("harness:action",{detail:{type:"auth:requestToken",detail:{callback:t}},bubbles:!0,composed:!0,cancelable:!0}))})}exports.getToken=o;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(e){return new Promise((t,n)=>{e!=null&&e.dispatchEvent||n(new Error("getToken must be called with an eventTarget or DOM element")),e.dispatchEvent(new CustomEvent("harness:action",{detail:{type:"auth:requestToken",detail:{callback:t}},bubbles:!0,composed:!0,cancelable:!0}))})}exports.getToken=o;
package/dist/lib.es.js CHANGED
@@ -1,11 +1,9 @@
1
- function a(e) {
1
+ function o(e) {
2
2
  return new Promise((t, n) => {
3
- (!e || !e.dispatchEvent) && n(new Error("getToken must be called with a DOM element")), e.dispatchEvent(new CustomEvent("harness:action", {
3
+ e != null && e.dispatchEvent || n(new Error("getToken must be called with an eventTarget or DOM element")), e.dispatchEvent(new CustomEvent("harness:action", {
4
4
  detail: {
5
5
  type: "auth:requestToken",
6
- detail: {
7
- callback: t
8
- }
6
+ detail: { callback: t }
9
7
  },
10
8
  bubbles: !0,
11
9
  composed: !0,
@@ -14,5 +12,5 @@ function a(e) {
14
12
  });
15
13
  }
16
14
  export {
17
- a as getToken
15
+ o as getToken
18
16
  };
@@ -0,0 +1,34 @@
1
+ import { html } from 'lit';
2
+
3
+ export async function init(toolkit, mod) {
4
+
5
+ let token = null;
6
+
7
+ const content = html`
8
+ <div slot="main" id="example-app">
9
+ <h1>Example</h1>
10
+ <p>This is an example app.</p>
11
+ <pre><code>${JSON.stringify(mod, null, 2)}</code></pre>
12
+ </div>
13
+ `;
14
+
15
+ toolkit.dispatchAction({
16
+ type: 'app:register',
17
+ detail: {
18
+ id: 'example',
19
+ name: 'Example App',
20
+ content: () => content,
21
+ },
22
+ });
23
+
24
+ toolkit.dispatchAction({
25
+ type: 'auth:requestToken',
26
+ detail: {
27
+ callback: (t) => {
28
+ token = t;
29
+ console.log("got a token!", token);
30
+ },
31
+ }
32
+ });
33
+
34
+ }
package/index.html CHANGED
@@ -4,82 +4,29 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>WRAL.studio</title>
8
- <script type="module" src="/vellum-fixture.mjs"></script>
7
+ <title>Auth Mod Fixture App</title>
8
+ <link rel="stylesheet" href="./vellum/themes/default/index.css">
9
9
  <meta name="robots" content="noindex, nofollow">
10
+ <script type="module" src="./vellum/index.mjs"></script>
10
11
  </head>
11
- <body>
12
- <style>
13
- /*
14
- TODO: move these styles into a theme
15
- */
16
- html {
17
- --spacing-sm: 0.5rem;
18
- --spacing-md: 1rem;
19
- --spacing-lg: 2rem;
20
- --spacing-xl: 3rem;
21
-
22
- --radius-sm: 5px;
23
- --radius-md: 8px;
24
- --radius-lg: 10px;
25
- --radius-full: 50%;
26
- --radius-dynamic: clamp(var(--radius-sm), 1vw, var(--radius-lg));
27
-
28
- --color-primary: #001D68;
29
- --color-primary-light: #193377;
30
- --color-primary-dark: #00144A;
31
- --color-secondary: #2594E3;
32
- --color-secondary-light: #46A4E7;
33
- --color-secondary-light-1: #c3d6e3;
34
- --color-secondary-dark: #1D76B5;
35
- --color-error: var(--color-red-dark);
36
- --color-red: #D1232A;
37
- --color-red-light: #FF2B32;
38
- --color-red-dark: #D1232A;
39
- --color-yellow: #FFEC19;
40
- --color-yellow-light: #FFF15E;
41
- --color-yellow-dark: #E5D416;
42
- --color-green: #72B509;
43
- --color-green-light: #8EC33A;
44
- --color-green-dark: #66A208;
45
- --color-purple: #33109C;
46
- --color-purple-light: #4727A5;
47
- --color-purple-dark: #280C7C;
48
- --color-orange: #FF9505;
49
- --color-orange-light: #FFA21F;
50
- --color-orange-dark: #F58F00;
51
- --color-white: #FFFFFF;
52
- --color-black: #030711;
53
- --color-gray-1: #F7F5F4;
54
- --color-gray-2: #E7E5E4;
55
- --color-gray-3: #D7D4D2;
56
- --color-gray-4: #7C7C7C;
57
- --color-gray-5: #6C6C6C;
58
- --color-gray-6: #565454;
59
- --color-gray-7: #424242;
60
- --color-gray-8: #263238;
61
- --color-gray-9: #1C2321;
12
+ <wral-studio mod-tag="studio-mod">
13
+ <studio-mod
14
+ name="setup"
15
+ src="/vellum/index.mjs"></studio-mod>
62
16
 
63
- --font-heading: Inter, sans-serif;
64
- --font-body: Arial, sans-serif;
65
- --font-size-base: 16px;
66
- --color-text: var(--color-gray-9);
17
+ <studio-mod
18
+ name="example"
19
+ require="setup"
20
+ src="/vellum/example-app.mjs"></studio-mod>
67
21
 
68
- --drop-shadow-md: 0 0 10px var(--color-gray-3);
69
- }
70
- html, body {
71
- margin: 0;
72
- padding: 0;
73
- box-sizing: border-box;
74
- }
75
- </style>
76
- <studio-app>
77
- <vellum-mod name="setup" src="/vellum-fixture.mjs"></vellum-mod>
22
+ <studio-mod
23
+ name="auth"
24
+ require="setup"
25
+ api="https://api.wral.com/auth"
26
+ force-login
27
+ src="/src/index.mjs"></studio-mod>
78
28
 
79
- <vellum-mod name="auth" src="/src/index.mjs"
80
- api="https://api.wral.com/auth"
81
- force-login></vellum-mod>
82
-
83
- </studio-app>
29
+ </wral-studio>
30
+ <body>
84
31
  </body>
85
32
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wral/studio.mods.auth",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Auth mod for Studio/Vellum",
5
5
  "main": "dist/auth.cjs.js",
6
6
  "module": "dist/auth.es.js",
@@ -21,7 +21,8 @@
21
21
  "lit": "^3.2.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@thefarce/vellum": "^0.4.2",
24
+ "@thefarce/vellum": "^1.0.4",
25
+ "@thefarce/vellum.mods.managers.layouts.simple-web-component": "^0.2.0",
25
26
  "eslint": "^9.22.0",
26
27
  "eslint-plugin-jest": "^28.11.0",
27
28
  "jest": "^29.7.0",
@@ -1,5 +1,4 @@
1
1
  import { html, css, LitElement } from 'lit';
2
- import login from '../login.mjs';
3
2
  import { createClient } from '@wral/sdk-auth';
4
3
 
5
4
  const stages = Object.freeze({
@@ -8,20 +7,25 @@ const stages = Object.freeze({
8
7
  FINISHED: 'finished',
9
8
  });
10
9
 
11
- export class ForgotPasswordForm extends LitElement {
10
+ class AuthForgotPasswordForm extends LitElement {
12
11
 
13
12
  static get properties() {
14
13
  return {
15
- api: { type: String, attribute: 'api' },
16
- confirmation: { type: 'String', attribute: 'confirmation' },
14
+ api: { type: String },
17
15
  };
18
16
  }
19
17
 
20
18
  static get styles() {
21
19
  return css`
22
20
  :host {
23
- display: block;
24
- font-family: Arial, sans-serif;
21
+ display: flex;
22
+ flex-direction: column;
23
+ justify-content: center;
24
+ align-items: center;
25
+ }
26
+ form {
27
+ display: flex;
28
+ flex-direction: column;
25
29
  }
26
30
  form input[type="text"],
27
31
  form input[type="password"] {
@@ -29,14 +33,14 @@ export class ForgotPasswordForm extends LitElement {
29
33
  margin: 5px 0 15px 0;
30
34
  padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem);
31
35
  box-sizing: border-box;
32
- border: 1px solid var(--color-gray-300, #ccc);
33
- border-radius: var(--radius-sm, 5px);
34
- color: var(--color-gray-9, #333);
35
- background-color: var(--color-gray-0, transparent);
36
+ border: 1px solid var(--color-gray-300, #CCC);
37
+ border-radius: var(--radius-sm, 3px);
38
+ color: var(--color-gray-700, #333);
39
+ background-color: var(--color-gray-50, #FFF);
36
40
  }
37
41
  form input[type="submit"] {
38
- background-color:var(--color-primary, inherit);
39
- color: var(--color-gray-0, #fff);
42
+ background-color: var(--color-primary, inherit);
43
+ color: var(--color-gray-50, #fff);
40
44
  border: none;
41
45
  padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem);
42
46
  border-radius: var(--radius-dynamic, 5px);
@@ -47,17 +51,16 @@ export class ForgotPasswordForm extends LitElement {
47
51
  }
48
52
  form label {
49
53
  font-size: 14px;
50
- color: var(--color-gray-9, #333);
54
+ color: var(--color-gray-700, #333);
51
55
  font-weight: bold;
52
56
  margin-bottom: var(--spacing-sm, 0.5rem);
53
57
  }
54
58
  .error {
55
59
  color: var(--color-danger, red);
56
- font-size: 12px;
60
+ font-size: 14px;
57
61
  }
58
62
  `;
59
63
  }
60
-
61
64
  constructor() {
62
65
  super();
63
66
  this.errors = {};
@@ -65,37 +68,35 @@ export class ForgotPasswordForm extends LitElement {
65
68
  }
66
69
 
67
70
  connectedCallback() {
68
- console.log('[auth]','[forgot-password]', { confirmation: this.confirmation });
69
71
  super.connectedCallback();
70
- // If there is a confirmation, update stage
71
- if (this.confirmation && this.stage !== stages.FINISHED) {
72
- this.stage = stages.CONFIRM;
72
+ if (this.confirmToken && this.stage !== stages.FINISHED) {
73
+ this.setStage(stages.CONFIRM);
73
74
  }
75
+ }
76
+
77
+ setStage(stage) {
78
+ if (this.stage === stage) {
79
+ return;
80
+ }
81
+ this.stage = stage;
74
82
  this.requestUpdate();
75
83
  }
76
84
 
77
85
  handleSubmitRequest(e) {
78
86
  e.preventDefault();
79
- // Check for submission errors (none since username is required)
80
87
  const username = e.target.username.value;
81
88
  this.username = username;
82
89
 
83
90
  // Send API Request
84
- const authClient = createClient({
85
- baseUrl: this.api,
86
- });
91
+ const authClient = createClient({ baseUrl: this.api });
87
92
  authClient.triggerResetPassword({ username }).then(() => {
88
- // Update stage
89
- console.log('[auth]', 'triggered reset password for ', username);
90
- this.stage = stages.CONFIRM;
93
+ this.setStage(stages.CONFIRM);
91
94
  }).catch((error) => {
92
- console.error('[auth] error triggering reset password', error);
93
95
  this.errors['general'] = error.message ||
94
- 'Unable to send password reset request. Please try again later.';
96
+ 'Unable to send password reset request. Please try again later.';
95
97
  }).finally(() => {
96
98
  this.requestUpdate();
97
99
  });
98
-
99
100
  }
100
101
 
101
102
  handleSubmitConfirm(e) {
@@ -105,18 +106,18 @@ export class ForgotPasswordForm extends LitElement {
105
106
  const password = e.target.password.value;
106
107
  const confirmPassword = e.target['confirm-password'].value;
107
108
  if (password !== confirmPassword) {
108
- this.errors['confirm-password'] = 'Passwords do not match.';
109
+ this.errors['confirm-password'] = 'Passwords do not match';
109
110
  this.requestUpdate();
110
111
  return;
111
112
  }
112
- const confirmationCode = this.confirmation || e.target['confirmation'].value;
113
+ const confirmationCode = this.confirmToken || e.target['confirmation'].value;
113
114
  if (!confirmationCode) {
114
- this.errors['general'] = 'Confirmation code is required.';
115
+ this.errors['general'] = 'Confirmation code is required';
115
116
  this.requestUpdate();
116
117
  return;
117
118
  }
118
119
 
119
- // Send API request
120
+ // Send API Request
120
121
  const authClient = createClient({
121
122
  baseUrl: this.api,
122
123
  });
@@ -125,19 +126,19 @@ export class ForgotPasswordForm extends LitElement {
125
126
  newPassword: password,
126
127
  confirmationCode,
127
128
  }).then(() => {
128
- // Login and emit a login event
129
- // TODO: DRY this login event
130
- return login(this, { username: this.username, password }).then(({token}) => {
131
- // emit a success event
132
- this.dispatchEvent(new CustomEvent('login-success', {
133
- detail: { token },
134
- composed: true,
135
- bubbles: true,
136
- }));
137
- });
129
+ authClient.mintToken({ username: this.username, password: password })
130
+ .then(({token}) => {
131
+ this.dispatchEvent(new CustomEvent('login-success', {
132
+ detail: { token },
133
+ bubbles: true,
134
+ composed: true,
135
+ cancelable: true,
136
+ }));
137
+ });
138
+ // TODO: handle issues here, probably by refreshing the page
138
139
  }).catch((error) => {
139
- console.error('[auth][forgot-password-form]',error);
140
- this.errors['general'] = 'Unable to reset password. Please try again later.';
140
+ this.errors['general'] = error.message ||
141
+ 'Unable to send password reset request. Please try again later.';
141
142
  }).finally(() => {
142
143
  this.requestUpdate();
143
144
  });
@@ -145,13 +146,10 @@ export class ForgotPasswordForm extends LitElement {
145
146
 
146
147
  render() {
147
148
  switch (this.stage) {
148
- case stages.CONFIRM:
149
- return this.renderConfirmForm();
150
149
  case stages.FINISHED:
151
150
  return html`<p>Your password has been reset.</p>`;
152
- case stages.REQUEST_SENT:
153
- return html`<p>A email has been sent with instructions to finish
154
- resetting your password.</p>`;
151
+ case stages.CONFIRM:
152
+ return this.renderConfirmForm();
155
153
  case stages.REQUEST:
156
154
  default:
157
155
  return this.renderRequestForm();
@@ -159,7 +157,8 @@ export class ForgotPasswordForm extends LitElement {
159
157
  }
160
158
 
161
159
  renderRequestForm() {
162
- return html`<form @submit=${this.handleSubmitRequest}>
160
+ return html`<form @submit="${this.handleSubmitRequest}">
161
+ <p>Enter your username or email address to request a password reset.</p>
163
162
  <label for="username">Username/Email</label>
164
163
  <input
165
164
  type="text"
@@ -168,22 +167,23 @@ export class ForgotPasswordForm extends LitElement {
168
167
  required
169
168
  />
170
169
  <p class="error">${this.errors['general']}</p>
171
- <input type="submit" value="Request password reset" />
170
+ <input type="submit" value="Request Password Reset" />
172
171
  </form>`;
173
172
  }
174
173
 
174
+
175
175
  renderConfirmForm() {
176
- return html`<form @submit=${this.handleSubmitConfirm}>
177
- <p>You should have received a confirmation code to reset your password</p>
176
+ return html`<form @submit="${this.handleSubmitConfirm}">
177
+ <p>You should have received a confirmation code to reset your password.</p>
178
178
 
179
- ${this.confirmation ? ''
179
+ ${this.confirmToken ? ''
180
180
  : html`<label for="confirmation">Confirmation Code</label>
181
- <input
182
- type="text"
183
- name="confirmation"
184
- placeholder="Confirmation Code"
185
- required
186
- />`}
181
+ <input
182
+ type="text"
183
+ name="confirmation"
184
+ placeholder="Confirmation Code"
185
+ required
186
+ />`}
187
187
 
188
188
  <label for="password">New Password</label>
189
189
  <input
@@ -192,10 +192,6 @@ export class ForgotPasswordForm extends LitElement {
192
192
  placeholder="Password"
193
193
  required
194
194
  />
195
- ${this.errors['password']
196
- ? html`<p class="error">${this.errors['password']}</p>`
197
- : ''}
198
-
199
195
  <label for="confirm-password">Confirm Password</label>
200
196
  <input
201
197
  type="password"
@@ -204,14 +200,18 @@ export class ForgotPasswordForm extends LitElement {
204
200
  required
205
201
  />
206
202
  ${this.errors['confirm-password']
207
- ? html`<p class="error">${this.errors['confirm-password']}</p>`
208
- : ''}
203
+ ? html`<p class="error">${this.errors['confirm-password']}</p>` : ''}
209
204
 
210
205
  ${this.errors['general']
211
- ? html`<p class="error">${this.errors['general']}</p>`
212
- : ''}
206
+ ? html`<p class="error">${this.errors['general']}</p>` : ''}
207
+
213
208
  <input type="submit" value="Reset Password" />
214
209
  </form>`;
215
210
  }
216
211
 
212
+
213
+ }
214
+
215
+ if (!customElements.get('auth-forgot-password-form')) {
216
+ window.customElements.define('auth-forgot-password-form', AuthForgotPasswordForm);
217
217
  }
package/src/helper.mjs CHANGED
@@ -1,27 +1,16 @@
1
1
  /**
2
- * This is a helper for consumers of the Auth mod to get
3
- * an API token.
4
- * @example <code>
5
- * async () => {
6
- * const myElem = document.querySelector('my-element');
7
- * const token = await getToken(myElem);
8
- * someApiCall({ token, ... });
9
- * }
10
- * </code>
11
- * @param {Element} eventTarget - a DOM element that will trigger the event
12
- * @returns {Promise} - a promise that resolves with the token
2
+ * These are helper functions for consumption by other modules.
13
3
  */
4
+
14
5
  export function getToken(eventTarget) {
15
6
  return new Promise((resolve, reject) => {
16
- if (!eventTarget || !eventTarget.dispatchEvent) {
17
- reject(new Error('getToken must be called with a DOM element'));
7
+ if (!eventTarget?.dispatchEvent) {
8
+ reject(new Error('getToken must be called with an eventTarget or DOM element'));
18
9
  }
19
10
  eventTarget.dispatchEvent(new CustomEvent('harness:action', {
20
11
  detail: {
21
12
  type: 'auth:requestToken',
22
- detail: {
23
- callback: resolve,
24
- },
13
+ detail: { callback: resolve },
25
14
  },
26
15
  bubbles: true,
27
16
  composed: true,
package/src/index.mjs CHANGED
@@ -1,17 +1,113 @@
1
- import { getToken as getTokenHelper } from './helper.mjs';
2
- import { getConfig } from './config.mjs';
3
- import { makeAuth } from './auth.mjs';
1
+ import { getToken as dispatchTokenRequest } from './helper.mjs';
2
+ import authLayout from './layout.mjs';
3
+ import loginForm from './login-form.mjs';
4
+ import {
5
+ setToken,
6
+ getFreshToken,
7
+ destroyToken,
8
+ } from './token.mjs';
4
9
 
5
10
  export function init(toolkit, mod) {
6
- const config = getConfig(toolkit, mod);
7
- const auth = makeAuth(config);
8
- auth.mount();
9
- auth.requestRender(); // initial render
10
-
11
- /* If we'd like to force a login immediately, request the token. */
12
- if (config.forceLogin) {
13
- getTokenHelper(toolkit.element).then((token) => {
14
- auth.log('You have logged in successfully!', token);
11
+
12
+ const modName = 'mod-auth';
13
+ const debug = mod?.element?.attributes?.debug?.value || false;
14
+ const forceLogin = mod?.element?.attributes?.['force-login']?.value || false;
15
+
16
+ const state = {
17
+ modName,
18
+ toolkit,
19
+ apiBaseUrl: mod?.element?.attributes?.api?.value || 'https://api.wral.com/auth',
20
+ debug: debug ? (...args) => console.log('[auth]',...args) : () => {},
21
+ tokenKey: `${modName}::token`,
22
+ callbacks: [],
23
+ isLoginPresent: false,
24
+ };
25
+
26
+ // Actions
27
+ Object.entries({
28
+ 'auth:requestToken': requestToken,
29
+ 'auth:destroy': destroy,
30
+ }).forEach(([actionType, fn]) => {
31
+ const debug = (...args) => state.debug('[action]', actionType, ...args);
32
+ toolkit.dispatchAction({
33
+ type: 'action:register',
34
+ detail: {
35
+ actionType,
36
+ modName,
37
+ handler: (...args) => {
38
+ debug('handler', ...args);
39
+ fn(state, ...args);
40
+ },
41
+ },
15
42
  });
43
+ });
44
+
45
+ // Auth Layout - secure layout containing only the auth forms
46
+ toolkit.dispatchAction({
47
+ type: 'layout:register',
48
+ detail: {
49
+ name: `${modName}::auth-layout`,
50
+ templateFn: authLayout,
51
+ },
52
+ });
53
+
54
+ if (forceLogin) {
55
+ dispatchTokenRequest(mod.element);
56
+ }
57
+
58
+ }
59
+
60
+ /**
61
+ * Handler for the `auth:requestToken` action
62
+ */
63
+ async function requestToken(state, eventDetail) {
64
+ const { callback } = eventDetail;
65
+ let token;
66
+ try {
67
+ token = await getFreshToken(state);
68
+ callback(token);
69
+ } catch (err) {
70
+ console.log('Presenting login form to get a fresh token', err);
71
+ state.callbacks.push(callback);
72
+ if (!state.isLoginPresent) {
73
+ presentLoginForm(state);
74
+ }
16
75
  }
76
+
77
+ };
78
+
79
+
80
+ function destroy(state) {
81
+ destroyToken(state);
82
+ // TODO: destroy the layouts and present a low-security layout
83
+ }
84
+
85
+ function presentLoginForm(state) {
86
+ state.isLoginPresent = true;
87
+ state.toolkit.dispatchAction({
88
+ type: 'layout:push',
89
+ detail: {
90
+ name: `${state.modName}::auth-layout`,
91
+ },
92
+ });
93
+ state.toolkit.dispatchAction({
94
+ type: 'layout:content:append',
95
+ detail: {
96
+ content: loginForm(state, loginSuccessHandler(state)),
97
+ },
98
+ });
17
99
  }
100
+
101
+ function loginSuccessHandler(state) {
102
+ return (event) => {
103
+ const { token } = event.detail;
104
+ setToken(state, token);
105
+ state.callbacks.forEach(callback => callback(token));
106
+ state.callbacks = [];
107
+ state.isLoginPresent = false;
108
+ state.toolkit.dispatchAction({
109
+ type: 'layout:pop',
110
+ });
111
+ };
112
+ }
113
+
package/src/layout.mjs ADDED
@@ -0,0 +1,38 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ export class ModAuthLayout extends LitElement {
4
+ static get styles() {
5
+ return css`
6
+ .login-layout {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 1rem;
10
+ justify-content: center;
11
+ align-items: center;
12
+ min-height: 100vh;
13
+ background-color: var(--color-gray-100);
14
+ }
15
+ .login-layout > * {
16
+ max-width: 400px;
17
+ }
18
+ `;
19
+ }
20
+
21
+ render() {
22
+ return html`
23
+ <div class="login-layout">
24
+ <slot name="main"></slot>
25
+ </div>
26
+ `;
27
+ }
28
+ }
29
+
30
+ if (customElements.get('mod-auth-layout') === undefined) {
31
+ customElements.define('mod-auth-layout', ModAuthLayout);
32
+ }
33
+
34
+ export function templateFn() {
35
+ return html`<mod-auth-layout></mod-auth-layout>`;
36
+ }
37
+
38
+ export default templateFn;