@live-change/frontend-auto-form 0.9.198 → 0.9.200
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/README.md +139 -0
- package/front/src/components/crud/AutoObjectIdentification.vue +1 -1
- package/front/src/components/crud/InjectedObjectIndentification.vue +1 -1
- package/front/src/components/crud/ModelSingle.vue +3 -3
- package/front/src/components/crud/ObjectPath.vue +13 -9
- package/front/src/logic/actionData.js +2 -0
- package/front/src/logic/editorData.js +13 -4
- package/front/src/logic/relations.js +19 -9
- package/front/src/logic/validation.js +3 -3
- package/front/src/logic/viewData.js +3 -1
- package/package.json +13 -13
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: @live-change/frontend-auto-form
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## @live-change/frontend-auto-form
|
|
6
|
+
|
|
7
|
+
`@live-change/frontend-auto-form` to moduł do **automatycznego generowania formularzy i CRUD-ów** na podstawie:
|
|
8
|
+
|
|
9
|
+
- definicji modeli i akcji po stronie serwera (Live Change)
|
|
10
|
+
- metadanych wygenerowanych przez `@live-change/framework` i dostępnych przez `@live-change/vue3-ssr`
|
|
11
|
+
|
|
12
|
+
Zapewnia:
|
|
13
|
+
|
|
14
|
+
- komponenty **pól formularza** (`AutoField`, `AutoInput`, `AutoEditor`, `ModelEditor`, `ModelView`, `ModelSingle`, `ActionForm`, itp.)
|
|
15
|
+
- provider-y konfiguracji (`provideAutoViewComponents`, `provideAutoInputConfiguration`, `provideMetadataBasedAutoInputConfiguration`)
|
|
16
|
+
- lokalizacje (`locales`) używane w aplikacjach takich jak `family-tree` i `speed-dating`
|
|
17
|
+
|
|
18
|
+
### Podstawowa integracja
|
|
19
|
+
|
|
20
|
+
W `App.vue` typowej aplikacji włączasz auto-form globalnie:
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import {
|
|
24
|
+
provideAutoViewComponents,
|
|
25
|
+
provideAutoInputConfiguration,
|
|
26
|
+
provideMetadataBasedAutoInputConfiguration
|
|
27
|
+
} from '@live-change/frontend-auto-form'
|
|
28
|
+
|
|
29
|
+
provideAutoViewComponents()
|
|
30
|
+
provideAutoInputConfiguration()
|
|
31
|
+
provideMetadataBasedAutoInputConfiguration()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
oraz dodajesz lokalizacje do i18n w `front/src/config.js`:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
import { locales as autoFormLocales } from '@live-change/frontend-auto-form'
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
i18n: {
|
|
41
|
+
messages: {
|
|
42
|
+
en: deepmerge.all([
|
|
43
|
+
baseLocales.en,
|
|
44
|
+
autoFormLocales.en,
|
|
45
|
+
// ...
|
|
46
|
+
]),
|
|
47
|
+
pl: deepmerge.all([
|
|
48
|
+
baseLocales.pl || {},
|
|
49
|
+
autoFormLocales.pl || {},
|
|
50
|
+
// ...
|
|
51
|
+
])
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Kluczowe komponenty
|
|
58
|
+
|
|
59
|
+
#### `AutoField`
|
|
60
|
+
|
|
61
|
+
Generuje pojedyncze pole formularza na podstawie definicji właściwości modelu lub akcji:
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<AutoField
|
|
65
|
+
:definition="definition.properties.firstName"
|
|
66
|
+
v-model="editable.firstName"
|
|
67
|
+
:error="validationResult?.propertyErrors?.firstName"
|
|
68
|
+
:label="t('profile.firstName')"
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Możesz wstrzykiwać własny layout przez sloty (`#default`, `#label`, `#error`), jak w `ProfileSettings.vue` (`speed-dating`).
|
|
73
|
+
|
|
74
|
+
#### `AutoInput` i `AutoEditor`
|
|
75
|
+
|
|
76
|
+
`AutoInput` – pojedyncze pole edycyjne na podstawie definicji,
|
|
77
|
+
|
|
78
|
+
`AutoEditor` – edytor całego obiektu (modelu) na podstawie definicji:
|
|
79
|
+
|
|
80
|
+
```vue
|
|
81
|
+
<auto-editor
|
|
82
|
+
:definition="eventDefinition"
|
|
83
|
+
v-model="editable"
|
|
84
|
+
:rootValue="editable"
|
|
85
|
+
i18n="event."
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
W przykładzie z `speed-dating/front/src/pages/events/[event]/edit.vue`:
|
|
90
|
+
|
|
91
|
+
- `eventDefinition` pochodzi z `api.services.speedDating.models.Event`
|
|
92
|
+
- `editable` jest synchronizowane z widokiem i akcją poprzez `synchronized`
|
|
93
|
+
|
|
94
|
+
#### `ModelEditor`, `ModelView`, `ModelSingle`
|
|
95
|
+
|
|
96
|
+
Te komponenty łączą:
|
|
97
|
+
|
|
98
|
+
- definicję modelu
|
|
99
|
+
- widoki i akcje (DAO)
|
|
100
|
+
- generowane formularze i widoki list/szczegółów
|
|
101
|
+
|
|
102
|
+
Są używane m.in. w:
|
|
103
|
+
|
|
104
|
+
- `speed-dating/front/src/pages/events/[event]/surveys.vue` (`ModelEditor` dla ankiet)
|
|
105
|
+
- `family-tree` – ekrany ustawień i edytory szablonów
|
|
106
|
+
|
|
107
|
+
### Flow od modelu do CRUD
|
|
108
|
+
|
|
109
|
+
1. **Definicja modelu i akcji** w serwisie po stronie serwera (np. `Event`, `TreeSettings`).
|
|
110
|
+
2. **Eksport metadanych** przez Live Change i odczyt przez `@live-change/vue3-ssr` (`api.services[service].models[Model]`).
|
|
111
|
+
3. **Auto-form** generuje formularze i widoki:
|
|
112
|
+
- `AutoField` dla pojedynczych pól,
|
|
113
|
+
- `AutoEditor` / `ModelEditor` dla całych obiektów,
|
|
114
|
+
- `ActionForm` / `ActionEditor` dla akcji.
|
|
115
|
+
|
|
116
|
+
### Przykłady z projektów
|
|
117
|
+
|
|
118
|
+
#### `family-tree`
|
|
119
|
+
|
|
120
|
+
- `front/src/components/TreeSettings.vue`:
|
|
121
|
+
- `AutoField` użyty do edycji zagnieżdżonych ustawień (rootPerson, format, marginesy, tło, tytuł)
|
|
122
|
+
- integracja z PrimeVue (Dropdown, Slider, InputNumber) przez sloty
|
|
123
|
+
- `front/src/components/marketing/*Editor.vue`:
|
|
124
|
+
- `AutoField`, `editorData` do edycji szablonów i obrazów reklamowych
|
|
125
|
+
- `front/src/pages/tree/[tree]/settings.vue`:
|
|
126
|
+
- użycie `editorData` z `@live-change/frontend-auto-form` do spięcia formularza z modelem ustawień
|
|
127
|
+
|
|
128
|
+
#### `speed-dating`
|
|
129
|
+
|
|
130
|
+
- `front/src/components/profile/ProfileSettings.vue`:
|
|
131
|
+
- `AutoField` dla pól identyfikacji użytkownika, z własnymi layoutami i walidacją
|
|
132
|
+
- integracja z `synchronized` i serwisem `draft`
|
|
133
|
+
- `front/src/pages/events/[event]/edit.vue`:
|
|
134
|
+
- `AutoInput`, `AutoField`, `AutoEditor` do edycji eventu
|
|
135
|
+
- `front/src/pages/events/[event]/surveys.vue`:
|
|
136
|
+
- `ModelEditor` do edycji ankiet powiązanych z eventem
|
|
137
|
+
|
|
138
|
+
Te pliki są najlepszym odniesieniem, jeśli chcesz szybko zobaczyć, jak układa się flow od modelu do działającego CRUD-a.
|
|
139
|
+
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
required: true,
|
|
108
108
|
},
|
|
109
109
|
views: {
|
|
110
|
-
type:
|
|
111
|
-
default: () => ({})
|
|
110
|
+
type: Array,
|
|
111
|
+
default: () => ([{}])
|
|
112
112
|
},
|
|
113
113
|
})
|
|
114
114
|
const { service, model, views } = toRefs(props)
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
}, AutoObjectIdentification)
|
|
125
125
|
)
|
|
126
126
|
|
|
127
|
-
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
127
|
+
import { useApi, usePath, live, view } from '@live-change/vue3-ssr'
|
|
128
128
|
const api = useApi()
|
|
129
129
|
const path = usePath()
|
|
130
130
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<pre>selectedPaths = {{ selectedPaths }}</pre>
|
|
7
7
|
<pre>selectedPathWithElements = {{ selectedPathWithElements }}</pre> -->
|
|
8
8
|
<div v-for="path in selectedPathsWithElements" :key="path">
|
|
9
|
-
<Breadcrumb :model="more ? [...path, '
|
|
9
|
+
<Breadcrumb :model="more ? [...path, ...pathObjects, 'placeholder'] : [...path, ...pathObjects]"
|
|
10
10
|
:pt="{
|
|
11
11
|
list: {
|
|
12
12
|
class: 'text-sm flex flex-row flex-wrap gap-2 first:negative-margin-left pl-[1.5rem]'
|
|
@@ -46,9 +46,13 @@
|
|
|
46
46
|
more: {
|
|
47
47
|
type: Boolean,
|
|
48
48
|
default: false
|
|
49
|
+
},
|
|
50
|
+
pathObjects: {
|
|
51
|
+
type: Array,
|
|
52
|
+
default: () => []
|
|
49
53
|
}
|
|
50
54
|
})
|
|
51
|
-
const { objectType, object, more } = toRefs(props)
|
|
55
|
+
const { objectType, object, more, pathObjects } = toRefs(props)
|
|
52
56
|
|
|
53
57
|
import { usePath, live } from '@live-change/vue3-ssr'
|
|
54
58
|
const path = usePath()
|
|
@@ -87,7 +91,7 @@
|
|
|
87
91
|
} else {
|
|
88
92
|
elements.push({ objectType, object, propertyFrom: propertyName })
|
|
89
93
|
}
|
|
90
|
-
}
|
|
94
|
+
}
|
|
91
95
|
return elements
|
|
92
96
|
}
|
|
93
97
|
|
|
@@ -114,13 +118,13 @@
|
|
|
114
118
|
live(pathsPath)
|
|
115
119
|
])
|
|
116
120
|
|
|
117
|
-
const selectedPaths = computed(() =>
|
|
118
|
-
return objectPathConfig.scopeSelector(paths.value)
|
|
119
|
-
})
|
|
121
|
+
const selectedPaths = computed(() => objectPathConfig.scopeSelector(paths.value))
|
|
120
122
|
|
|
121
|
-
const selectedPathsWithElements = computed(
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
const selectedPathsWithElements = computed(() => {
|
|
124
|
+
const result = selectedPaths.value.map(scopePath => objectPathConfig.pathElements(scopePath))
|
|
125
|
+
if(result.length == 0) return [[{objectType: objectType.value, object: object.value}]]
|
|
126
|
+
return result
|
|
127
|
+
})
|
|
124
128
|
|
|
125
129
|
|
|
126
130
|
|
|
@@ -194,6 +194,7 @@ export default async function actionData(options) {
|
|
|
194
194
|
mergedInitialValue,
|
|
195
195
|
editableProperties,
|
|
196
196
|
value: synchronizedData.value,
|
|
197
|
+
data: synchronizedData.value,
|
|
197
198
|
changed,
|
|
198
199
|
submit,
|
|
199
200
|
submitting,
|
|
@@ -231,6 +232,7 @@ export default async function actionData(options) {
|
|
|
231
232
|
initialValue,
|
|
232
233
|
editableProperties,
|
|
233
234
|
value: formData,
|
|
235
|
+
data: formData,
|
|
234
236
|
changed,
|
|
235
237
|
submit,
|
|
236
238
|
submitting,
|
|
@@ -25,6 +25,7 @@ export default function editorData(options) {
|
|
|
25
25
|
recursive = true,
|
|
26
26
|
debounce = 600,
|
|
27
27
|
timeField = 'lastUpdate',
|
|
28
|
+
crudSource = 'crud',
|
|
28
29
|
|
|
29
30
|
savedToast = "Saved",
|
|
30
31
|
savedDraftToast = "Draft saved",
|
|
@@ -56,7 +57,7 @@ export default function editorData(options) {
|
|
|
56
57
|
const service = api.services[serviceName]
|
|
57
58
|
const model = service.models[modelName]
|
|
58
59
|
const {
|
|
59
|
-
crudMethods = model
|
|
60
|
+
crudMethods = model[crudSource],
|
|
60
61
|
identifiersNames = model.identifiers,
|
|
61
62
|
editableProperties = model.editableProperties ?? Object.keys(model.properties),
|
|
62
63
|
} = options
|
|
@@ -130,7 +131,8 @@ export default function editorData(options) {
|
|
|
130
131
|
const savedIdentifiers = {}
|
|
131
132
|
for(const identifier of identifiersNames) {
|
|
132
133
|
if(typeof identifier === 'object') {
|
|
133
|
-
savedIdentifiers[identifier.name] = savedData.value?.[identifier.
|
|
134
|
+
savedIdentifiers[identifier.name] = savedData.value?.[identifier.field] ?? savedData.value?.[identifier.name]
|
|
135
|
+
?? draftData.value?.data?.[identifier.field] ?? draftData.value?.data?.[identifier.name]
|
|
134
136
|
} else {
|
|
135
137
|
savedIdentifiers[identifier] = savedData.value?.[identifier] ?? draftData.value?.data?.[identifier]
|
|
136
138
|
}
|
|
@@ -209,7 +211,7 @@ export default function editorData(options) {
|
|
|
209
211
|
|
|
210
212
|
const propertiesErrors = computed(() => propertiesValidationErrors(
|
|
211
213
|
synchronizedData.value.value, identifiers, model, lastUploadedData.value,
|
|
212
|
-
|
|
214
|
+
propertiesServerErrors.value, appContext, editableProperties))
|
|
213
215
|
|
|
214
216
|
async function save() {
|
|
215
217
|
const saveResult = await saveData(synchronizedData.value.value)
|
|
@@ -240,6 +242,7 @@ export default function editorData(options) {
|
|
|
240
242
|
return {
|
|
241
243
|
identifiers,
|
|
242
244
|
value: synchronizedData.value,
|
|
245
|
+
data: synchronizedData.value,
|
|
243
246
|
changed,
|
|
244
247
|
save,
|
|
245
248
|
saving,
|
|
@@ -255,6 +258,8 @@ export default function editorData(options) {
|
|
|
255
258
|
saved: savedData,
|
|
256
259
|
savedPath: savedDataPath,
|
|
257
260
|
editableSavedData,
|
|
261
|
+
editableDraftData,
|
|
262
|
+
defaultData: defaultData(model),
|
|
258
263
|
draft: draftData,
|
|
259
264
|
sourceChanged /// needed for draft discard on concurrent save
|
|
260
265
|
}
|
|
@@ -281,7 +286,7 @@ export default function editorData(options) {
|
|
|
281
286
|
|
|
282
287
|
const propertiesErrors = computed(() => propertiesValidationErrors(
|
|
283
288
|
synchronizedData.value.value, identifiers, model, lastUploadedData.value,
|
|
284
|
-
propertiesServerErrors.value, appContext))
|
|
289
|
+
propertiesServerErrors.value, appContext, editableProperties))
|
|
285
290
|
|
|
286
291
|
async function reset() {
|
|
287
292
|
synchronizedData.value.value = editableSavedData.value || deepmerge(defaultData(model), initialData)
|
|
@@ -292,11 +297,15 @@ export default function editorData(options) {
|
|
|
292
297
|
return {
|
|
293
298
|
identifiers,
|
|
294
299
|
value: synchronizedData.value,
|
|
300
|
+
data: synchronizedData.value,
|
|
295
301
|
changed: synchronizedData.changed,
|
|
296
302
|
save: synchronizedData.save,
|
|
297
303
|
saving: synchronizedData.saving,
|
|
298
304
|
saved: savedData,
|
|
299
305
|
savedPath: savedDataPath,
|
|
306
|
+
editableProperties,
|
|
307
|
+
editableSavedData,
|
|
308
|
+
defaultData: defaultData(model),
|
|
300
309
|
isNew,
|
|
301
310
|
propertiesErrors,
|
|
302
311
|
reset,
|
|
@@ -196,7 +196,8 @@ export function prepareObjectRelations(objectType, object, api = useApi()) {
|
|
|
196
196
|
const name = 'rangeBy_' + objectType
|
|
197
197
|
const typeView = from.crud?.[name]
|
|
198
198
|
? name
|
|
199
|
-
: undefined
|
|
199
|
+
: undefined
|
|
200
|
+
|
|
200
201
|
if(typeView) {
|
|
201
202
|
views.push({
|
|
202
203
|
name: typeView,
|
|
@@ -205,18 +206,27 @@ export function prepareObjectRelations(objectType, object, api = useApi()) {
|
|
|
205
206
|
}
|
|
206
207
|
})
|
|
207
208
|
} else {
|
|
208
|
-
|
|
209
|
-
if(what[i] !== objectType) continue
|
|
210
|
-
const propertyName = relationConfig.propertyNames?.[i]
|
|
211
|
-
?? model[0].toLowerCase() + model.slice(1)
|
|
212
|
-
const name = 'rangeBy' + propertyName[0].toUpperCase() + propertyName.slice(1)
|
|
213
|
-
if(!from.crud?.[name]) continue
|
|
209
|
+
if(singular) {
|
|
214
210
|
views.push({
|
|
215
|
-
name,
|
|
211
|
+
name: 'read',
|
|
216
212
|
identifiers: {
|
|
217
|
-
[
|
|
213
|
+
[model[0].toLowerCase() + model.slice(1)]: object
|
|
218
214
|
}
|
|
219
215
|
})
|
|
216
|
+
} else {
|
|
217
|
+
for(let i = 0; i < what.length; i++) {
|
|
218
|
+
if(what[i] !== objectType) continue
|
|
219
|
+
const propertyName = relationConfig.propertyNames?.[i]
|
|
220
|
+
?? model[0].toLowerCase() + model.slice(1)
|
|
221
|
+
const name = 'rangeBy' + propertyName[0].toUpperCase() + propertyName.slice(1)
|
|
222
|
+
if(!from.crud?.[name]) continue
|
|
223
|
+
views.push({
|
|
224
|
+
name,
|
|
225
|
+
identifiers: {
|
|
226
|
+
[propertyName]: object
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}
|
|
220
230
|
}
|
|
221
231
|
}
|
|
222
232
|
console.log(objectType, "VIEWS", views, "FROM", from, "SINGULAR", singular)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validateData } from "@live-change/vue3-components"
|
|
2
2
|
|
|
3
|
-
export function propertiesValidationErrors(rootValue, parameters, definition, lastData, propertiesServerErrors, appContext) {
|
|
3
|
+
export function propertiesValidationErrors(rootValue, parameters, definition, lastData, propertiesServerErrors, appContext, validatedProperties) {
|
|
4
4
|
const currentValue = {
|
|
5
5
|
...parameters,
|
|
6
6
|
...rootValue,
|
|
@@ -10,10 +10,10 @@ export function propertiesValidationErrors(rootValue, parameters, definition, la
|
|
|
10
10
|
lastData, propertiesServerErrors, appContext) */
|
|
11
11
|
|
|
12
12
|
const validationResult = validateData(definition, currentValue, 'validation', appContext,
|
|
13
|
-
'', rootValue, true)
|
|
13
|
+
'', rootValue, true, validatedProperties)
|
|
14
14
|
|
|
15
15
|
const softValidationResult = validateData(definition, currentValue, 'softValidation', appContext,
|
|
16
|
-
'', rootValue, true)
|
|
16
|
+
'', rootValue, true, validatedProperties)
|
|
17
17
|
|
|
18
18
|
const serverValidationResult = {}
|
|
19
19
|
if(propertiesServerErrors) {
|
|
@@ -28,7 +28,9 @@ export default async function viewData(options) {
|
|
|
28
28
|
|
|
29
29
|
const savedDataPath = path[serviceName][crudMethods.read]({
|
|
30
30
|
[modelName[0].toLowerCase() + modelName.slice(1)]: id
|
|
31
|
-
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
console.log("SAVED DATA PATH", savedDataPath)
|
|
32
34
|
|
|
33
35
|
let data
|
|
34
36
|
let error
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/frontend-auto-form",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.200",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"memDev": "node server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
6
|
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
|
25
|
-
"@live-change/cli": "^0.9.
|
|
26
|
-
"@live-change/dao": "^0.9.
|
|
27
|
-
"@live-change/dao-vue3": "^0.9.
|
|
28
|
-
"@live-change/dao-websocket": "^0.9.
|
|
29
|
-
"@live-change/framework": "^0.9.
|
|
30
|
-
"@live-change/image-frontend": "^0.9.
|
|
31
|
-
"@live-change/image-service": "^0.9.
|
|
32
|
-
"@live-change/session-service": "^0.9.
|
|
33
|
-
"@live-change/vue3-components": "^0.9.
|
|
34
|
-
"@live-change/vue3-ssr": "^0.9.
|
|
25
|
+
"@live-change/cli": "^0.9.200",
|
|
26
|
+
"@live-change/dao": "^0.9.200",
|
|
27
|
+
"@live-change/dao-vue3": "^0.9.200",
|
|
28
|
+
"@live-change/dao-websocket": "^0.9.200",
|
|
29
|
+
"@live-change/framework": "^0.9.200",
|
|
30
|
+
"@live-change/image-frontend": "^0.9.200",
|
|
31
|
+
"@live-change/image-service": "^0.9.200",
|
|
32
|
+
"@live-change/session-service": "^0.9.200",
|
|
33
|
+
"@live-change/vue3-components": "^0.9.200",
|
|
34
|
+
"@live-change/vue3-ssr": "^0.9.200",
|
|
35
35
|
"@vueuse/core": "^12.3.0",
|
|
36
36
|
"codeceptjs-assert": "^0.0.5",
|
|
37
37
|
"compression": "^1.7.5",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"vue3-scroll-border": "0.1.7"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@live-change/codeceptjs-helper": "^0.9.
|
|
55
|
+
"@live-change/codeceptjs-helper": "^0.9.200",
|
|
56
56
|
"codeceptjs": "^3.7.6",
|
|
57
57
|
"generate-password": "1.7.1",
|
|
58
58
|
"playwright": "1.49.1",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"author": "Michał Łaszczewski <michal@laszczewski.pl>",
|
|
64
64
|
"license": "ISC",
|
|
65
65
|
"description": "",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "a509834e600a546297faa7d1534b6f52e66d2e66"
|
|
67
67
|
}
|