5htp-core 0.2.3 → 0.2.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.2.3",
4
+ "version": "0.2.4",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -102,11 +102,7 @@ export default abstract class Application {
102
102
  })
103
103
  }
104
104
 
105
- public handleError( error: CoreError | Error, httpCode?: number ) {
106
-
107
- /*console.error(`[api] Network error:`, e);
108
- context.toast.error("Please check your internet connection and try again.", undefined, null, { autohide: false });*/
109
- }
105
+ public abstract handleError( error: CoreError | Error, httpCode?: number );
110
106
 
111
107
  public reportBug = (infos: TBugReportInfos) => fetch('/help/bug/gui', {
112
108
  method: 'POST',
@@ -48,10 +48,10 @@ i.solid {
48
48
  }
49
49
 
50
50
  .input.text {
51
- border: solid 0.25em #f5f5f5;
51
+ border: solid 0.25em #eee;
52
52
 
53
53
  &:hover {
54
- border-color: #eee;
54
+ border-color: #ddd;
55
55
  }
56
56
 
57
57
  &.focus {
@@ -151,7 +151,7 @@
151
151
  &-3 { flex: 3; }
152
152
 
153
153
  &.menu {
154
- gap: @spacing / 2;
154
+ gap: 1px;
155
155
  }
156
156
  }
157
157
 
@@ -149,7 +149,7 @@ export default ({
149
149
 
150
150
  </div>
151
151
  ) : (
152
- <div class={"card pd-2 col"} style={width === undefined
152
+ <div class={"card pd-2 col al-top"} style={width === undefined
153
153
  ? {}
154
154
  : { minWidth: width + "px", maxWidth: width + "px" }
155
155
  }>
@@ -82,8 +82,7 @@
82
82
  gap: @spacing;
83
83
  z-index: @modal-zindex;
84
84
 
85
- background: fade(#fff, 20%);
86
- backdrop-filter: blur(30px) saturate(2);
85
+ background: fade(#000, 20%);
87
86
  border-radius: @radius;
88
87
 
89
88
  // Desktop = vertically center the modal
@@ -109,6 +108,7 @@
109
108
  max-height: 100vh;
110
109
  box-shadow: none;
111
110
  padding: @spacing * 2;
111
+ overflow-y: auto;
112
112
 
113
113
  // Pas d'anim quand pas card,
114
114
  // Car peut contenir bcp d'elemnts => performance
@@ -25,6 +25,13 @@ export type Form<TFormData extends {} = {}> = {
25
25
  set: (data: Partial<TFormData>) => void,
26
26
  submit: (additionnalData?: Partial<TFormData>) => Promise<any>,
27
27
  fields: FieldsAttrs<TFormData>,
28
+ } & FormState
29
+
30
+ type FormState = {
31
+ isLoading: boolean,
32
+ errorsCount: number,
33
+ errors: {[fieldName: string]: string[]},
34
+ changed: boolean
28
35
  }
29
36
 
30
37
  /*----------------------------------
@@ -38,15 +45,16 @@ export default function useForm<TFormData extends {}>( schema: Schema<TFormData>
38
45
  const fields = React.useRef<FieldsAttrs<TFormData>>(null);
39
46
 
40
47
  const [data, setData] = React.useState<TFormData>( options.data || {} );
41
- const [state, setState] = React.useState({
48
+ const [state, setState] = React.useState<FormState>({
42
49
  isLoading: false,
43
50
  errorsCount: 0,
44
51
  errors: {},
45
- validated: false
52
+ changed: false
46
53
  });
47
54
 
55
+ // Validate data when it changes
48
56
  React.useEffect(() => {
49
- state.validated && validate(data);
57
+ state.changed && validate(data);
50
58
  }, [data]);
51
59
 
52
60
  /*----------------------------------
@@ -58,13 +66,10 @@ export default function useForm<TFormData extends {}>( schema: Schema<TFormData>
58
66
 
59
67
  // Update errors
60
68
  if (validated.nbErreurs !== state.errorsCount) {
61
- fields.current = null; // Update the fields definition
62
- setState( old => ({
63
- ...old,
69
+ rebuildFieldsAttrs({
64
70
  errorsCount: validated.nbErreurs,
65
71
  errors: validated.erreurs,
66
- validated: true
67
- }));
72
+ });
68
73
  }
69
74
 
70
75
  return validated;
@@ -84,14 +89,39 @@ export default function useForm<TFormData extends {}>( schema: Schema<TFormData>
84
89
  return options.submit(validated.values);
85
90
  }
86
91
 
87
- if (fields.current === null){
92
+ const rebuildFieldsAttrs = (newState: Partial<FormState> = {}) => {
93
+ // Force rebuilding the fields definition on the next state change
94
+ fields.current = null;
95
+ // Force state change
96
+ setState( old => ({
97
+ ...old,
98
+ ...newState,
99
+ changed: true
100
+ }));
101
+ }
102
+
103
+ // Rebuild the fields attrs when the schema changes
104
+ if (fields.current === null || Object.keys(schema).join(',') !== Object.keys(fields.current).join(',')){
88
105
  fields.current = {}
89
106
  for (const fieldName in schema.fields) {
90
107
  fields.current[fieldName] = {
91
108
 
92
109
  // Value control
93
110
  value: data[fieldName],
94
- onChange: (val) => setData( old => ({ ...old, [fieldName]: val })),
111
+ onChange: (val) => {
112
+ setState( old => ({
113
+ ...old,
114
+ changed: true
115
+ }));
116
+ setData( old => {
117
+ return {
118
+ ...old,
119
+ [fieldName]: typeof val === 'function'
120
+ ? val(old[fieldName])
121
+ : val
122
+ }
123
+ })
124
+ },
95
125
 
96
126
  // Submit on press enter
97
127
  onKeyDown: e => {
@@ -102,7 +132,8 @@ export default function useForm<TFormData extends {}>( schema: Schema<TFormData>
102
132
 
103
133
  // Error
104
134
  errors: state.errors[ fieldName ],
105
- required: schema.fields[ fieldName ].options?.opt !== true
135
+ required: schema.fields[ fieldName ].options?.opt !== true,
136
+ validator: schema.fields[ fieldName ]
106
137
  }
107
138
  }
108
139
  }
@@ -4,9 +4,12 @@
4
4
 
5
5
  // Npm
6
6
  import React from 'react';
7
+ import type { ComponentChild } from 'preact';
8
+ import type { StateUpdater } from 'preact/hooks';
7
9
 
8
10
  // Core
9
11
  import Button from '@client/components/button';
12
+ import String from '@client/components/inputv3/string';
10
13
  import Dropdown, { TDialogControls, Props as DropdownProps } from '@client/components/dropdown';
11
14
 
12
15
  /*----------------------------------
@@ -17,42 +20,174 @@ import Dropdown, { TDialogControls, Props as DropdownProps } from '@client/compo
17
20
  - TYPES
18
21
  ----------------------------------*/
19
22
 
20
- type Choices = string[]
23
+ export type Choice = { label: ComponentChild, value: string }
21
24
 
22
- type SearchResultsFunction = (keywords: string) => Choices
25
+ export type Choices = Choice[]
23
26
 
24
- export type Props = DropdownProps & {
27
+ type ChoicesFunc = (search: string) => Promise<Choices>
28
+
29
+ type SelectorProps = (
30
+ {
31
+ multiple: true,
32
+ value?: Choice[],
33
+ onChange: StateUpdater<Choice[]>,
34
+ validator?: ArrayValidator
35
+ }
36
+ |
37
+ {
38
+ multiple?: false,
39
+ value?: Choice,
40
+ onChange: StateUpdater<Choice>,
41
+ validator?: StringValidator
42
+ }
43
+ ) & {
44
+ choices: Choices | ChoicesFunc,
45
+ enableSearch?: boolean,
46
+ inline?: boolean,
47
+ errors?: string[],
48
+ required?: boolean,
49
+ noneSelection?: false | string
50
+ }
51
+
52
+ export type Props = DropdownProps & SelectorProps & {
25
53
  title: string,
26
- choices: Choices,
27
- value?: string,
28
- onChange: (value: string) => void,
29
- search?: true | SearchResultsFunction
30
54
  }
31
55
 
32
56
  /*----------------------------------
33
57
  - COMONENT
34
58
  ----------------------------------*/
59
+ export default ({
60
+ title, choices: initChoices, errors, validator, required, noneSelection, enableSearch, value: current,
61
+ onChange, inline, multiple,
62
+ ...props
63
+ }: Props) => {
35
64
 
36
- export default ({ title, choices, value, onChange, ...dropDownProps }: Props) => {
37
65
  const refModal = React.useRef<TDialogControls>(null);
38
- return (
39
- <Dropdown {...dropDownProps} content={(
40
- <ul class="card col menu">
41
- {choices.map( jt => (
42
- <li>
43
- <Button active={jt === value} onClick={() => {
44
- onChange(jt);
45
- refModal.current?.close();
46
- }}>
47
- {jt}
66
+
67
+ /*----------------------------------
68
+ - INIT
69
+ ----------------------------------*/
70
+
71
+ const choicesViaFunc = typeof initChoices === 'function';
72
+ if (choicesViaFunc && enableSearch === undefined)
73
+ enableSearch = true;
74
+
75
+ const [search, setSearch] = React.useState<{
76
+ keywords: string,
77
+ loading: boolean
78
+ }>({
79
+ keywords: '',
80
+ loading: choicesViaFunc
81
+ });
82
+
83
+ const [choices, setChoices] = React.useState<Choices>([]);
84
+
85
+ /*----------------------------------
86
+ - ACTIONS
87
+ ----------------------------------*/
88
+ React.useEffect(() => {
89
+ if (choicesViaFunc) {
90
+ initChoices(search.keywords).then((searchResults) => {
91
+ setSearch(s => ({ ...s, loading: false }))
92
+ setChoices(searchResults);
93
+ })
94
+ }
95
+ }, [initChoices, search.keywords]);
96
+
97
+ const currentList: Choice[] = current === undefined
98
+ ? []
99
+ : (Array.isArray(current) ? current : [current]);
100
+
101
+ /*----------------------------------
102
+ - RENDER
103
+ ----------------------------------*/
104
+
105
+ const selector = (
106
+ <div class={(inline ? '' : 'card ') + "col"}>
107
+
108
+ {enableSearch && (
109
+ <String icon="search"
110
+ title="Search"
111
+ value={search.keywords}
112
+ onChange={keywords => setSearch(s => ({ ...s, loading: true, keywords }))}
113
+ iconR={'spin'}
114
+ />
115
+ )}
116
+
117
+ {currentList.length !== 0 && (
118
+ <ul class="col menu">
119
+ {currentList.map(choice => (
120
+ <Button size="s" onClick={() => {
121
+ onChange( current => multiple
122
+ ? current.filter(c => c.value !== choice.value)
123
+ : undefined
124
+ );
125
+ }} suffix={<i src="check" class="fg primary" />}>
126
+ {choice.label}
48
127
  </Button>
49
- </li>
50
- ))}
51
- </ul>
52
- )} iconR="chevron-down" refModal={refModal}>
128
+ ))}
129
+ </ul>
130
+ )}
131
+
132
+ {choices === null ? (
133
+ <div class="row h-3 al-center">
134
+ <i src="spin" />
135
+ </div>
136
+ ) : (
137
+ <ul class="col menu">
138
+ {choices.map( choice => {
139
+
140
+ const isCurrent = currentList.some(c => c.value === choice.value);
53
141
 
54
- {value || title}
142
+ return !isCurrent && (
143
+ <li>
144
+ <Button size="s" onClick={() => {
145
+ onChange( current => {
146
+ return multiple
147
+ ? [...(current || []), choice]
148
+ : choice
149
+ });
150
+ }}>
151
+ {/*search.keywords ? (
152
+ <span>
153
+
154
+ <strong>{search.keywords}</strong>{choice.label.slice( search.keywords.length )}
155
+
156
+ </span>
157
+ ) : */choice.label}
158
+ </Button>
159
+ </li>
160
+ )
161
+ })}
55
162
 
56
- </Dropdown>
163
+ {((!required || !validator?.options.min) && noneSelection) && (
164
+ <li>
165
+ <Button size="s" onClick={() => onChange(multiple ? [] : undefined)}
166
+ suffix={(current === undefined || (multiple && current.length === 0)) && <i src="check" class="fg primary" />}>
167
+ {noneSelection}
168
+ </Button>
169
+ </li>
170
+ )}
171
+ </ul>
172
+ )}
173
+ </div>
57
174
  )
175
+
176
+ return <>
177
+ {inline ? selector : (
178
+ <Dropdown {...props} content={selector} iconR="chevron-down" refModal={refModal}>
179
+
180
+ {currentList.length === 0
181
+ ? title
182
+ : currentList.map(choice => choice.label).join(', ')}
183
+
184
+ </Dropdown>
185
+ )}
186
+
187
+ {errors?.length && (
188
+ <div class="fg error txt-left">
189
+ {errors.join('. ')}
190
+ </div>
191
+ )}
192
+ </>
58
193
  }
@@ -9,15 +9,14 @@ import { ComponentChild, RefObject } from 'preact';
9
9
  // Core
10
10
  import Button, { Props as ButtonProps } from '../button';
11
11
  import { TDialogControls } from '../Dialog/Manager';
12
- export type { TDialogControls } from '../Dialog/Manager';
13
-
14
- // Libs
15
- import useContexte from '@/client/context';
12
+ import Popover from '../containers/Popover';
16
13
 
17
14
  /*----------------------------------
18
15
  - TYPES
19
16
  ----------------------------------*/
20
17
 
18
+ export type { TDialogControls } from '../Dialog/Manager';
19
+
21
20
  export type Props = ButtonProps & {
22
21
  content: ComponentChild,
23
22
  refModal?: RefObject<TDialogControls>
@@ -28,23 +27,19 @@ export type Props = ButtonProps & {
28
27
  ----------------------------------*/
29
28
  export default (props: Props) => {
30
29
 
31
- const { modal } = useContexte();
32
-
33
30
  let {
34
31
  content,
35
32
  refModal,
36
33
  ...buttonProps
37
34
  } = props;
38
35
 
39
- const refButton = React.useRef<HTMLElement>(null);
36
+ const popoverState = React.useState(false);
40
37
 
41
- const open = () => {
42
- const modalInstance = modal.show(() => content);
43
- if (refModal)
44
- refModal.current = modalInstance;
45
- }
38
+ const refButton = React.useRef<HTMLElement>(null);
46
39
 
47
40
  return (
48
- <Button {...buttonProps} onClick={(open)} refElem={refButton} />
41
+ <Popover content={content} state={popoverState}>
42
+ <Button {...buttonProps} refElem={refButton} />
43
+ </Popover>
49
44
  )
50
45
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  // Npm
6
6
  import React from 'react';
7
- import { ComponentChild } from 'preact';
7
+ import type { StateUpdater } from 'preact/hooks';
8
8
 
9
9
  // Core libs
10
10
  import { useState } from '@client/hooks';
@@ -20,7 +20,7 @@ export type InputBaseProps<TValue> = {
20
20
  errors?: string[],
21
21
 
22
22
  value: TValue,
23
- onChange?: (newValue: TValue) => void,
23
+ onChange?: StateUpdater<TValue>,
24
24
  }
25
25
 
26
26
  export type TInputState<TValue> = {
@@ -28,6 +28,7 @@ export type TInputState<TValue> = {
28
28
  fieldProps: {[key: string]: any},
29
29
  valueSource: 'internal'|'external',
30
30
  focus: boolean,
31
+ changed: boolean,
31
32
  }
32
33
 
33
34
  /*----------------------------------
@@ -47,12 +48,18 @@ export function useInput<TValue>(
47
48
  value: externalValue !== undefined ? externalValue : defaultValue,
48
49
  valueSource: 'external',
49
50
  fieldProps: {},
50
- focus: false
51
+ focus: false,
52
+ changed: false
51
53
  });
52
54
 
53
- const setValue = (value: TValue) => setState({ value, valueSource: 'internal' });
55
+ const setValue = (value: TValue) => setState({ value, valueSource: 'internal', changed: true });
54
56
 
55
57
  const commitValue = () => {
58
+
59
+ // Avoid to change parent component state at first render
60
+ if (state.changed === false)
61
+ return;
62
+
56
63
  console.log(`[input] Commit value:`, state.value);
57
64
  if (onChange !== undefined)
58
65
  onChange(state.value);
@@ -63,7 +70,7 @@ export function useInput<TValue>(
63
70
 
64
71
  if (externalValue !== undefined && externalValue !== state.value) {
65
72
  console.log("External value change", externalValue);
66
- setState({ value: externalValue, valueSource: 'external' })
73
+ setState({ value: externalValue, valueSource: 'external', changed: true })
67
74
  }
68
75
 
69
76
  }, [externalValue]);
@@ -91,7 +91,7 @@ export default ({
91
91
  } else if (type === 'longtext') {
92
92
 
93
93
  prefix = prefix || <i src="text" />;
94
- Tag = TextareaAutosize;
94
+ Tag = 'textarea'//TextareaAutosize;
95
95
 
96
96
  }
97
97
 
@@ -127,7 +127,7 @@ export default class ApiClient implements ApiClientService {
127
127
 
128
128
  return await this.fetch<TData>(method, path, data, options).catch((e) => {
129
129
  this.app.handleError(e);
130
- throw e;
130
+ throw e; // Throw to break the loop
131
131
  })
132
132
  }
133
133
 
@@ -158,6 +158,8 @@ export default class ApiClient implements ApiClientService {
158
158
 
159
159
  });
160
160
 
161
+ // Errors will be catched in the caller
162
+
161
163
  return { ...alreadyLoadedData, ...fetchedData }
162
164
  }
163
165
 
@@ -93,7 +93,7 @@ export default class Schema<TFields extends TSchemaFields> {
93
93
  opts.debug && console.warn(LogPrefix, '[' + champ + ']', 'Exclusion (pas présent dans le schéma)');
94
94
  continue;
95
95
  }
96
-
96
+
97
97
  const cheminA = [...chemin, champ]
98
98
  const cheminAstr = cheminA.join('.')
99
99
 
@@ -178,7 +178,7 @@ export default class Schema<TFields extends TSchemaFields> {
178
178
  if (nbErreurs !== 0 && opts.throwError === true) {
179
179
  throw new InputErrorSchema(erreurs);
180
180
  }
181
-
181
+
182
182
  opts.debug && console.log(LogPrefix, '', dataToValidate, '=>', output);
183
183
 
184
184
  return {
@@ -63,8 +63,10 @@ export default class SchemaValidator {
63
63
  return val;
64
64
  }, opts)
65
65
 
66
- public array = (subtype?: Validator<any>, { choice, ...opts }: TValidator<any[]> & {
67
- choice?: any[]
66
+ public array = (subtype?: Validator<any>, { choice, min, max, ...opts }: TValidator<any[]> & {
67
+ choice?: any[],
68
+ min?: number,
69
+ max?: number
68
70
  } = {}) => {
69
71
 
70
72
  if (subtype !== undefined)
@@ -72,12 +74,17 @@ export default class SchemaValidator {
72
74
 
73
75
  return new Validator<any[]>('array', (items, input, output, corriger) => {
74
76
 
75
- //console.log('VALIDER ARRAY', items, input);
76
-
77
+ // Type
77
78
  if (!Array.isArray(items))
78
- throw new InputError("This value must be an array.");
79
+ throw new InputError("This value must be a list.");
80
+
81
+ // Items number
82
+ if ((min !== undefined && items.length < min))
83
+ throw new InputError(`Please select at least ${min} items.`);
84
+ if ((max !== undefined && items.length > max))
85
+ throw new InputError(`Please select maximum ${max} items.`);
79
86
 
80
- // Verif items
87
+ // Verif each item
81
88
  if (subtype !== undefined) {
82
89
  if (false/*subtype instanceof Schema*/) {
83
90
 
@@ -85,9 +92,9 @@ export default class SchemaValidator {
85
92
 
86
93
  } else {
87
94
 
88
- for (let i = 0; i < items.length; i++)
89
- items[i] = subtype.validate( items[i], items, items, corriger );
90
-
95
+ items = items.map( item =>
96
+ subtype.validate( item, items, items, corriger )
97
+ )
91
98
  }
92
99
  }
93
100
 
@@ -101,7 +108,7 @@ export default class SchemaValidator {
101
108
  }
102
109
 
103
110
  public choice = (values: any[], opts: TValidator<any> & {} = {}) =>
104
- new Validator<any>('object', (val, input, output) => {
111
+ new Validator<any>('choice', (val, input, output) => {
105
112
 
106
113
  if (!values.includes(val))
107
114
  throw new InputError("Invalid value. Must be: " + values.join(', '));
@@ -116,6 +123,10 @@ export default class SchemaValidator {
116
123
  public string = ({ min, max, ...opts }: TValidator<string> & { min?: number, max?: number } = {}) =>
117
124
  new Validator<string>('string', (val, input, output, corriger?: boolean) => {
118
125
 
126
+ // Choice value from Select component
127
+ if (typeof val === 'object' && val.value)
128
+ val = val.value;
129
+
119
130
  if (val === '')
120
131
  return undefined;
121
132
  else if (typeof val === 'number')
@@ -170,7 +170,7 @@ export default class BugReporter {
170
170
  channelType,
171
171
  channelId,
172
172
  // User
173
- user: request?.user?.name,
173
+ user: request?.user?.email,
174
174
  ip: request?.ip,
175
175
  // Error
176
176
  error,
@@ -180,25 +180,8 @@ export default class BugReporter {
180
180
 
181
181
  await this.sendToTransporters(bugReport);
182
182
 
183
- // TODO: Move on App side
184
- /*if (app.isLoaded('sql'))
185
- // Memorize
186
- $.sql.insert('BugServer', {
187
- // Context
188
- hash: hash,
189
- date: now,
190
- channelType,
191
- channelId,
192
- // User
193
- user: request?.user?.name,
194
- ip: request?.ip,
195
- // Error
196
- stacktrace: error.stack || error.message,
197
- logs: logsHtml
198
- });*/
199
-
200
183
  // Update error message
201
- error.message = "A bug report has been sent to my personal mailbox. Sorry for the inconvenience.";
184
+ error.message = "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.";
202
185
  }
203
186
 
204
187
  public async application( report: AppBugInfos ) {
@@ -279,7 +279,7 @@ export default class SQL extends Service<Config, Hooks, Application> {
279
279
  public update<TData extends TObjetDonnees>(
280
280
  tableName: string,
281
281
  data: TData[],
282
- where: (keyof TData)[],
282
+ where?: (keyof TData)[],
283
283
  opts?: TUpdateQueryOptions<TData>
284
284
  );
285
285
 
@@ -287,30 +287,42 @@ export default class SQL extends Service<Config, Hooks, Application> {
287
287
  public update<TData extends TObjetDonnees>(
288
288
  tableName: string,
289
289
  data: TData,
290
- where: (keyof TData)[] | TObjetDonnees,
290
+ where?: (keyof TData)[] | TObjetDonnees,
291
291
  opts?: TUpdateQueryOptions<TData>
292
292
  );
293
293
 
294
294
  public update<TData extends TObjetDonnees>(...args: [
295
295
  tableName: string,
296
296
  data: TData[],
297
- where: (keyof TData)[],
297
+ where?: (keyof TData)[],
298
298
  opts?: TUpdateQueryOptions<TData>
299
299
  ] | [
300
300
  tableName: string,
301
301
  data: TData,
302
- where: (keyof TData)[] | TObjetDonnees,
302
+ where?: (keyof TData)[] | TObjetDonnees,
303
303
  opts?: TUpdateQueryOptions<TData>
304
- ]) {
304
+ ]): Promise<ResultSetHeader> {
305
305
 
306
306
  let [tableName, data, where, opts] = args;
307
307
 
308
308
  // Multiple updates in one
309
309
  if (Array.isArray( data ))
310
310
  return this.database.query(
311
- data.map(record => this.update(tableName, record, where, opts)).join(';\n')
311
+ data.map( record =>
312
+ this.update(tableName, record, where, { ...opts, returnQuery: true })
313
+ ).join(';\n')
312
314
  )
313
315
 
316
+ // Automatic where based on pks
317
+ if (where === undefined) {
318
+
319
+ const tableMetas = this.database.getTable(tableName);
320
+ if (tableMetas.pk.length === 0)
321
+ throw new Error(`Tried to build the where condition based on the pks list, but no pk is attached to this table ${tableMetas.chemin}`);
322
+
323
+ where = tableMetas.pk;
324
+ }
325
+
314
326
  // No condition specified = use the pks
315
327
  if (Array.isArray(where)) {
316
328
  const whereColNames = where;