@meeovi/layer-lists 1.0.2 → 1.0.3
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 +111 -0
- package/app/composables/config.ts +1 -1
- package/app/composables/module.ts +75 -0
- package/app/composables/providers/directus.ts +112 -16
- package/app/composables/providers/memory.ts +39 -0
- package/app/composables/registry.ts +5 -0
- package/app/composables/types.ts +9 -0
- package/app/composables/utils/health.ts +16 -0
- package/dist/app/composables/config.d.ts +7 -0
- package/dist/app/composables/config.js +9 -0
- package/dist/app/composables/globals/useDirectusForm.d.ts +4 -0
- package/dist/app/composables/globals/useDirectusForm.js +10 -0
- package/dist/app/composables/module.d.ts +7 -0
- package/dist/app/composables/module.js +60 -0
- package/dist/app/composables/providers/atproto.d.ts +1 -0
- package/dist/app/composables/providers/atproto.js +133 -0
- package/dist/app/composables/providers/directus.d.ts +1 -0
- package/dist/app/composables/providers/directus.js +130 -0
- package/dist/app/composables/providers/memory.d.ts +1 -0
- package/dist/app/composables/providers/memory.js +110 -0
- package/dist/app/composables/registry.d.ts +4 -0
- package/dist/app/composables/registry.js +14 -0
- package/dist/app/composables/types.d.ts +38 -0
- package/dist/app/composables/types.js +1 -0
- package/dist/app/composables/useLists.d.ts +11 -0
- package/dist/app/composables/useLists.js +17 -0
- package/dist/app/composables/utils/health.d.ts +10 -0
- package/dist/app/composables/utils/health.js +15 -0
- package/dist/app/composables/utils/transforms.d.ts +13 -0
- package/dist/app/composables/utils/transforms.js +38 -0
- package/dist/app/composables/utils/validation.d.ts +3 -0
- package/dist/app/composables/utils/validation.js +16 -0
- package/dist/layers/lists/app/composables/config.d.ts +7 -0
- package/dist/layers/lists/app/composables/config.js +9 -0
- package/dist/layers/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
- package/dist/layers/lists/app/composables/globals/useDirectusForm.js +1 -0
- package/dist/layers/lists/app/composables/module.d.ts +7 -0
- package/dist/layers/lists/app/composables/module.js +60 -0
- package/dist/layers/lists/app/composables/providers/atproto.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/atproto.js +133 -0
- package/dist/layers/lists/app/composables/providers/directus.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/directus.js +130 -0
- package/dist/layers/lists/app/composables/providers/memory.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/memory.js +110 -0
- package/dist/layers/lists/app/composables/registry.d.ts +4 -0
- package/dist/layers/lists/app/composables/registry.js +14 -0
- package/dist/layers/lists/app/composables/types.d.ts +38 -0
- package/dist/layers/lists/app/composables/types.js +1 -0
- package/dist/layers/lists/app/composables/useLists.d.ts +11 -0
- package/dist/layers/lists/app/composables/useLists.js +17 -0
- package/dist/layers/lists/app/composables/utils/health.d.ts +10 -0
- package/dist/layers/lists/app/composables/utils/health.js +15 -0
- package/dist/layers/lists/app/composables/utils/transforms.d.ts +13 -0
- package/dist/layers/lists/app/composables/utils/transforms.js +38 -0
- package/dist/layers/lists/app/composables/utils/validation.d.ts +3 -0
- package/dist/layers/lists/app/composables/utils/validation.js +16 -0
- package/dist/layers/lists/nuxt.config.d.ts +2 -0
- package/dist/layers/lists/nuxt.config.js +7 -0
- package/dist/layers/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
- package/dist/layers/shared/app/composables/globals/useDirectusForm.js +66 -0
- package/dist/lists/app/composables/config.d.ts +7 -0
- package/dist/lists/app/composables/config.js +9 -0
- package/dist/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
- package/dist/lists/app/composables/globals/useDirectusForm.js +1 -0
- package/dist/lists/app/composables/module.d.ts +7 -0
- package/dist/lists/app/composables/module.js +60 -0
- package/dist/lists/app/composables/providers/atproto.d.ts +1 -0
- package/dist/lists/app/composables/providers/atproto.js +133 -0
- package/dist/lists/app/composables/providers/directus.d.ts +1 -0
- package/dist/lists/app/composables/providers/directus.js +130 -0
- package/dist/lists/app/composables/providers/memory.d.ts +1 -0
- package/dist/lists/app/composables/providers/memory.js +110 -0
- package/dist/lists/app/composables/registry.d.ts +4 -0
- package/dist/lists/app/composables/registry.js +14 -0
- package/dist/lists/app/composables/types.d.ts +38 -0
- package/dist/lists/app/composables/types.js +1 -0
- package/dist/lists/app/composables/useLists.d.ts +11 -0
- package/dist/lists/app/composables/useLists.js +17 -0
- package/dist/lists/app/composables/utils/health.d.ts +10 -0
- package/dist/lists/app/composables/utils/health.js +15 -0
- package/dist/lists/app/composables/utils/transforms.d.ts +13 -0
- package/dist/lists/app/composables/utils/transforms.js +38 -0
- package/dist/lists/app/composables/utils/validation.d.ts +3 -0
- package/dist/lists/app/composables/utils/validation.js +16 -0
- package/dist/lists/nuxt.config.d.ts +2 -0
- package/dist/lists/nuxt.config.js +7 -0
- package/dist/nuxt.config.d.ts +2 -0
- package/dist/nuxt.config.js +7 -0
- package/dist/packages/core/index.d.ts +17 -0
- package/dist/packages/core/index.js +18 -0
- package/dist/packages/core/src/adapters/auth.d.ts +9 -0
- package/dist/packages/core/src/adapters/auth.js +1 -0
- package/dist/packages/core/src/adapters/cart.d.ts +22 -0
- package/dist/packages/core/src/adapters/cart.js +1 -0
- package/dist/packages/core/src/adapters/catalog.d.ts +17 -0
- package/dist/packages/core/src/adapters/catalog.js +1 -0
- package/dist/packages/core/src/adapters/common.d.ts +9 -0
- package/dist/packages/core/src/adapters/common.js +1 -0
- package/dist/packages/core/src/adapters/lists.d.ts +17 -0
- package/dist/packages/core/src/adapters/lists.js +1 -0
- package/dist/packages/core/src/adapters/search.d.ts +21 -0
- package/dist/packages/core/src/adapters/search.js +1 -0
- package/dist/packages/core/src/plugins/defineAdapter.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineAdapter.js +3 -0
- package/dist/packages/core/src/plugins/defineModule.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineModule.js +3 -0
- package/dist/packages/core/src/plugins/registry.d.ts +14 -0
- package/dist/packages/core/src/plugins/registry.js +46 -0
- package/dist/packages/core/src/runtime/app.d.ts +2 -0
- package/dist/packages/core/src/runtime/app.js +20 -0
- package/dist/packages/core/src/runtime/config.d.ts +7 -0
- package/dist/packages/core/src/runtime/config.js +9 -0
- package/dist/packages/core/src/runtime/context.d.ts +9 -0
- package/dist/packages/core/src/runtime/context.js +11 -0
- package/dist/packages/core/src/runtime/hooks.d.ts +5 -0
- package/dist/packages/core/src/runtime/hooks.js +18 -0
- package/dist/packages/core/src/runtime/lifecycle.d.ts +4 -0
- package/dist/packages/core/src/runtime/lifecycle.js +5 -0
- package/dist/packages/core/src/types/adapters.d.ts +14 -0
- package/dist/packages/core/src/types/adapters.js +1 -0
- package/dist/packages/core/src/types/app.d.ts +13 -0
- package/dist/packages/core/src/types/app.js +1 -0
- package/dist/packages/core/src/types/config.d.ts +8 -0
- package/dist/packages/core/src/types/config.js +1 -0
- package/dist/packages/core/src/types/events.d.ts +20 -0
- package/dist/packages/core/src/types/events.js +22 -0
- package/dist/packages/core/src/types/module.d.ts +15 -0
- package/dist/packages/core/src/types/module.js +1 -0
- package/dist/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
- package/dist/shared/app/composables/globals/useDirectusForm.js +66 -0
- package/global.d.ts +1 -0
- package/package.json +4 -1
- package/shims.d.ts +7 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { wrapSocialRequest } from '@meeovi/social';
|
|
3
|
+
import { transformList, transformItem } from '../utils/transforms';
|
|
4
|
+
import { validateListInput, validateItemInput } from '../utils/validation';
|
|
5
|
+
import { getListsConfig } from '../config';
|
|
6
|
+
async function atprotoFetch(path, options = {}) {
|
|
7
|
+
const { baseUrl, apiKey } = getListsConfig();
|
|
8
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
9
|
+
...options,
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
13
|
+
...(options.headers || {})
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok) {
|
|
17
|
+
const error = new Error(`ATProto error: ${res.status}`);
|
|
18
|
+
error.status = res.status;
|
|
19
|
+
error.response = res;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
const AtprotoListsProvider = {
|
|
25
|
+
async getList(id) {
|
|
26
|
+
return wrapSocialRequest('atproto', async () => {
|
|
27
|
+
const data = await atprotoFetch(`/xrpc/app.bsky.graph.getList?list=${id}`);
|
|
28
|
+
return transformList(data.list);
|
|
29
|
+
}, {
|
|
30
|
+
cacheKey: `atproto:list:${id}`,
|
|
31
|
+
ttlMs: 1000 * 30,
|
|
32
|
+
retry: true,
|
|
33
|
+
swr: true
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
async listLists() {
|
|
37
|
+
return wrapSocialRequest('atproto', async () => {
|
|
38
|
+
const data = await atprotoFetch(`/xrpc/app.bsky.graph.getLists`);
|
|
39
|
+
return data.lists.map(transformList);
|
|
40
|
+
}, {
|
|
41
|
+
cacheKey: `atproto:lists`,
|
|
42
|
+
ttlMs: 1000 * 30,
|
|
43
|
+
retry: true,
|
|
44
|
+
swr: true
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
async createList(data) {
|
|
48
|
+
validateListInput(data);
|
|
49
|
+
return wrapSocialRequest('atproto', async () => {
|
|
50
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.createList`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
name: data.title,
|
|
54
|
+
purpose: data.type ?? 'list',
|
|
55
|
+
description: data.metadata?.description ?? ''
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
return transformList(result);
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
async updateList(id, data) {
|
|
62
|
+
validateListInput(data);
|
|
63
|
+
return wrapSocialRequest('atproto', async () => {
|
|
64
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.updateList`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
list: id,
|
|
68
|
+
name: data.title,
|
|
69
|
+
description: data.metadata?.description
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
return transformList(result);
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
async deleteList(id) {
|
|
76
|
+
return wrapSocialRequest('atproto', async () => {
|
|
77
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.deleteList`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: JSON.stringify({ list: id })
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
async addItem(listId, item) {
|
|
84
|
+
validateItemInput(item);
|
|
85
|
+
return wrapSocialRequest('atproto', async () => {
|
|
86
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.addListItem`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
list: listId,
|
|
90
|
+
subject: item.title // ATProto uses "subject" for list entries
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
return transformItem(result);
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
async updateItem(listId, itemId, data) {
|
|
97
|
+
validateItemInput(data);
|
|
98
|
+
return wrapSocialRequest('atproto', async () => {
|
|
99
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.updateListItem`, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
list: listId,
|
|
103
|
+
item: itemId,
|
|
104
|
+
...data
|
|
105
|
+
})
|
|
106
|
+
});
|
|
107
|
+
return transformItem(result);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async deleteItem(listId, itemId) {
|
|
111
|
+
return wrapSocialRequest('atproto', async () => {
|
|
112
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.deleteListItem`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
list: listId,
|
|
116
|
+
item: itemId
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
async reorderItems(listId, itemIds) {
|
|
122
|
+
return wrapSocialRequest('atproto', async () => {
|
|
123
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.reorderListItems`, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
list: listId,
|
|
127
|
+
items: itemIds
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
registerListsProvider('atproto', AtprotoListsProvider);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { getListsConfig } from '../config';
|
|
3
|
+
async function directusFetch(path, options = {}) {
|
|
4
|
+
const cfg = getListsConfig();
|
|
5
|
+
const base = cfg.baseUrl?.replace(/\/$/, '') || '';
|
|
6
|
+
const res = await fetch(`${base}${path}`, {
|
|
7
|
+
...options,
|
|
8
|
+
headers: {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
...(cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}),
|
|
11
|
+
...(options.headers || {})
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
const err = new Error(`Directus error: ${res.status}`);
|
|
16
|
+
err.status = res.status;
|
|
17
|
+
err.response = res;
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
return res.json();
|
|
21
|
+
}
|
|
22
|
+
const DirectusListsProvider = {
|
|
23
|
+
async getList(id) {
|
|
24
|
+
const json = await directusFetch(`/lists/${id}`);
|
|
25
|
+
return json.list ?? json;
|
|
26
|
+
},
|
|
27
|
+
async listLists() {
|
|
28
|
+
const json = await directusFetch(`/lists`);
|
|
29
|
+
return json.lists ?? json;
|
|
30
|
+
},
|
|
31
|
+
async createList(data) {
|
|
32
|
+
const json = await directusFetch(`/lists`, { method: 'POST', body: JSON.stringify(data) });
|
|
33
|
+
return json.list ?? json;
|
|
34
|
+
},
|
|
35
|
+
async updateList(id, data) {
|
|
36
|
+
const json = await directusFetch(`/lists/${id}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
37
|
+
return json.list ?? json;
|
|
38
|
+
},
|
|
39
|
+
async deleteList(id) {
|
|
40
|
+
await directusFetch(`/lists/${id}`, { method: 'DELETE' });
|
|
41
|
+
},
|
|
42
|
+
async addItem(listId, item) {
|
|
43
|
+
const json = await directusFetch(`/lists/${listId}/items`, { method: 'POST', body: JSON.stringify(item) });
|
|
44
|
+
return json.item ?? json;
|
|
45
|
+
},
|
|
46
|
+
async updateItem(listId, itemId, data) {
|
|
47
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
48
|
+
return json.item ?? json;
|
|
49
|
+
},
|
|
50
|
+
async deleteItem(listId, itemId) {
|
|
51
|
+
await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'DELETE' });
|
|
52
|
+
},
|
|
53
|
+
async reorderItems(listId, itemIds) {
|
|
54
|
+
await directusFetch(`/lists/${listId}/reorder`, { method: 'POST', body: JSON.stringify({ items: itemIds }) });
|
|
55
|
+
},
|
|
56
|
+
async toggleComplete(listId, itemId, completed = true) {
|
|
57
|
+
try {
|
|
58
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/toggle`, { method: 'POST', body: JSON.stringify({ completed }) });
|
|
59
|
+
return json.item ?? json;
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ completed }) });
|
|
63
|
+
return json.item ?? json;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
async setDueDate(listId, itemId, dueDate) {
|
|
67
|
+
try {
|
|
68
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/due`, { method: 'POST', body: JSON.stringify({ dueDate }) });
|
|
69
|
+
return json.item ?? json;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { dueDate } }) });
|
|
73
|
+
return json.item ?? json;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
async setReminder(listId, itemId, reminder) {
|
|
77
|
+
try {
|
|
78
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/reminder`, { method: 'POST', body: JSON.stringify({ reminder }) });
|
|
79
|
+
return json.item ?? json;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { reminder } }) });
|
|
83
|
+
return json.item ?? json;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async setPriority(listId, itemId, priority) {
|
|
87
|
+
try {
|
|
88
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/priority`, { method: 'POST', body: JSON.stringify({ priority }) });
|
|
89
|
+
return json.item ?? json;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { priority } }) });
|
|
93
|
+
return json.item ?? json;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
async shareList(listId, userId, role = 'editor') {
|
|
97
|
+
try {
|
|
98
|
+
await directusFetch(`/lists/${listId}/share`, { method: 'POST', body: JSON.stringify({ userId, role }) });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
102
|
+
const list = json.list ?? json;
|
|
103
|
+
const collaborators = (list.metadata?.collaborators || []).concat({ userId, role });
|
|
104
|
+
await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), collaborators } }) });
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async searchItems(listId, query) {
|
|
108
|
+
try {
|
|
109
|
+
const json = await directusFetch(`/lists/${listId}/search?q=${encodeURIComponent(String(query))}`);
|
|
110
|
+
return json.items ?? json;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
114
|
+
const list = json.list ?? json;
|
|
115
|
+
const q = String(query).toLowerCase();
|
|
116
|
+
return list.items.filter((i) => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q));
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
async archiveList(listId) {
|
|
120
|
+
try {
|
|
121
|
+
await directusFetch(`/lists/${listId}/archive`, { method: 'POST' });
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
125
|
+
const list = json.list ?? json;
|
|
126
|
+
await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), archived: true } }) });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
registerListsProvider('directus', DirectusListsProvider);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
const lists = new Map();
|
|
4
|
+
const MemoryListsProvider = {
|
|
5
|
+
async getList(id) {
|
|
6
|
+
const list = lists.get(id);
|
|
7
|
+
if (!list)
|
|
8
|
+
throw new Error(`List ${id} not found`);
|
|
9
|
+
return list;
|
|
10
|
+
},
|
|
11
|
+
async listLists() {
|
|
12
|
+
return Array.from(lists.values());
|
|
13
|
+
},
|
|
14
|
+
async createList(data) {
|
|
15
|
+
const id = nanoid();
|
|
16
|
+
const list = {
|
|
17
|
+
id,
|
|
18
|
+
title: data.title || 'Untitled List',
|
|
19
|
+
type: data.type || 'list',
|
|
20
|
+
items: [],
|
|
21
|
+
metadata: data.metadata || {},
|
|
22
|
+
createdAt: new Date().toISOString(),
|
|
23
|
+
updatedAt: new Date().toISOString()
|
|
24
|
+
};
|
|
25
|
+
lists.set(id, list);
|
|
26
|
+
return list;
|
|
27
|
+
},
|
|
28
|
+
async updateList(id, data) {
|
|
29
|
+
const list = await this.getList(id);
|
|
30
|
+
const updated = {
|
|
31
|
+
...list,
|
|
32
|
+
...data,
|
|
33
|
+
updatedAt: new Date().toISOString()
|
|
34
|
+
};
|
|
35
|
+
lists.set(id, updated);
|
|
36
|
+
return updated;
|
|
37
|
+
},
|
|
38
|
+
async deleteList(id) {
|
|
39
|
+
lists.delete(id);
|
|
40
|
+
},
|
|
41
|
+
async addItem(listId, item) {
|
|
42
|
+
const list = await this.getList(listId);
|
|
43
|
+
const newItem = {
|
|
44
|
+
id: nanoid(),
|
|
45
|
+
title: item.title || '',
|
|
46
|
+
description: item.description,
|
|
47
|
+
completed: item.completed || false,
|
|
48
|
+
position: list.items.length,
|
|
49
|
+
parentId: item.parentId,
|
|
50
|
+
metadata: item.metadata || {},
|
|
51
|
+
createdAt: new Date().toISOString(),
|
|
52
|
+
updatedAt: new Date().toISOString()
|
|
53
|
+
};
|
|
54
|
+
list.items.push(newItem);
|
|
55
|
+
return newItem;
|
|
56
|
+
},
|
|
57
|
+
async updateItem(listId, itemId, data) {
|
|
58
|
+
const list = await this.getList(listId);
|
|
59
|
+
const item = list.items.find(i => i.id === itemId);
|
|
60
|
+
if (!item)
|
|
61
|
+
throw new Error(`Item ${itemId} not found`);
|
|
62
|
+
Object.assign(item, data, { updatedAt: new Date().toISOString() });
|
|
63
|
+
return item;
|
|
64
|
+
},
|
|
65
|
+
async deleteItem(listId, itemId) {
|
|
66
|
+
const list = await this.getList(listId);
|
|
67
|
+
list.items = list.items.filter(i => i.id !== itemId);
|
|
68
|
+
},
|
|
69
|
+
async reorderItems(listId, itemIds) {
|
|
70
|
+
const list = await this.getList(listId);
|
|
71
|
+
const newOrder = itemIds.map(id => list.items.find(i => i.id === id));
|
|
72
|
+
list.items = newOrder.map((item, index) => ({
|
|
73
|
+
...item,
|
|
74
|
+
position: index
|
|
75
|
+
}));
|
|
76
|
+
},
|
|
77
|
+
async toggleComplete(listId, itemId, completed = true) {
|
|
78
|
+
const item = await this.updateItem(listId, itemId, { completed });
|
|
79
|
+
return item;
|
|
80
|
+
},
|
|
81
|
+
async setDueDate(listId, itemId, dueDate) {
|
|
82
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), dueDate } });
|
|
83
|
+
return item;
|
|
84
|
+
},
|
|
85
|
+
async setReminder(listId, itemId, reminder) {
|
|
86
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), reminder } });
|
|
87
|
+
return item;
|
|
88
|
+
},
|
|
89
|
+
async setPriority(listId, itemId, priority) {
|
|
90
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), priority } });
|
|
91
|
+
return item;
|
|
92
|
+
},
|
|
93
|
+
async shareList(listId, userId, role = 'editor') {
|
|
94
|
+
// memory store: attach collaborators in list.metadata.collaborators
|
|
95
|
+
const list = await this.getList(listId);
|
|
96
|
+
const collaborators = list.metadata?.collaborators || [];
|
|
97
|
+
collaborators.push({ userId, role });
|
|
98
|
+
list.metadata = { ...(list.metadata || {}), collaborators };
|
|
99
|
+
},
|
|
100
|
+
async searchItems(listId, query) {
|
|
101
|
+
const list = await this.getList(listId);
|
|
102
|
+
const q = String(query).toLowerCase();
|
|
103
|
+
return list.items.filter(i => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q));
|
|
104
|
+
},
|
|
105
|
+
async archiveList(listId) {
|
|
106
|
+
const list = await this.getList(listId);
|
|
107
|
+
list.metadata = { ...(list.metadata || {}), archived: true };
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
registerListsProvider('memory', MemoryListsProvider);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ListsProvider } from './types';
|
|
2
|
+
export declare function registerListsProvider(name: string, provider: ListsProvider): void;
|
|
3
|
+
export declare function registerListsProviderRuntime(name: string, provider: ListsProvider): void;
|
|
4
|
+
export declare function getListsProvider(name: string): ListsProvider;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const providers = {};
|
|
2
|
+
export function registerListsProvider(name, provider) {
|
|
3
|
+
providers[name] = provider;
|
|
4
|
+
}
|
|
5
|
+
// Allow runtime registration from other modules (e.g. via core ModuleRegistry adapters)
|
|
6
|
+
export function registerListsProviderRuntime(name, provider) {
|
|
7
|
+
registerListsProvider(name, provider);
|
|
8
|
+
}
|
|
9
|
+
export function getListsProvider(name) {
|
|
10
|
+
const provider = providers[name];
|
|
11
|
+
if (!provider)
|
|
12
|
+
throw new Error(`Lists provider "${name}" not found`);
|
|
13
|
+
return provider;
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface ListItem {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
completed?: boolean;
|
|
6
|
+
position?: number;
|
|
7
|
+
parentId?: string;
|
|
8
|
+
metadata?: Record<string, any>;
|
|
9
|
+
createdAt?: string;
|
|
10
|
+
updatedAt?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface List {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
type: 'checklist' | 'kanban' | 'list' | string;
|
|
16
|
+
items: ListItem[];
|
|
17
|
+
metadata?: Record<string, any>;
|
|
18
|
+
createdAt?: string;
|
|
19
|
+
updatedAt?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ListsProvider {
|
|
22
|
+
getList(id: string): Promise<List>;
|
|
23
|
+
listLists(params?: Record<string, any>): Promise<List[]>;
|
|
24
|
+
createList(data: Partial<List>): Promise<List>;
|
|
25
|
+
updateList(id: string, data: Partial<List>): Promise<List>;
|
|
26
|
+
deleteList(id: string): Promise<void>;
|
|
27
|
+
addItem(listId: string, item: Partial<ListItem>): Promise<ListItem>;
|
|
28
|
+
updateItem(listId: string, itemId: string, data: Partial<ListItem>): Promise<ListItem>;
|
|
29
|
+
deleteItem(listId: string, itemId: string): Promise<void>;
|
|
30
|
+
reorderItems?(listId: string, itemIds: string[]): Promise<void>;
|
|
31
|
+
toggleComplete?(listId: string, itemId: string, completed: boolean): Promise<ListItem>;
|
|
32
|
+
setDueDate?(listId: string, itemId: string, dueDate: string | null): Promise<ListItem>;
|
|
33
|
+
setReminder?(listId: string, itemId: string, reminder: string | null): Promise<ListItem>;
|
|
34
|
+
setPriority?(listId: string, itemId: string, priority: number | null): Promise<ListItem>;
|
|
35
|
+
shareList?(listId: string, userId: string, role?: string): Promise<void>;
|
|
36
|
+
searchItems?(listId: string, query: string): Promise<ListItem[]>;
|
|
37
|
+
archiveList?(listId: string): Promise<void>;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function useLists(): {
|
|
2
|
+
getList: (id: string) => Promise<import("./types").List>;
|
|
3
|
+
listLists: (params?: Record<string, any>) => Promise<import("./types").List[]>;
|
|
4
|
+
createList: (data: Partial<import("./types").List>) => Promise<import("./types").List>;
|
|
5
|
+
updateList: (id: string, data: Partial<import("./types").List>) => Promise<import("./types").List>;
|
|
6
|
+
deleteList: (id: string) => Promise<void>;
|
|
7
|
+
addItem: (listId: string, item: Partial<import("./types").ListItem>) => Promise<import("./types").ListItem>;
|
|
8
|
+
updateItem: (listId: string, itemId: string, data: Partial<import("./types").ListItem>) => Promise<import("./types").ListItem>;
|
|
9
|
+
deleteItem: (listId: string, itemId: string) => Promise<void>;
|
|
10
|
+
reorderItems: ((listId: string, itemIds: string[]) => Promise<void>) | undefined;
|
|
11
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getListsConfig } from './config';
|
|
2
|
+
import { getListsProvider } from './registry';
|
|
3
|
+
export function useLists() {
|
|
4
|
+
const { provider } = getListsConfig();
|
|
5
|
+
const lists = getListsProvider(provider);
|
|
6
|
+
return {
|
|
7
|
+
getList: lists.getList,
|
|
8
|
+
listLists: lists.listLists,
|
|
9
|
+
createList: lists.createList,
|
|
10
|
+
updateList: lists.updateList,
|
|
11
|
+
deleteList: lists.deleteList,
|
|
12
|
+
addItem: lists.addItem,
|
|
13
|
+
updateItem: lists.updateItem,
|
|
14
|
+
deleteItem: lists.deleteItem,
|
|
15
|
+
reorderItems: lists.reorderItems
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getListsProvider } from '../registry';
|
|
2
|
+
import { getListsConfig } from '../config';
|
|
3
|
+
export async function checkListsProviderHealth(providerName) {
|
|
4
|
+
try {
|
|
5
|
+
const cfg = getListsConfig();
|
|
6
|
+
const name = providerName || cfg.provider || 'directus';
|
|
7
|
+
const provider = getListsProvider(name);
|
|
8
|
+
const lists = await provider.listLists();
|
|
9
|
+
return { ok: true, count: Array.isArray(lists) ? lists.length : null };
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
return { ok: false, error: e?.message || String(e) };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export default checkListsProviderHealth;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { List, ListItem } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a raw provider list into Meeovi's List shape.
|
|
4
|
+
*/
|
|
5
|
+
export declare function transformList(raw: any): List;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a raw provider item into Meeovi's ListItem shape.
|
|
8
|
+
*/
|
|
9
|
+
export declare function transformItem(raw: any): ListItem;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize arrays safely.
|
|
12
|
+
*/
|
|
13
|
+
export declare function transformListArray(raw: any[]): List[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a raw provider list into Meeovi's List shape.
|
|
3
|
+
*/
|
|
4
|
+
export function transformList(raw) {
|
|
5
|
+
return {
|
|
6
|
+
id: raw.id,
|
|
7
|
+
title: raw.title ?? raw.name ?? 'Untitled',
|
|
8
|
+
type: raw.type ?? 'list',
|
|
9
|
+
items: Array.isArray(raw.items)
|
|
10
|
+
? raw.items.map(transformItem)
|
|
11
|
+
: [],
|
|
12
|
+
metadata: raw.metadata ?? {},
|
|
13
|
+
createdAt: raw.createdAt ?? raw.created_at ?? null,
|
|
14
|
+
updatedAt: raw.updatedAt ?? raw.updated_at ?? null
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a raw provider item into Meeovi's ListItem shape.
|
|
19
|
+
*/
|
|
20
|
+
export function transformItem(raw) {
|
|
21
|
+
return {
|
|
22
|
+
id: raw.id,
|
|
23
|
+
title: raw.title ?? raw.name ?? '',
|
|
24
|
+
description: raw.description ?? raw.body ?? '',
|
|
25
|
+
completed: raw.completed ?? raw.done ?? false,
|
|
26
|
+
position: raw.position ?? raw.order ?? 0,
|
|
27
|
+
parentId: raw.parentId ?? raw.parent_id ?? null,
|
|
28
|
+
metadata: raw.metadata ?? {},
|
|
29
|
+
createdAt: raw.createdAt ?? raw.created_at ?? null,
|
|
30
|
+
updatedAt: raw.updatedAt ?? raw.updated_at ?? null
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Normalize arrays safely.
|
|
35
|
+
*/
|
|
36
|
+
export function transformListArray(raw) {
|
|
37
|
+
return raw.map(transformList);
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function validateListInput(data) {
|
|
2
|
+
if (!data.title || typeof data.title !== 'string') {
|
|
3
|
+
throw new Error('List title is required and must be a string');
|
|
4
|
+
}
|
|
5
|
+
if (data.type && typeof data.type !== 'string') {
|
|
6
|
+
throw new Error('List type must be a string');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function validateItemInput(data) {
|
|
10
|
+
if (!data.title || typeof data.title !== 'string') {
|
|
11
|
+
throw new Error('Item title is required and must be a string');
|
|
12
|
+
}
|
|
13
|
+
if (data.completed !== undefined && typeof data.completed !== 'boolean') {
|
|
14
|
+
throw new Error('Item completed must be a boolean');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '#shared/app/composables/globals/useDirectusForm';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '#shared/app/composables/globals/useDirectusForm';
|