@dinoxx/dinox-cli 2.2.0 → 2.3.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/cli.js +4 -0
- package/dist/commands/domain/options.d.ts +10 -0
- package/dist/commands/domain/options.js +75 -0
- package/dist/commands/domain/run.d.ts +24 -0
- package/dist/commands/domain/run.js +137 -0
- package/dist/commands/notes/actions.js +47 -18
- package/dist/commands/notes/createFromTemplate.d.ts +35 -0
- package/dist/commands/notes/createFromTemplate.js +58 -0
- package/dist/commands/notes/descriptors.js +9 -4
- package/dist/commands/notes/repo/read.js +4 -23
- package/dist/commands/notes/repo/write.d.ts +2 -0
- package/dist/commands/notes/repo/write.js +1 -1
- package/dist/commands/properties/index.d.ts +16 -0
- package/dist/commands/properties/index.js +640 -0
- package/dist/commands/properties/repo.d.ts +150 -0
- package/dist/commands/properties/repo.js +272 -0
- package/dist/commands/savedViews/fields.d.ts +28 -0
- package/dist/commands/savedViews/fields.js +87 -0
- package/dist/commands/savedViews/index.js +203 -164
- package/dist/commands/savedViews/model.d.ts +27 -69
- package/dist/commands/savedViews/model.js +63 -279
- package/dist/commands/savedViews/repo.d.ts +64 -62
- package/dist/commands/savedViews/repo.js +309 -428
- package/dist/commands/schema/renderSkills.js +29 -5
- package/dist/commands/schema/spec.js +4 -0
- package/dist/commands/templates/index.d.ts +4 -0
- package/dist/commands/templates/index.js +290 -0
- package/dist/commands/templates/model.d.ts +12 -34
- package/dist/commands/templates/model.js +39 -83
- package/dist/commands/templates/repo.d.ts +53 -0
- package/dist/commands/templates/repo.js +78 -0
- package/dist/skills/registry.js +7 -0
- package/dist/utils/errorCodes.d.ts +212 -0
- package/dist/utils/errorCodes.js +53 -0
- package/dist/vendor/electron/domain/note-templates.d.ts +3 -0
- package/dist/vendor/electron/domain/note-templates.js +232 -0
- package/dist/vendor/electron/domain/properties.d.ts +10 -0
- package/dist/vendor/electron/domain/properties.js +1538 -0
- package/dist/vendor/electron/domain/saved-view-analytics.d.ts +45 -0
- package/dist/vendor/electron/domain/saved-view-analytics.js +365 -0
- package/dist/vendor/electron/domain/saved-view-relative-date.d.ts +21 -0
- package/dist/vendor/electron/domain/saved-view-relative-date.js +153 -0
- package/dist/vendor/electron/domain/saved-views.d.ts +61 -0
- package/dist/vendor/electron/domain/saved-views.js +1117 -0
- package/dist/vendor/electron/rpc.d.ts +22 -0
- package/dist/vendor/electron/rpc.js +27 -0
- package/dist/vendor/electron/shared/ai-errors.d.ts +11 -0
- package/dist/vendor/electron/shared/ai-errors.js +107 -0
- package/dist/vendor/electron/shared/external-types.d.ts +2 -0
- package/dist/vendor/electron/shared/external-types.js +1 -0
- package/dist/vendor/electron/shared/index.d.ts +9 -0
- package/dist/vendor/electron/shared/index.js +7 -0
- package/dist/vendor/electron/shared/note-properties.d.ts +312 -0
- package/dist/vendor/electron/shared/note-properties.js +982 -0
- package/dist/vendor/electron/shared/note-templates.d.ts +82 -0
- package/dist/vendor/electron/shared/note-templates.js +84 -0
- package/dist/vendor/electron/shared/powersync-read-models.d.ts +1710 -0
- package/dist/vendor/electron/shared/powersync-read-models.js +1 -0
- package/dist/vendor/electron/shared/saved-views.d.ts +394 -0
- package/dist/vendor/electron/shared/saved-views.js +981 -0
- package/dist/vendor/electron/sql-utils.d.ts +5 -0
- package/dist/vendor/electron/sql-utils.js +32 -0
- package/package.json +6 -3
- package/dist/commands/savedViews/semantics.d.ts +0 -46
- package/dist/commands/savedViews/semantics.js +0 -738
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
2
|
+
import { fetchDomainReadModel, previewInRolledBackTransaction, runDomainCommand, } from '../domain/run.js';
|
|
3
|
+
import { mapNoteTemplateRow } from './model.js';
|
|
4
|
+
function nowIso() {
|
|
5
|
+
return new Date().toISOString();
|
|
6
|
+
}
|
|
7
|
+
function notFound(id) {
|
|
8
|
+
return new DinoxError(`Note template not found: ${id}`, {
|
|
9
|
+
code: 'NOTE_TEMPLATE_NOT_FOUND',
|
|
10
|
+
details: { templateId: id },
|
|
11
|
+
suggestion: 'Run `dino template list --format json` to see template ids.',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export async function listNoteTemplates(db, input) {
|
|
15
|
+
const rows = await fetchDomainReadModel(db, 'noteTemplates.catalog', {
|
|
16
|
+
userId: input.userId,
|
|
17
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
18
|
+
});
|
|
19
|
+
return rows.map(mapNoteTemplateRow);
|
|
20
|
+
}
|
|
21
|
+
export async function getNoteTemplate(db, input) {
|
|
22
|
+
const row = await db.getOptional(`
|
|
23
|
+
SELECT id, user_id, name, content, priority, is_show, extra_data, property_def,
|
|
24
|
+
insert_at, update_at, is_del
|
|
25
|
+
FROM c_note_template
|
|
26
|
+
WHERE id = ? AND user_id = ? AND (is_del IS NULL OR is_del = 0)
|
|
27
|
+
LIMIT 1
|
|
28
|
+
`, [input.id.trim(), input.userId]);
|
|
29
|
+
if (!row)
|
|
30
|
+
throw notFound(input.id);
|
|
31
|
+
return mapNoteTemplateRow(row);
|
|
32
|
+
}
|
|
33
|
+
async function runCreate(db, input) {
|
|
34
|
+
const { result: id } = await runDomainCommand(db, 'noteTemplates.create', {
|
|
35
|
+
userId: input.userId,
|
|
36
|
+
name: input.name,
|
|
37
|
+
propertyKeys: input.propertyKeys ?? [],
|
|
38
|
+
...(input.content === undefined ? {} : { content: input.content }),
|
|
39
|
+
updatedAt: nowIso(),
|
|
40
|
+
});
|
|
41
|
+
return getNoteTemplate(db, { userId: input.userId, id });
|
|
42
|
+
}
|
|
43
|
+
async function runUpdate(db, input) {
|
|
44
|
+
await getNoteTemplate(db, input);
|
|
45
|
+
await runDomainCommand(db, 'noteTemplates.update', {
|
|
46
|
+
id: input.id,
|
|
47
|
+
userId: input.userId,
|
|
48
|
+
...(input.name === undefined ? {} : { name: input.name }),
|
|
49
|
+
...(input.propertyKeys === undefined ? {} : { propertyKeys: input.propertyKeys }),
|
|
50
|
+
...(input.content === undefined ? {} : { content: input.content }),
|
|
51
|
+
updatedAt: nowIso(),
|
|
52
|
+
});
|
|
53
|
+
return getNoteTemplate(db, input);
|
|
54
|
+
}
|
|
55
|
+
async function runDelete(db, input) {
|
|
56
|
+
const before = await getNoteTemplate(db, input);
|
|
57
|
+
const { result: changed } = await runDomainCommand(db, 'noteTemplates.softDelete', {
|
|
58
|
+
id: input.id,
|
|
59
|
+
userId: input.userId,
|
|
60
|
+
updatedAt: nowIso(),
|
|
61
|
+
});
|
|
62
|
+
if (!changed)
|
|
63
|
+
throw notFound(input.id);
|
|
64
|
+
return before;
|
|
65
|
+
}
|
|
66
|
+
export const createNoteTemplate = runCreate;
|
|
67
|
+
export const updateNoteTemplate = runUpdate;
|
|
68
|
+
export const deleteNoteTemplate = runDelete;
|
|
69
|
+
export async function previewCreateNoteTemplate(db, input) {
|
|
70
|
+
return previewInRolledBackTransaction(db, (tx) => runCreate(tx, input), async (_tx, after) => ({ before: null, after }));
|
|
71
|
+
}
|
|
72
|
+
export async function previewUpdateNoteTemplate(db, input) {
|
|
73
|
+
const before = await getNoteTemplate(db, input);
|
|
74
|
+
return previewInRolledBackTransaction(db, (tx) => runUpdate(tx, input), async (_tx, after) => ({ before, after }));
|
|
75
|
+
}
|
|
76
|
+
export async function previewDeleteNoteTemplate(db, input) {
|
|
77
|
+
return previewInRolledBackTransaction(db, (tx) => runDelete(tx, input), async (_tx, before) => ({ before, after: null }));
|
|
78
|
+
}
|
package/dist/skills/registry.js
CHANGED
|
@@ -18,6 +18,13 @@ const REGISTRY_ENTRIES = [
|
|
|
18
18
|
name: 'dino-manage-boxes',
|
|
19
19
|
generated: [commands('SKILL.md', 'skill:dino-manage-boxes')],
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
name: 'dino-manage-properties',
|
|
23
|
+
generated: [
|
|
24
|
+
commands('SKILL.md', 'skill:dino-manage-properties'),
|
|
25
|
+
commands('references/templates.md', 'skill:dino-manage-properties:templates'),
|
|
26
|
+
],
|
|
27
|
+
},
|
|
21
28
|
{
|
|
22
29
|
name: 'dino-manage-prompts',
|
|
23
30
|
generated: [commands('SKILL.md', 'skill:dino-manage-prompts')],
|
|
@@ -95,6 +95,18 @@ export declare const ERROR_CODES: {
|
|
|
95
95
|
readonly exitCode: 2;
|
|
96
96
|
readonly description: "An argument or option value is invalid.";
|
|
97
97
|
};
|
|
98
|
+
readonly INVALID_PROPERTY_DEF_CONFIG: {
|
|
99
|
+
readonly exitCode: 1;
|
|
100
|
+
readonly description: "A property definition config could not be serialized.";
|
|
101
|
+
};
|
|
102
|
+
readonly INVALID_PROPERTY_DEF_ID: {
|
|
103
|
+
readonly exitCode: 1;
|
|
104
|
+
readonly description: "A property definition id is missing or blank.";
|
|
105
|
+
};
|
|
106
|
+
readonly INVALID_PROPERTY_OPTIONS: {
|
|
107
|
+
readonly exitCode: 1;
|
|
108
|
+
readonly description: "Property options could not be serialized.";
|
|
109
|
+
};
|
|
98
110
|
readonly INVALID_STORAGE_KEY: {
|
|
99
111
|
readonly exitCode: 2;
|
|
100
112
|
readonly description: "The storage object key is invalid.";
|
|
@@ -107,6 +119,22 @@ export declare const ERROR_CODES: {
|
|
|
107
119
|
readonly exitCode: 5;
|
|
108
120
|
readonly description: "The note does not exist or is deleted.";
|
|
109
121
|
};
|
|
122
|
+
readonly NOTE_PROPERTIES_CORRUPT: {
|
|
123
|
+
readonly exitCode: 1;
|
|
124
|
+
readonly description: "The note properties JSON is corrupt and cannot be mutated safely.";
|
|
125
|
+
};
|
|
126
|
+
readonly NOTE_PROPERTIES_INVALID: {
|
|
127
|
+
readonly exitCode: 1;
|
|
128
|
+
readonly description: "The note properties mutation is invalid.";
|
|
129
|
+
};
|
|
130
|
+
readonly NOTE_PROPERTIES_MUTATION_FAILED: {
|
|
131
|
+
readonly exitCode: 1;
|
|
132
|
+
readonly description: "The note properties mutation could not be applied.";
|
|
133
|
+
};
|
|
134
|
+
readonly NOTE_PROPERTY_ORDER_STALE: {
|
|
135
|
+
readonly exitCode: 5;
|
|
136
|
+
readonly description: "The property slot order no longer matches the note; re-read and retry.";
|
|
137
|
+
};
|
|
110
138
|
readonly NOTE_TEMPLATE_INVALID_CONTENT: {
|
|
111
139
|
readonly exitCode: 1;
|
|
112
140
|
readonly description: "The note template content is invalid.";
|
|
@@ -119,6 +147,10 @@ export declare const ERROR_CODES: {
|
|
|
119
147
|
readonly exitCode: 1;
|
|
120
148
|
readonly description: "A note template token is used in an invalid context.";
|
|
121
149
|
};
|
|
150
|
+
readonly NOTE_TEMPLATE_NOT_FOUND: {
|
|
151
|
+
readonly exitCode: 5;
|
|
152
|
+
readonly description: "The note template does not exist or is deleted.";
|
|
153
|
+
};
|
|
122
154
|
readonly NOTE_TEMPLATE_UNSUPPORTED_PROPERTY_DEF_VERSION: {
|
|
123
155
|
readonly exitCode: 1;
|
|
124
156
|
readonly description: "The note template property definition version is unsupported.";
|
|
@@ -135,6 +167,166 @@ export declare const ERROR_CODES: {
|
|
|
135
167
|
readonly exitCode: 5;
|
|
136
168
|
readonly description: "A safety precondition for the operation did not hold.";
|
|
137
169
|
};
|
|
170
|
+
readonly PROPERTY_DATE_INVALID: {
|
|
171
|
+
readonly exitCode: 1;
|
|
172
|
+
readonly description: "A date property value is not a valid date.";
|
|
173
|
+
};
|
|
174
|
+
readonly PROPERTY_DEF_CONFIG_INVALID: {
|
|
175
|
+
readonly exitCode: 1;
|
|
176
|
+
readonly description: "A property definition config is invalid.";
|
|
177
|
+
};
|
|
178
|
+
readonly PROPERTY_DEF_CONFIG_TYPE_MISMATCH: {
|
|
179
|
+
readonly exitCode: 1;
|
|
180
|
+
readonly description: "A property definition config does not match the property type.";
|
|
181
|
+
};
|
|
182
|
+
readonly PROPERTY_DEF_ENSURE_FAILED: {
|
|
183
|
+
readonly exitCode: 1;
|
|
184
|
+
readonly description: "The property definition could not be created or reused.";
|
|
185
|
+
};
|
|
186
|
+
readonly PROPERTY_DEF_ENSURE_INPUT_INVALID: {
|
|
187
|
+
readonly exitCode: 1;
|
|
188
|
+
readonly description: "The property definition create input is invalid.";
|
|
189
|
+
};
|
|
190
|
+
readonly PROPERTY_DEF_NOT_FOUND: {
|
|
191
|
+
readonly exitCode: 5;
|
|
192
|
+
readonly description: "The property definition does not exist or is deleted.";
|
|
193
|
+
};
|
|
194
|
+
readonly PROPERTY_DEF_TYPE_CONFLICT: {
|
|
195
|
+
readonly exitCode: 5;
|
|
196
|
+
readonly description: "A property with this key already exists with a different type.";
|
|
197
|
+
};
|
|
198
|
+
readonly PROPERTY_DEF_TYPE_INVALID: {
|
|
199
|
+
readonly exitCode: 1;
|
|
200
|
+
readonly description: "The stored property definition type is invalid.";
|
|
201
|
+
};
|
|
202
|
+
readonly PROPERTY_FILES_INVALID: {
|
|
203
|
+
readonly exitCode: 1;
|
|
204
|
+
readonly description: "A files property value is invalid.";
|
|
205
|
+
};
|
|
206
|
+
readonly PROPERTY_LEGACY_OPTION_LABEL_INVALID: {
|
|
207
|
+
readonly exitCode: 1;
|
|
208
|
+
readonly description: "A legacy option label cannot be migrated.";
|
|
209
|
+
};
|
|
210
|
+
readonly PROPERTY_LEGACY_OPTION_NOT_PLANNED: {
|
|
211
|
+
readonly exitCode: 1;
|
|
212
|
+
readonly description: "A legacy option value was not covered by the migration plan.";
|
|
213
|
+
};
|
|
214
|
+
readonly PROPERTY_LEGACY_VALUE_INVALID: {
|
|
215
|
+
readonly exitCode: 1;
|
|
216
|
+
readonly description: "A legacy option value cannot be migrated.";
|
|
217
|
+
};
|
|
218
|
+
readonly PROPERTY_MULTI_SELECT_EMPTY: {
|
|
219
|
+
readonly exitCode: 1;
|
|
220
|
+
readonly description: "A multi-select property value is empty.";
|
|
221
|
+
};
|
|
222
|
+
readonly PROPERTY_MULTI_SELECT_LIMIT_EXCEEDED: {
|
|
223
|
+
readonly exitCode: 1;
|
|
224
|
+
readonly description: "A multi-select property value has too many selections.";
|
|
225
|
+
};
|
|
226
|
+
readonly PROPERTY_NUMBER_INVALID: {
|
|
227
|
+
readonly exitCode: 1;
|
|
228
|
+
readonly description: "A number property value is invalid or out of range.";
|
|
229
|
+
};
|
|
230
|
+
readonly PROPERTY_OPTIONS_INVALID: {
|
|
231
|
+
readonly exitCode: 1;
|
|
232
|
+
readonly description: "Stored property options are invalid.";
|
|
233
|
+
};
|
|
234
|
+
readonly PROPERTY_OPTIONS_MIGRATION_REQUIRED: {
|
|
235
|
+
readonly exitCode: 5;
|
|
236
|
+
readonly description: "Legacy property options must be migrated first (dino prop migrate-options).";
|
|
237
|
+
};
|
|
238
|
+
readonly PROPERTY_OPTIONS_TYPE_MISMATCH: {
|
|
239
|
+
readonly exitCode: 1;
|
|
240
|
+
readonly description: "Options were supplied for a property type that has none.";
|
|
241
|
+
};
|
|
242
|
+
readonly PROPERTY_OPTION_ARCHIVED: {
|
|
243
|
+
readonly exitCode: 5;
|
|
244
|
+
readonly description: "The select option is archived and cannot be assigned.";
|
|
245
|
+
};
|
|
246
|
+
readonly PROPERTY_OPTION_ID_DUPLICATE: {
|
|
247
|
+
readonly exitCode: 1;
|
|
248
|
+
readonly description: "Property options contain duplicate ids.";
|
|
249
|
+
};
|
|
250
|
+
readonly PROPERTY_OPTION_ID_UNKNOWN: {
|
|
251
|
+
readonly exitCode: 1;
|
|
252
|
+
readonly description: "A property option id does not exist on the definition.";
|
|
253
|
+
};
|
|
254
|
+
readonly PROPERTY_OPTION_LABEL_INVALID: {
|
|
255
|
+
readonly exitCode: 1;
|
|
256
|
+
readonly description: "A property option label is blank, too long, or duplicated.";
|
|
257
|
+
};
|
|
258
|
+
readonly PROPERTY_OPTION_LIMIT_EXCEEDED: {
|
|
259
|
+
readonly exitCode: 1;
|
|
260
|
+
readonly description: "The property has too many options.";
|
|
261
|
+
};
|
|
262
|
+
readonly PROPERTY_OPTION_NEW_ID_FORBIDDEN: {
|
|
263
|
+
readonly exitCode: 1;
|
|
264
|
+
readonly description: "New property options must not carry an id.";
|
|
265
|
+
};
|
|
266
|
+
readonly PROPERTY_OPTION_NOT_FOUND: {
|
|
267
|
+
readonly exitCode: 5;
|
|
268
|
+
readonly description: "The select option does not exist on a closed property.";
|
|
269
|
+
};
|
|
270
|
+
readonly PROPERTY_OPTION_REMOVAL_REQUIRES_ARCHIVE: {
|
|
271
|
+
readonly exitCode: 1;
|
|
272
|
+
readonly description: "Existing property options must be archived instead of removed.";
|
|
273
|
+
};
|
|
274
|
+
readonly PROPERTY_OPTION_REPAIR_ID_FORBIDDEN: {
|
|
275
|
+
readonly exitCode: 1;
|
|
276
|
+
readonly description: "Repair options must not carry ids.";
|
|
277
|
+
};
|
|
278
|
+
readonly PROPERTY_OPTION_SORT_RANK_EXHAUSTED: {
|
|
279
|
+
readonly exitCode: 1;
|
|
280
|
+
readonly description: "No sort rank is left for a new property option.";
|
|
281
|
+
};
|
|
282
|
+
readonly PROPERTY_OPTION_VALUE_INVALID: {
|
|
283
|
+
readonly exitCode: 1;
|
|
284
|
+
readonly description: "A select property value is invalid.";
|
|
285
|
+
};
|
|
286
|
+
readonly PROPERTY_PLACE_INVALID: {
|
|
287
|
+
readonly exitCode: 1;
|
|
288
|
+
readonly description: "A place property value is invalid.";
|
|
289
|
+
};
|
|
290
|
+
readonly PROPERTY_RELATION_EMPTY: {
|
|
291
|
+
readonly exitCode: 1;
|
|
292
|
+
readonly description: "A relation property value is empty.";
|
|
293
|
+
};
|
|
294
|
+
readonly PROPERTY_RELATION_INVALID: {
|
|
295
|
+
readonly exitCode: 1;
|
|
296
|
+
readonly description: "A relation property value references invalid notes.";
|
|
297
|
+
};
|
|
298
|
+
readonly PROPERTY_RELATION_LIMIT: {
|
|
299
|
+
readonly exitCode: 1;
|
|
300
|
+
readonly description: "A relation property value has too many notes.";
|
|
301
|
+
};
|
|
302
|
+
readonly PROPERTY_TYPE_NOT_ACTIVE: {
|
|
303
|
+
readonly exitCode: 1;
|
|
304
|
+
readonly description: "The property type is not active.";
|
|
305
|
+
};
|
|
306
|
+
readonly PROPERTY_UNIQUE_ID_CONFIG_INVALID: {
|
|
307
|
+
readonly exitCode: 1;
|
|
308
|
+
readonly description: "A unique-id property counter is invalid.";
|
|
309
|
+
};
|
|
310
|
+
readonly PROPERTY_UNIQUE_ID_INVALID: {
|
|
311
|
+
readonly exitCode: 1;
|
|
312
|
+
readonly description: "A unique-id property value is invalid.";
|
|
313
|
+
};
|
|
314
|
+
readonly PROPERTY_UNIQUE_ID_TYPE_MISMATCH: {
|
|
315
|
+
readonly exitCode: 1;
|
|
316
|
+
readonly description: "A unique-id setting was supplied for a different property type.";
|
|
317
|
+
};
|
|
318
|
+
readonly PROPERTY_URL_INVALID: {
|
|
319
|
+
readonly exitCode: 1;
|
|
320
|
+
readonly description: "A url property value is invalid.";
|
|
321
|
+
};
|
|
322
|
+
readonly PROPERTY_VALUE_INVALID: {
|
|
323
|
+
readonly exitCode: 1;
|
|
324
|
+
readonly description: "A property value does not match its definition.";
|
|
325
|
+
};
|
|
326
|
+
readonly SAVED_VIEW_FILTER_CORRUPT: {
|
|
327
|
+
readonly exitCode: 1;
|
|
328
|
+
readonly description: "A stored saved view filter is corrupt.";
|
|
329
|
+
};
|
|
138
330
|
readonly SAVED_VIEW_FILTER_TOO_COMPLEX: {
|
|
139
331
|
readonly exitCode: 1;
|
|
140
332
|
readonly description: "The saved view filter exceeds supported complexity.";
|
|
@@ -147,6 +339,10 @@ export declare const ERROR_CODES: {
|
|
|
147
339
|
readonly exitCode: 1;
|
|
148
340
|
readonly description: "The saved view config is invalid.";
|
|
149
341
|
};
|
|
342
|
+
readonly SAVED_VIEW_INVALID_CURRENT_TIME: {
|
|
343
|
+
readonly exitCode: 1;
|
|
344
|
+
readonly description: "The reference time for dynamic date filters is invalid.";
|
|
345
|
+
};
|
|
150
346
|
readonly SAVED_VIEW_INVALID_DATE_VALUE: {
|
|
151
347
|
readonly exitCode: 1;
|
|
152
348
|
readonly description: "A saved view date value is not a real calendar date.";
|
|
@@ -159,10 +355,22 @@ export declare const ERROR_CODES: {
|
|
|
159
355
|
readonly exitCode: 1;
|
|
160
356
|
readonly description: "The saved view filter is invalid.";
|
|
161
357
|
};
|
|
358
|
+
readonly SAVED_VIEW_INVALID_OPERATOR: {
|
|
359
|
+
readonly exitCode: 1;
|
|
360
|
+
readonly description: "A saved view filter operator is invalid.";
|
|
361
|
+
};
|
|
362
|
+
readonly SAVED_VIEW_INVALID_RELATIVE_DAYS: {
|
|
363
|
+
readonly exitCode: 1;
|
|
364
|
+
readonly description: "withinLastDays requires an integer day count in range.";
|
|
365
|
+
};
|
|
162
366
|
readonly SAVED_VIEW_INVALID_ROW: {
|
|
163
367
|
readonly exitCode: 1;
|
|
164
368
|
readonly description: "A stored saved view row is invalid.";
|
|
165
369
|
};
|
|
370
|
+
readonly SAVED_VIEW_INVALID_TIME_ZONE: {
|
|
371
|
+
readonly exitCode: 2;
|
|
372
|
+
readonly description: "The time zone is not a valid IANA time zone.";
|
|
373
|
+
};
|
|
166
374
|
readonly SAVED_VIEW_NOT_FOUND: {
|
|
167
375
|
readonly exitCode: 5;
|
|
168
376
|
readonly description: "The saved view does not exist.";
|
|
@@ -199,6 +407,10 @@ export declare const ERROR_CODES: {
|
|
|
199
407
|
readonly exitCode: 1;
|
|
200
408
|
readonly description: "The field type cannot be sorted.";
|
|
201
409
|
};
|
|
410
|
+
readonly SAVED_VIEW_TIME_ZONE_CONVERSION_FAILED: {
|
|
411
|
+
readonly exitCode: 1;
|
|
412
|
+
readonly description: "A dynamic date boundary could not be converted to the time zone.";
|
|
413
|
+
};
|
|
202
414
|
readonly SAVED_VIEW_UNSUPPORTED_CONFIG_VERSION: {
|
|
203
415
|
readonly exitCode: 1;
|
|
204
416
|
readonly description: "The saved view config version is unsupported.";
|
package/dist/utils/errorCodes.js
CHANGED
|
@@ -29,23 +29,75 @@ export const ERROR_CODES = {
|
|
|
29
29
|
DAEMON_UNREACHABLE: { exitCode: 1, description: 'The daemon socket could not be reached.' },
|
|
30
30
|
INTERNAL_ERROR: { exitCode: 1, description: 'Unexpected internal error; not a DinoxError.' },
|
|
31
31
|
INVALID_ARGUMENT: { exitCode: 2, description: 'An argument or option value is invalid.' },
|
|
32
|
+
INVALID_PROPERTY_DEF_CONFIG: { exitCode: 1, description: 'A property definition config could not be serialized.' },
|
|
33
|
+
INVALID_PROPERTY_DEF_ID: { exitCode: 1, description: 'A property definition id is missing or blank.' },
|
|
34
|
+
INVALID_PROPERTY_OPTIONS: { exitCode: 1, description: 'Property options could not be serialized.' },
|
|
32
35
|
INVALID_STORAGE_KEY: { exitCode: 2, description: 'The storage object key is invalid.' },
|
|
33
36
|
NOTE_INVALID_PROPERTIES: { exitCode: 1, description: 'Note properties are invalid.' },
|
|
34
37
|
NOTE_NOT_FOUND: { exitCode: 5, description: 'The note does not exist or is deleted.' },
|
|
38
|
+
NOTE_PROPERTIES_CORRUPT: { exitCode: 1, description: 'The note properties JSON is corrupt and cannot be mutated safely.' },
|
|
39
|
+
NOTE_PROPERTIES_INVALID: { exitCode: 1, description: 'The note properties mutation is invalid.' },
|
|
40
|
+
NOTE_PROPERTIES_MUTATION_FAILED: { exitCode: 1, description: 'The note properties mutation could not be applied.' },
|
|
41
|
+
NOTE_PROPERTY_ORDER_STALE: { exitCode: 5, description: 'The property slot order no longer matches the note; re-read and retry.' },
|
|
35
42
|
NOTE_TEMPLATE_INVALID_CONTENT: { exitCode: 1, description: 'The note template content is invalid.' },
|
|
36
43
|
NOTE_TEMPLATE_INVALID_PROPERTY_DEF: { exitCode: 1, description: 'A note template property definition is invalid.' },
|
|
37
44
|
NOTE_TEMPLATE_INVALID_TOKEN_CONTEXT: { exitCode: 1, description: 'A note template token is used in an invalid context.' },
|
|
45
|
+
NOTE_TEMPLATE_NOT_FOUND: { exitCode: 5, description: 'The note template does not exist or is deleted.' },
|
|
38
46
|
NOTE_TEMPLATE_UNSUPPORTED_PROPERTY_DEF_VERSION: { exitCode: 1, description: 'The note template property definition version is unsupported.' },
|
|
39
47
|
NOT_FOUND: { exitCode: 5, description: 'The requested resource was not found.' },
|
|
40
48
|
POWERSYNC_OWNER_BUSY: { exitCode: 1, description: 'Another process owns the local PowerSync database.' },
|
|
41
49
|
PRECONDITION_FAILED: { exitCode: 5, description: 'A safety precondition for the operation did not hold.' },
|
|
50
|
+
PROPERTY_DATE_INVALID: { exitCode: 1, description: 'A date property value is not a valid date.' },
|
|
51
|
+
PROPERTY_DEF_CONFIG_INVALID: { exitCode: 1, description: 'A property definition config is invalid.' },
|
|
52
|
+
PROPERTY_DEF_CONFIG_TYPE_MISMATCH: { exitCode: 1, description: 'A property definition config does not match the property type.' },
|
|
53
|
+
PROPERTY_DEF_ENSURE_FAILED: { exitCode: 1, description: 'The property definition could not be created or reused.' },
|
|
54
|
+
PROPERTY_DEF_ENSURE_INPUT_INVALID: { exitCode: 1, description: 'The property definition create input is invalid.' },
|
|
55
|
+
PROPERTY_DEF_NOT_FOUND: { exitCode: 5, description: 'The property definition does not exist or is deleted.' },
|
|
56
|
+
PROPERTY_DEF_TYPE_CONFLICT: { exitCode: 5, description: 'A property with this key already exists with a different type.' },
|
|
57
|
+
PROPERTY_DEF_TYPE_INVALID: { exitCode: 1, description: 'The stored property definition type is invalid.' },
|
|
58
|
+
PROPERTY_FILES_INVALID: { exitCode: 1, description: 'A files property value is invalid.' },
|
|
59
|
+
PROPERTY_LEGACY_OPTION_LABEL_INVALID: { exitCode: 1, description: 'A legacy option label cannot be migrated.' },
|
|
60
|
+
PROPERTY_LEGACY_OPTION_NOT_PLANNED: { exitCode: 1, description: 'A legacy option value was not covered by the migration plan.' },
|
|
61
|
+
PROPERTY_LEGACY_VALUE_INVALID: { exitCode: 1, description: 'A legacy option value cannot be migrated.' },
|
|
62
|
+
PROPERTY_MULTI_SELECT_EMPTY: { exitCode: 1, description: 'A multi-select property value is empty.' },
|
|
63
|
+
PROPERTY_MULTI_SELECT_LIMIT_EXCEEDED: { exitCode: 1, description: 'A multi-select property value has too many selections.' },
|
|
64
|
+
PROPERTY_NUMBER_INVALID: { exitCode: 1, description: 'A number property value is invalid or out of range.' },
|
|
65
|
+
PROPERTY_OPTIONS_INVALID: { exitCode: 1, description: 'Stored property options are invalid.' },
|
|
66
|
+
PROPERTY_OPTIONS_MIGRATION_REQUIRED: { exitCode: 5, description: 'Legacy property options must be migrated first (dino prop migrate-options).' },
|
|
67
|
+
PROPERTY_OPTIONS_TYPE_MISMATCH: { exitCode: 1, description: 'Options were supplied for a property type that has none.' },
|
|
68
|
+
PROPERTY_OPTION_ARCHIVED: { exitCode: 5, description: 'The select option is archived and cannot be assigned.' },
|
|
69
|
+
PROPERTY_OPTION_ID_DUPLICATE: { exitCode: 1, description: 'Property options contain duplicate ids.' },
|
|
70
|
+
PROPERTY_OPTION_ID_UNKNOWN: { exitCode: 1, description: 'A property option id does not exist on the definition.' },
|
|
71
|
+
PROPERTY_OPTION_LABEL_INVALID: { exitCode: 1, description: 'A property option label is blank, too long, or duplicated.' },
|
|
72
|
+
PROPERTY_OPTION_LIMIT_EXCEEDED: { exitCode: 1, description: 'The property has too many options.' },
|
|
73
|
+
PROPERTY_OPTION_NEW_ID_FORBIDDEN: { exitCode: 1, description: 'New property options must not carry an id.' },
|
|
74
|
+
PROPERTY_OPTION_NOT_FOUND: { exitCode: 5, description: 'The select option does not exist on a closed property.' },
|
|
75
|
+
PROPERTY_OPTION_REMOVAL_REQUIRES_ARCHIVE: { exitCode: 1, description: 'Existing property options must be archived instead of removed.' },
|
|
76
|
+
PROPERTY_OPTION_REPAIR_ID_FORBIDDEN: { exitCode: 1, description: 'Repair options must not carry ids.' },
|
|
77
|
+
PROPERTY_OPTION_SORT_RANK_EXHAUSTED: { exitCode: 1, description: 'No sort rank is left for a new property option.' },
|
|
78
|
+
PROPERTY_OPTION_VALUE_INVALID: { exitCode: 1, description: 'A select property value is invalid.' },
|
|
79
|
+
PROPERTY_PLACE_INVALID: { exitCode: 1, description: 'A place property value is invalid.' },
|
|
80
|
+
PROPERTY_RELATION_EMPTY: { exitCode: 1, description: 'A relation property value is empty.' },
|
|
81
|
+
PROPERTY_RELATION_INVALID: { exitCode: 1, description: 'A relation property value references invalid notes.' },
|
|
82
|
+
PROPERTY_RELATION_LIMIT: { exitCode: 1, description: 'A relation property value has too many notes.' },
|
|
83
|
+
PROPERTY_TYPE_NOT_ACTIVE: { exitCode: 1, description: 'The property type is not active.' },
|
|
84
|
+
PROPERTY_UNIQUE_ID_CONFIG_INVALID: { exitCode: 1, description: 'A unique-id property counter is invalid.' },
|
|
85
|
+
PROPERTY_UNIQUE_ID_INVALID: { exitCode: 1, description: 'A unique-id property value is invalid.' },
|
|
86
|
+
PROPERTY_UNIQUE_ID_TYPE_MISMATCH: { exitCode: 1, description: 'A unique-id setting was supplied for a different property type.' },
|
|
87
|
+
PROPERTY_URL_INVALID: { exitCode: 1, description: 'A url property value is invalid.' },
|
|
88
|
+
PROPERTY_VALUE_INVALID: { exitCode: 1, description: 'A property value does not match its definition.' },
|
|
89
|
+
SAVED_VIEW_FILTER_CORRUPT: { exitCode: 1, description: 'A stored saved view filter is corrupt.' },
|
|
42
90
|
SAVED_VIEW_FILTER_TOO_COMPLEX: { exitCode: 1, description: 'The saved view filter exceeds supported complexity.' },
|
|
43
91
|
SAVED_VIEW_FILTER_VALUE_REQUIRED: { exitCode: 5, description: 'A saved view filter condition is missing its value.' },
|
|
44
92
|
SAVED_VIEW_INVALID_CONFIG: { exitCode: 1, description: 'The saved view config is invalid.' },
|
|
93
|
+
SAVED_VIEW_INVALID_CURRENT_TIME: { exitCode: 1, description: 'The reference time for dynamic date filters is invalid.' },
|
|
45
94
|
SAVED_VIEW_INVALID_DATE_VALUE: { exitCode: 1, description: 'A saved view date value is not a real calendar date.' },
|
|
46
95
|
SAVED_VIEW_INVALID_FIELD: { exitCode: 1, description: 'A saved view references an invalid field.' },
|
|
47
96
|
SAVED_VIEW_INVALID_FILTER: { exitCode: 1, description: 'The saved view filter is invalid.' },
|
|
97
|
+
SAVED_VIEW_INVALID_OPERATOR: { exitCode: 1, description: 'A saved view filter operator is invalid.' },
|
|
98
|
+
SAVED_VIEW_INVALID_RELATIVE_DAYS: { exitCode: 1, description: 'withinLastDays requires an integer day count in range.' },
|
|
48
99
|
SAVED_VIEW_INVALID_ROW: { exitCode: 1, description: 'A stored saved view row is invalid.' },
|
|
100
|
+
SAVED_VIEW_INVALID_TIME_ZONE: { exitCode: 2, description: 'The time zone is not a valid IANA time zone.' },
|
|
49
101
|
SAVED_VIEW_NOT_FOUND: { exitCode: 5, description: 'The saved view does not exist.' },
|
|
50
102
|
SAVED_VIEW_OPERATOR_TYPE_MISMATCH: { exitCode: 1, description: 'A filter operator is not valid for the field type.' },
|
|
51
103
|
SAVED_VIEW_OPTION_NOT_FOUND: { exitCode: 5, description: 'A select option id referenced by the view does not exist.' },
|
|
@@ -55,6 +107,7 @@ export const ERROR_CODES = {
|
|
|
55
107
|
SAVED_VIEW_PROPERTY_OPTIONS_INVALID: { exitCode: 1, description: 'Property options used by the view are invalid.' },
|
|
56
108
|
SAVED_VIEW_PROPERTY_OPTIONS_MIGRATION_REQUIRED: { exitCode: 5, description: 'Property options need migration before use.' },
|
|
57
109
|
SAVED_VIEW_SORT_TYPE_UNSUPPORTED: { exitCode: 1, description: 'The field type cannot be sorted.' },
|
|
110
|
+
SAVED_VIEW_TIME_ZONE_CONVERSION_FAILED: { exitCode: 1, description: 'A dynamic date boundary could not be converted to the time zone.' },
|
|
58
111
|
SAVED_VIEW_UNSUPPORTED_CONFIG_VERSION: { exitCode: 1, description: 'The saved view config version is unsupported.' },
|
|
59
112
|
SAVED_VIEW_UNSUPPORTED_FILTER_VERSION: { exitCode: 1, description: 'The saved view filter version is unsupported.' },
|
|
60
113
|
SAVED_VIEW_UNSUPPORTED_LAYOUT: { exitCode: 1, description: 'The saved view layout is unsupported.' },
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { isPropertyKeyValid, serializeNoteTemplatePropertyKeys, } from "../shared/index.js";
|
|
2
|
+
import { uuidv7 } from "uuidv7";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { defineCommand, defineReadModel, } from "../rpc.js";
|
|
5
|
+
import { normalizeLimit } from "../sql-utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Note templates are synced rows shared with the mobile client. The electron
|
|
8
|
+
* side owns the `property_def` column (versioned key-list envelope); writes
|
|
9
|
+
* only SET the columns they change so template content/extra_data written by
|
|
10
|
+
* other clients is never clobbered.
|
|
11
|
+
*/
|
|
12
|
+
const NOTE_TEMPLATE_TABLE = "c_note_template";
|
|
13
|
+
const NOTE_TEMPLATE_COLUMNS = `
|
|
14
|
+
id, user_id, name, content, priority, is_show, extra_data, property_def,
|
|
15
|
+
insert_at, update_at, is_del
|
|
16
|
+
`;
|
|
17
|
+
const PROPERTY_KEYS_INPUT = z
|
|
18
|
+
.array(z.string().min(1).max(64).refine(isPropertyKeyValid))
|
|
19
|
+
.max(50);
|
|
20
|
+
const NOTE_TEMPLATE_CATALOG_INPUT = z.object({
|
|
21
|
+
userId: z.string().trim().min(1),
|
|
22
|
+
limit: z.number().int().positive().max(2000).optional(),
|
|
23
|
+
});
|
|
24
|
+
const TEMPLATE_CONTENT_INPUT = z.string().max(200_000);
|
|
25
|
+
const NOTE_TEMPLATE_CREATE_INPUT = z.object({
|
|
26
|
+
userId: z.string().trim().min(1),
|
|
27
|
+
name: z.string().trim().min(1).max(120),
|
|
28
|
+
propertyKeys: PROPERTY_KEYS_INPUT,
|
|
29
|
+
content: TEMPLATE_CONTENT_INPUT.nullable().optional(),
|
|
30
|
+
updatedAt: z.string().trim().min(1).max(64),
|
|
31
|
+
});
|
|
32
|
+
const NOTE_TEMPLATE_UPDATE_INPUT = z
|
|
33
|
+
.object({
|
|
34
|
+
id: z.string().trim().min(1),
|
|
35
|
+
userId: z.string().trim().min(1),
|
|
36
|
+
name: z.string().trim().min(1).max(120).optional(),
|
|
37
|
+
propertyKeys: PROPERTY_KEYS_INPUT.optional(),
|
|
38
|
+
content: TEMPLATE_CONTENT_INPUT.nullable().optional(),
|
|
39
|
+
updatedAt: z.string().trim().min(1).max(64),
|
|
40
|
+
})
|
|
41
|
+
.refine((input) => input.name !== undefined ||
|
|
42
|
+
input.propertyKeys !== undefined ||
|
|
43
|
+
input.content !== undefined, {
|
|
44
|
+
message: "At least one note-template field must be updated",
|
|
45
|
+
});
|
|
46
|
+
const NOTE_TEMPLATE_SOFT_DELETE_INPUT = z.object({
|
|
47
|
+
id: z.string().trim().min(1),
|
|
48
|
+
userId: z.string().trim().min(1),
|
|
49
|
+
updatedAt: z.string().trim().min(1).max(64),
|
|
50
|
+
});
|
|
51
|
+
export function createNoteTemplateReadModels() {
|
|
52
|
+
return [
|
|
53
|
+
defineReadModel({
|
|
54
|
+
name: "noteTemplates.catalog",
|
|
55
|
+
inputSchema: NOTE_TEMPLATE_CATALOG_INPUT,
|
|
56
|
+
tables: () => [NOTE_TEMPLATE_TABLE],
|
|
57
|
+
fetch: async (db, input) => fetchNoteTemplateCatalog(db, input),
|
|
58
|
+
}),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
export function createNoteTemplateCommands() {
|
|
62
|
+
return [
|
|
63
|
+
defineCommand({
|
|
64
|
+
name: "noteTemplates.create",
|
|
65
|
+
inputSchema: NOTE_TEMPLATE_CREATE_INPUT,
|
|
66
|
+
run: async (db, input) => {
|
|
67
|
+
const id = await createNoteTemplate(db, input);
|
|
68
|
+
return { result: id, affectedTables: [NOTE_TEMPLATE_TABLE] };
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
defineCommand({
|
|
72
|
+
name: "noteTemplates.update",
|
|
73
|
+
inputSchema: NOTE_TEMPLATE_UPDATE_INPUT,
|
|
74
|
+
run: async (db, input) => {
|
|
75
|
+
const changed = await updateNoteTemplate(db, input);
|
|
76
|
+
return {
|
|
77
|
+
result: changed,
|
|
78
|
+
affectedTables: changed ? [NOTE_TEMPLATE_TABLE] : [],
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
defineCommand({
|
|
83
|
+
name: "noteTemplates.softDelete",
|
|
84
|
+
inputSchema: NOTE_TEMPLATE_SOFT_DELETE_INPUT,
|
|
85
|
+
run: async (db, input) => {
|
|
86
|
+
const changed = await softDeleteNoteTemplate(db, input);
|
|
87
|
+
return {
|
|
88
|
+
result: changed,
|
|
89
|
+
affectedTables: changed ? [NOTE_TEMPLATE_TABLE] : [],
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
}),
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
async function fetchNoteTemplateCatalog(db, input) {
|
|
96
|
+
const userId = input.userId.trim();
|
|
97
|
+
const limit = normalizeLimit(input.limit, 500, 2000);
|
|
98
|
+
if (!userId)
|
|
99
|
+
throw new Error("Invalid note template catalog input");
|
|
100
|
+
if (limit <= 0)
|
|
101
|
+
return [];
|
|
102
|
+
return await db.getAll(`
|
|
103
|
+
SELECT ${NOTE_TEMPLATE_COLUMNS}
|
|
104
|
+
FROM c_note_template
|
|
105
|
+
WHERE user_id = ?
|
|
106
|
+
AND (is_del IS NULL OR is_del = 0)
|
|
107
|
+
ORDER BY COALESCE(priority, 0) ASC,
|
|
108
|
+
COALESCE(NULLIF(insert_at, ''), id) ASC,
|
|
109
|
+
id ASC
|
|
110
|
+
LIMIT ?
|
|
111
|
+
`, [userId, limit]);
|
|
112
|
+
}
|
|
113
|
+
function normalizeTemplateContent(content) {
|
|
114
|
+
if (typeof content !== "string")
|
|
115
|
+
return "";
|
|
116
|
+
return content.trim() ? content : "";
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Templates with content are visible in the mobile template picker
|
|
120
|
+
* (is_show = 1); property-only templates stay hidden there (is_show = 0)
|
|
121
|
+
* because mobile does not render property_def yet and an empty-content
|
|
122
|
+
* template would produce blank notes.
|
|
123
|
+
*/
|
|
124
|
+
async function createNoteTemplate(db, input) {
|
|
125
|
+
const userId = input.userId.trim();
|
|
126
|
+
const name = input.name.trim();
|
|
127
|
+
const updatedAt = input.updatedAt.trim();
|
|
128
|
+
if (!userId || !name || !updatedAt) {
|
|
129
|
+
throw new Error("Invalid note template create input");
|
|
130
|
+
}
|
|
131
|
+
const content = normalizeTemplateContent(input.content);
|
|
132
|
+
const id = uuidv7();
|
|
133
|
+
await db.writeTransaction(async (context) => {
|
|
134
|
+
await context.execute(`INSERT INTO c_note_template (
|
|
135
|
+
id, user_id, name, content, priority, is_show, extra_data,
|
|
136
|
+
property_def, insert_at, update_at, is_del
|
|
137
|
+
) VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?, 0)
|
|
138
|
+
`, [
|
|
139
|
+
id,
|
|
140
|
+
userId,
|
|
141
|
+
name,
|
|
142
|
+
content,
|
|
143
|
+
content ? 1 : 0,
|
|
144
|
+
"{}",
|
|
145
|
+
serializeNoteTemplatePropertyKeys(input.propertyKeys),
|
|
146
|
+
updatedAt,
|
|
147
|
+
updatedAt,
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
return id;
|
|
151
|
+
}
|
|
152
|
+
async function updateNoteTemplate(db, input) {
|
|
153
|
+
const id = input.id.trim();
|
|
154
|
+
const userId = input.userId.trim();
|
|
155
|
+
const updatedAt = input.updatedAt.trim();
|
|
156
|
+
if (!id || !userId || !updatedAt) {
|
|
157
|
+
throw new Error("Invalid note template update input");
|
|
158
|
+
}
|
|
159
|
+
let changed = false;
|
|
160
|
+
await db.writeTransaction(async (context) => {
|
|
161
|
+
const row = await context.getOptional(`SELECT ${NOTE_TEMPLATE_COLUMNS}
|
|
162
|
+
FROM c_note_template
|
|
163
|
+
WHERE id = ?
|
|
164
|
+
AND user_id = ?
|
|
165
|
+
AND (is_del IS NULL OR is_del = 0)
|
|
166
|
+
LIMIT 1`, [id, userId]);
|
|
167
|
+
if (!row)
|
|
168
|
+
return;
|
|
169
|
+
const assignments = [];
|
|
170
|
+
const params = [];
|
|
171
|
+
if (input.name !== undefined) {
|
|
172
|
+
const name = input.name.trim();
|
|
173
|
+
if (name && name !== (row.name ?? "")) {
|
|
174
|
+
assignments.push("name = ?");
|
|
175
|
+
params.push(name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (input.propertyKeys !== undefined) {
|
|
179
|
+
const serialized = serializeNoteTemplatePropertyKeys(input.propertyKeys);
|
|
180
|
+
if (serialized !== (row.property_def ?? null)) {
|
|
181
|
+
assignments.push("property_def = ?");
|
|
182
|
+
params.push(serialized);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (input.content !== undefined) {
|
|
186
|
+
const nextContent = normalizeTemplateContent(input.content);
|
|
187
|
+
const previousContent = normalizeTemplateContent(row.content);
|
|
188
|
+
if (nextContent !== previousContent) {
|
|
189
|
+
assignments.push("content = ?");
|
|
190
|
+
params.push(nextContent);
|
|
191
|
+
// Flip mobile-picker visibility only when content presence changes,
|
|
192
|
+
// so a manual is_show choice survives ordinary edits.
|
|
193
|
+
if (Boolean(nextContent) !== Boolean(previousContent)) {
|
|
194
|
+
assignments.push("is_show = ?");
|
|
195
|
+
params.push(nextContent ? 1 : 0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (assignments.length === 0)
|
|
200
|
+
return;
|
|
201
|
+
assignments.push("update_at = ?");
|
|
202
|
+
params.push(updatedAt, id, userId);
|
|
203
|
+
await context.execute(`UPDATE c_note_template
|
|
204
|
+
SET ${assignments.join(", ")}
|
|
205
|
+
WHERE id = ? AND user_id = ?`, params);
|
|
206
|
+
changed = true;
|
|
207
|
+
});
|
|
208
|
+
return changed;
|
|
209
|
+
}
|
|
210
|
+
async function softDeleteNoteTemplate(db, input) {
|
|
211
|
+
const id = input.id.trim();
|
|
212
|
+
const userId = input.userId.trim();
|
|
213
|
+
const updatedAt = input.updatedAt.trim();
|
|
214
|
+
if (!id || !userId || !updatedAt) {
|
|
215
|
+
throw new Error("Invalid note template delete input");
|
|
216
|
+
}
|
|
217
|
+
let changed = false;
|
|
218
|
+
await db.writeTransaction(async (context) => {
|
|
219
|
+
const row = await context.getOptional(`SELECT id FROM c_note_template
|
|
220
|
+
WHERE id = ?
|
|
221
|
+
AND user_id = ?
|
|
222
|
+
AND (is_del IS NULL OR is_del = 0)
|
|
223
|
+
LIMIT 1`, [id, userId]);
|
|
224
|
+
if (!row)
|
|
225
|
+
return;
|
|
226
|
+
await context.execute(`UPDATE c_note_template
|
|
227
|
+
SET is_del = 1, update_at = ?
|
|
228
|
+
WHERE id = ? AND user_id = ?`, [updatedAt, id, userId]);
|
|
229
|
+
changed = true;
|
|
230
|
+
});
|
|
231
|
+
return changed;
|
|
232
|
+
}
|