@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.
Files changed (134) hide show
  1. package/README.md +111 -0
  2. package/app/composables/config.ts +1 -1
  3. package/app/composables/module.ts +75 -0
  4. package/app/composables/providers/directus.ts +112 -16
  5. package/app/composables/providers/memory.ts +39 -0
  6. package/app/composables/registry.ts +5 -0
  7. package/app/composables/types.ts +9 -0
  8. package/app/composables/utils/health.ts +16 -0
  9. package/dist/app/composables/config.d.ts +7 -0
  10. package/dist/app/composables/config.js +9 -0
  11. package/dist/app/composables/globals/useDirectusForm.d.ts +4 -0
  12. package/dist/app/composables/globals/useDirectusForm.js +10 -0
  13. package/dist/app/composables/module.d.ts +7 -0
  14. package/dist/app/composables/module.js +60 -0
  15. package/dist/app/composables/providers/atproto.d.ts +1 -0
  16. package/dist/app/composables/providers/atproto.js +133 -0
  17. package/dist/app/composables/providers/directus.d.ts +1 -0
  18. package/dist/app/composables/providers/directus.js +130 -0
  19. package/dist/app/composables/providers/memory.d.ts +1 -0
  20. package/dist/app/composables/providers/memory.js +110 -0
  21. package/dist/app/composables/registry.d.ts +4 -0
  22. package/dist/app/composables/registry.js +14 -0
  23. package/dist/app/composables/types.d.ts +38 -0
  24. package/dist/app/composables/types.js +1 -0
  25. package/dist/app/composables/useLists.d.ts +11 -0
  26. package/dist/app/composables/useLists.js +17 -0
  27. package/dist/app/composables/utils/health.d.ts +10 -0
  28. package/dist/app/composables/utils/health.js +15 -0
  29. package/dist/app/composables/utils/transforms.d.ts +13 -0
  30. package/dist/app/composables/utils/transforms.js +38 -0
  31. package/dist/app/composables/utils/validation.d.ts +3 -0
  32. package/dist/app/composables/utils/validation.js +16 -0
  33. package/dist/layers/lists/app/composables/config.d.ts +7 -0
  34. package/dist/layers/lists/app/composables/config.js +9 -0
  35. package/dist/layers/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
  36. package/dist/layers/lists/app/composables/globals/useDirectusForm.js +1 -0
  37. package/dist/layers/lists/app/composables/module.d.ts +7 -0
  38. package/dist/layers/lists/app/composables/module.js +60 -0
  39. package/dist/layers/lists/app/composables/providers/atproto.d.ts +1 -0
  40. package/dist/layers/lists/app/composables/providers/atproto.js +133 -0
  41. package/dist/layers/lists/app/composables/providers/directus.d.ts +1 -0
  42. package/dist/layers/lists/app/composables/providers/directus.js +130 -0
  43. package/dist/layers/lists/app/composables/providers/memory.d.ts +1 -0
  44. package/dist/layers/lists/app/composables/providers/memory.js +110 -0
  45. package/dist/layers/lists/app/composables/registry.d.ts +4 -0
  46. package/dist/layers/lists/app/composables/registry.js +14 -0
  47. package/dist/layers/lists/app/composables/types.d.ts +38 -0
  48. package/dist/layers/lists/app/composables/types.js +1 -0
  49. package/dist/layers/lists/app/composables/useLists.d.ts +11 -0
  50. package/dist/layers/lists/app/composables/useLists.js +17 -0
  51. package/dist/layers/lists/app/composables/utils/health.d.ts +10 -0
  52. package/dist/layers/lists/app/composables/utils/health.js +15 -0
  53. package/dist/layers/lists/app/composables/utils/transforms.d.ts +13 -0
  54. package/dist/layers/lists/app/composables/utils/transforms.js +38 -0
  55. package/dist/layers/lists/app/composables/utils/validation.d.ts +3 -0
  56. package/dist/layers/lists/app/composables/utils/validation.js +16 -0
  57. package/dist/layers/lists/nuxt.config.d.ts +2 -0
  58. package/dist/layers/lists/nuxt.config.js +7 -0
  59. package/dist/layers/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
  60. package/dist/layers/shared/app/composables/globals/useDirectusForm.js +66 -0
  61. package/dist/lists/app/composables/config.d.ts +7 -0
  62. package/dist/lists/app/composables/config.js +9 -0
  63. package/dist/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
  64. package/dist/lists/app/composables/globals/useDirectusForm.js +1 -0
  65. package/dist/lists/app/composables/module.d.ts +7 -0
  66. package/dist/lists/app/composables/module.js +60 -0
  67. package/dist/lists/app/composables/providers/atproto.d.ts +1 -0
  68. package/dist/lists/app/composables/providers/atproto.js +133 -0
  69. package/dist/lists/app/composables/providers/directus.d.ts +1 -0
  70. package/dist/lists/app/composables/providers/directus.js +130 -0
  71. package/dist/lists/app/composables/providers/memory.d.ts +1 -0
  72. package/dist/lists/app/composables/providers/memory.js +110 -0
  73. package/dist/lists/app/composables/registry.d.ts +4 -0
  74. package/dist/lists/app/composables/registry.js +14 -0
  75. package/dist/lists/app/composables/types.d.ts +38 -0
  76. package/dist/lists/app/composables/types.js +1 -0
  77. package/dist/lists/app/composables/useLists.d.ts +11 -0
  78. package/dist/lists/app/composables/useLists.js +17 -0
  79. package/dist/lists/app/composables/utils/health.d.ts +10 -0
  80. package/dist/lists/app/composables/utils/health.js +15 -0
  81. package/dist/lists/app/composables/utils/transforms.d.ts +13 -0
  82. package/dist/lists/app/composables/utils/transforms.js +38 -0
  83. package/dist/lists/app/composables/utils/validation.d.ts +3 -0
  84. package/dist/lists/app/composables/utils/validation.js +16 -0
  85. package/dist/lists/nuxt.config.d.ts +2 -0
  86. package/dist/lists/nuxt.config.js +7 -0
  87. package/dist/nuxt.config.d.ts +2 -0
  88. package/dist/nuxt.config.js +7 -0
  89. package/dist/packages/core/index.d.ts +17 -0
  90. package/dist/packages/core/index.js +18 -0
  91. package/dist/packages/core/src/adapters/auth.d.ts +9 -0
  92. package/dist/packages/core/src/adapters/auth.js +1 -0
  93. package/dist/packages/core/src/adapters/cart.d.ts +22 -0
  94. package/dist/packages/core/src/adapters/cart.js +1 -0
  95. package/dist/packages/core/src/adapters/catalog.d.ts +17 -0
  96. package/dist/packages/core/src/adapters/catalog.js +1 -0
  97. package/dist/packages/core/src/adapters/common.d.ts +9 -0
  98. package/dist/packages/core/src/adapters/common.js +1 -0
  99. package/dist/packages/core/src/adapters/lists.d.ts +17 -0
  100. package/dist/packages/core/src/adapters/lists.js +1 -0
  101. package/dist/packages/core/src/adapters/search.d.ts +21 -0
  102. package/dist/packages/core/src/adapters/search.js +1 -0
  103. package/dist/packages/core/src/plugins/defineAdapter.d.ts +2 -0
  104. package/dist/packages/core/src/plugins/defineAdapter.js +3 -0
  105. package/dist/packages/core/src/plugins/defineModule.d.ts +2 -0
  106. package/dist/packages/core/src/plugins/defineModule.js +3 -0
  107. package/dist/packages/core/src/plugins/registry.d.ts +14 -0
  108. package/dist/packages/core/src/plugins/registry.js +46 -0
  109. package/dist/packages/core/src/runtime/app.d.ts +2 -0
  110. package/dist/packages/core/src/runtime/app.js +20 -0
  111. package/dist/packages/core/src/runtime/config.d.ts +7 -0
  112. package/dist/packages/core/src/runtime/config.js +9 -0
  113. package/dist/packages/core/src/runtime/context.d.ts +9 -0
  114. package/dist/packages/core/src/runtime/context.js +11 -0
  115. package/dist/packages/core/src/runtime/hooks.d.ts +5 -0
  116. package/dist/packages/core/src/runtime/hooks.js +18 -0
  117. package/dist/packages/core/src/runtime/lifecycle.d.ts +4 -0
  118. package/dist/packages/core/src/runtime/lifecycle.js +5 -0
  119. package/dist/packages/core/src/types/adapters.d.ts +14 -0
  120. package/dist/packages/core/src/types/adapters.js +1 -0
  121. package/dist/packages/core/src/types/app.d.ts +13 -0
  122. package/dist/packages/core/src/types/app.js +1 -0
  123. package/dist/packages/core/src/types/config.d.ts +8 -0
  124. package/dist/packages/core/src/types/config.js +1 -0
  125. package/dist/packages/core/src/types/events.d.ts +20 -0
  126. package/dist/packages/core/src/types/events.js +22 -0
  127. package/dist/packages/core/src/types/module.d.ts +15 -0
  128. package/dist/packages/core/src/types/module.js +1 -0
  129. package/dist/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
  130. package/dist/shared/app/composables/globals/useDirectusForm.js +66 -0
  131. package/global.d.ts +1 -0
  132. package/package.json +4 -1
  133. package/shims.d.ts +7 -0
  134. package/tsconfig.json +21 -0
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # @meeovi/layer-lists
2
+
3
+ This layer provides first-class lists and task management functionality for the Alternate Framework. It's provider-driven: you can plug in different backends (Directus, memory, ATProto, etc.) without changing UI code. The default production-ready fallback is the `@meeovi/directus-client` provider.
4
+
5
+ Key goals:
6
+ - Provider-agnostic API used by the UI (`useLists()` composable).
7
+ - Advanced task features (due dates, reminders, priority, sharing, search, archive) exposed as optional provider methods.
8
+ - Production-ready defaults: Directus provider, health checks, graceful fallbacks.
9
+
10
+ Quick start
11
+
12
+ Install the layer into your project (monorepo/local install):
13
+
14
+ ```bash
15
+ npm install @meeovi/layer-lists
16
+ # also install your chosen provider (Directus example)
17
+ npm install @meeovi/directus-client
18
+ ```
19
+
20
+ Register the layer and configure the provider in your Alternate app (Directus example):
21
+
22
+ ```ts
23
+ import lists from '@meeovi/layer-lists'
24
+
25
+ const app = createAlternateApp({
26
+ config: {
27
+ lists: {
28
+ provider: 'directus',
29
+ directus: {
30
+ baseUrl: 'https://cms.example.com',
31
+ apiKey: process.env.DIRECTUS_KEY
32
+ }
33
+ }
34
+ },
35
+ modules: [lists]
36
+ })
37
+
38
+ await app.start()
39
+ ```
40
+
41
+ Using in a Nuxt (modules) app
42
+
43
+ 1. Install the packages into your Nuxt app project.
44
+ 2. Add the layer module to your Nuxt alternate app bootstrap (see above).
45
+ 3. In Vue components or composables, use the `useLists()` composable the same way as in the Alternate UI:
46
+
47
+ ```ts
48
+ const { listLists, createList, addItem, updateItem } = useLists()
49
+
50
+ const lists = await listLists()
51
+ await createList({ title: 'Groceries' })
52
+ await addItem('list-id', { title: 'Buy milk', metadata: { dueDate: '2026-02-01' } })
53
+ ```
54
+
55
+ Advanced features
56
+
57
+ Providers may implement optional advanced methods:
58
+ - `toggleComplete(listId, itemId, completed)`
59
+ - `setDueDate(listId, itemId, dueDate)`
60
+ - `setReminder(listId, itemId, reminder)`
61
+ - `setPriority(listId, itemId, priority)`
62
+ - `shareList(listId, userId, role)`
63
+ - `searchItems(listId, query)`
64
+ - `archiveList(listId)`
65
+
66
+ If a provider doesn't implement these methods, the `memory` provider includes sensible defaults and the `directus` provider attempts fallbacks to existing endpoints when possible.
67
+
68
+ Creating a provider
69
+
70
+ Implement a `ListsProvider` and register it with `registerListsProvider(name, provider)`.
71
+
72
+ Example skeleton (register at runtime):
73
+
74
+ ```ts
75
+ import { registerListsProvider } from '@meeovi/layer-lists/app/composables/registry'
76
+
77
+ const MyProvider = {
78
+ async getList(id) { /* ... */ },
79
+ async listLists() { /* ... */ },
80
+ async createList(data) { /* ... */ },
81
+ async addItem(listId, item) { /* ... */ },
82
+ // ... implement optional methods as desired
83
+ }
84
+
85
+ registerListsProvider('my-provider', MyProvider)
86
+ ```
87
+
88
+ Runtime registration
89
+
90
+ Other modules can register providers at runtime using `registerListsProviderRuntime(name, provider)`.
91
+
92
+ Health checks
93
+
94
+ Use the provided `checkListsProviderHealth()` helper to validate that the configured provider is reachable (it performs a lightweight `listLists()` call). Example:
95
+
96
+ ```ts
97
+ import { checkListsProviderHealth } from '@meeovi/layer-lists/app/composables/utils/health'
98
+
99
+ const healthy = await checkListsProviderHealth()
100
+ if (!healthy) {
101
+ // fallback or alert
102
+ }
103
+ ```
104
+
105
+ What's next
106
+
107
+ - Add server-side sync/notifications (webhooks) for reminders.
108
+ - Add tests and CI for providers and bridges.
109
+ - Provide an Express example wiring server-side endpoints to the Directus client.
110
+
111
+ License: MIT
@@ -5,7 +5,7 @@ export interface ListsConfig {
5
5
  }
6
6
 
7
7
  let config: ListsConfig = {
8
- provider: 'memory'
8
+ provider: 'directus'
9
9
  }
10
10
 
11
11
  export function setListsConfig(newConfig: Partial<ListsConfig>) {
@@ -0,0 +1,75 @@
1
+ import {
2
+ defineAlternateModule,
3
+ useAlternateEventBus,
4
+ useAlternateContext,
5
+ type AlternateContext,
6
+ type ListsAdapter
7
+ } from '@meeovi/core'
8
+
9
+ import { getListsConfig } from './config'
10
+ import { registerListsProviderRuntime } from './registry'
11
+
12
+ export default defineAlternateModule({
13
+ id: 'lists',
14
+ adapters: {},
15
+
16
+ async setup(ctx: AlternateContext) {
17
+ const bus = useAlternateEventBus()
18
+ const config = getListsConfig()
19
+
20
+ // If a core adapter was registered under the `lists` key, register it
21
+ // into the local provider registry so UI code can consume it via `useLists()`.
22
+ // If a core adapter was registered under the `lists` key, adapt it
23
+ // into the `ListsProvider` shape and register it into the local registry.
24
+ try {
25
+ const runtimeAdapter = ctx.getAdapter('lists' as any) as ListsAdapter | undefined
26
+ if (runtimeAdapter) {
27
+ const provider = {
28
+ getList: (id: string) => runtimeAdapter.getList(id),
29
+ listLists: (params?: Record<string, unknown>) => runtimeAdapter.listLists(params),
30
+ createList: (data: Partial<Record<string, unknown>>) => runtimeAdapter.createList(data),
31
+ updateList: (id: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateList(id, data),
32
+ deleteList: (id: string) => runtimeAdapter.deleteList(id),
33
+
34
+ addItem: (listId: string, item: Partial<Record<string, unknown>>) => runtimeAdapter.addItem(listId, item),
35
+ updateItem: (listId: string, itemId: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateItem(listId, itemId, data),
36
+ deleteItem: (listId: string, itemId: string) => runtimeAdapter.deleteItem(listId, itemId),
37
+
38
+ reorderItems: runtimeAdapter.reorderItems ? (listId: string, itemIds: string[]) => runtimeAdapter.reorderItems!(listId, itemIds) : undefined
39
+ }
40
+
41
+ registerListsProviderRuntime('core', provider)
42
+ }
43
+ } catch (e) {
44
+ // noop
45
+ }
46
+
47
+ // Listen for runtime adapter registrations from ModuleRegistry
48
+ bus.on('adapter:registered' as any, (payload: any) => {
49
+ try {
50
+ if (payload?.key === 'lists') {
51
+ const runtimeAdapter = ctx.getAdapter('lists' as any) as ListsAdapter | undefined
52
+ if (runtimeAdapter) {
53
+ const provider = {
54
+ getList: (id: string) => runtimeAdapter.getList(id),
55
+ listLists: (params?: Record<string, unknown>) => runtimeAdapter.listLists(params),
56
+ createList: (data: Partial<Record<string, unknown>>) => runtimeAdapter.createList(data),
57
+ updateList: (id: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateList(id, data),
58
+ deleteList: (id: string) => runtimeAdapter.deleteList(id),
59
+
60
+ addItem: (listId: string, item: Partial<Record<string, unknown>>) => runtimeAdapter.addItem(listId, item),
61
+ updateItem: (listId: string, itemId: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateItem(listId, itemId, data),
62
+ deleteItem: (listId: string, itemId: string) => runtimeAdapter.deleteItem(listId, itemId),
63
+
64
+ reorderItems: runtimeAdapter.reorderItems ? (listId: string, itemIds: string[]) => runtimeAdapter.reorderItems!(listId, itemIds) : undefined
65
+ }
66
+
67
+ registerListsProviderRuntime('core', provider)
68
+ }
69
+ }
70
+ } catch (e) {
71
+ /* noop */
72
+ }
73
+ })
74
+ }
75
+ })
@@ -1,48 +1,144 @@
1
1
  import { registerListsProvider } from '../registry'
2
2
  import type { ListsProvider } from '../types'
3
- import { fetcher } from '@meeovi/api'
3
+ import { getListsConfig } from '../config'
4
+
5
+ async function directusFetch(path: string, options: RequestInit = {}) {
6
+ const cfg = getListsConfig()
7
+ const base = cfg.baseUrl?.replace(/\/$/, '') || ''
8
+
9
+ const res = await fetch(`${base}${path}`, {
10
+ ...options,
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ ...(cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}),
14
+ ...(options.headers || {})
15
+ }
16
+ })
17
+
18
+ if (!res.ok) {
19
+ const err: any = new Error(`Directus error: ${res.status}`)
20
+ err.status = res.status
21
+ err.response = res
22
+ throw err
23
+ }
24
+
25
+ return res.json()
26
+ }
4
27
 
5
28
  const DirectusListsProvider: ListsProvider = {
6
29
  async getList(id) {
7
- const { data } = await fetcher('lists.GET_LIST', { id })
8
- return data.list
30
+ const json = await directusFetch(`/lists/${id}`)
31
+ return json.list ?? json
9
32
  },
10
33
 
11
34
  async listLists() {
12
- const { data } = await fetcher('lists.LIST_LISTS')
13
- return data.lists
35
+ const json = await directusFetch(`/lists`)
36
+ return json.lists ?? json
14
37
  },
15
38
 
16
39
  async createList(data) {
17
- const { data: result } = await fetcher('lists.CREATE_LIST', { data })
18
- return result.list
40
+ const json = await directusFetch(`/lists`, { method: 'POST', body: JSON.stringify(data) })
41
+ return json.list ?? json
19
42
  },
20
43
 
21
44
  async updateList(id, data) {
22
- const { data: result } = await fetcher('lists.UPDATE_LIST', { id, data })
23
- return result.list
45
+ const json = await directusFetch(`/lists/${id}`, { method: 'PUT', body: JSON.stringify(data) })
46
+ return json.list ?? json
24
47
  },
25
48
 
26
49
  async deleteList(id) {
27
- await fetcher('lists.DELETE_LIST', { id })
50
+ await directusFetch(`/lists/${id}`, { method: 'DELETE' })
28
51
  },
29
52
 
30
53
  async addItem(listId, item) {
31
- const { data } = await fetcher('lists.ADD_ITEM', { listId, item })
32
- return data.item
54
+ const json = await directusFetch(`/lists/${listId}/items`, { method: 'POST', body: JSON.stringify(item) })
55
+ return json.item ?? json
33
56
  },
34
57
 
35
58
  async updateItem(listId, itemId, data) {
36
- const { data: result } = await fetcher('lists.UPDATE_ITEM', { listId, itemId, data })
37
- return result.item
59
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify(data) })
60
+ return json.item ?? json
38
61
  },
39
62
 
40
63
  async deleteItem(listId, itemId) {
41
- await fetcher('lists.DELETE_ITEM', { listId, itemId })
64
+ await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'DELETE' })
42
65
  },
43
66
 
44
67
  async reorderItems(listId, itemIds) {
45
- await fetcher('lists.REORDER_ITEMS', { listId, itemIds })
68
+ await directusFetch(`/lists/${listId}/reorder`, { method: 'POST', body: JSON.stringify({ items: itemIds }) })
69
+ },
70
+
71
+ async toggleComplete(listId, itemId, completed = true) {
72
+ try {
73
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/toggle`, { method: 'POST', body: JSON.stringify({ completed }) })
74
+ return json.item ?? json
75
+ } catch (e) {
76
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ completed }) })
77
+ return json.item ?? json
78
+ }
79
+ },
80
+
81
+ async setDueDate(listId, itemId, dueDate) {
82
+ try {
83
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/due`, { method: 'POST', body: JSON.stringify({ dueDate }) })
84
+ return json.item ?? json
85
+ } catch (e) {
86
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { dueDate } }) })
87
+ return json.item ?? json
88
+ }
89
+ },
90
+
91
+ async setReminder(listId, itemId, reminder) {
92
+ try {
93
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/reminder`, { method: 'POST', body: JSON.stringify({ reminder }) })
94
+ return json.item ?? json
95
+ } catch (e) {
96
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { reminder } }) })
97
+ return json.item ?? json
98
+ }
99
+ },
100
+
101
+ async setPriority(listId, itemId, priority) {
102
+ try {
103
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/priority`, { method: 'POST', body: JSON.stringify({ priority }) })
104
+ return json.item ?? json
105
+ } catch (e) {
106
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { priority } }) })
107
+ return json.item ?? json
108
+ }
109
+ },
110
+
111
+ async shareList(listId, userId, role = 'editor') {
112
+ try {
113
+ await directusFetch(`/lists/${listId}/share`, { method: 'POST', body: JSON.stringify({ userId, role }) })
114
+ } catch (e) {
115
+ const json = await directusFetch(`/lists/${listId}`)
116
+ const list = json.list ?? json
117
+ const collaborators = (list.metadata?.collaborators || []).concat({ userId, role })
118
+ await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), collaborators } }) })
119
+ }
120
+ },
121
+
122
+ async searchItems(listId, query) {
123
+ try {
124
+ const json = await directusFetch(`/lists/${listId}/search?q=${encodeURIComponent(String(query))}`)
125
+ return json.items ?? json
126
+ } catch (e) {
127
+ const json = await directusFetch(`/lists/${listId}`)
128
+ const list = json.list ?? json
129
+ const q = String(query).toLowerCase()
130
+ return list.items.filter((i: any) => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q))
131
+ }
132
+ },
133
+
134
+ async archiveList(listId) {
135
+ try {
136
+ await directusFetch(`/lists/${listId}/archive`, { method: 'POST' })
137
+ } catch (e) {
138
+ const json = await directusFetch(`/lists/${listId}`)
139
+ const list = json.list ?? json
140
+ await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), archived: true } }) })
141
+ }
46
142
  }
47
143
  }
48
144
 
@@ -82,6 +82,45 @@ const MemoryListsProvider: ListsProvider = {
82
82
  ...item,
83
83
  position: index
84
84
  }))
85
+ },
86
+
87
+ async toggleComplete(listId, itemId, completed = true) {
88
+ const item = await this.updateItem(listId, itemId, { completed })
89
+ return item
90
+ },
91
+
92
+ async setDueDate(listId, itemId, dueDate) {
93
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), dueDate } })
94
+ return item
95
+ },
96
+
97
+ async setReminder(listId, itemId, reminder) {
98
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), reminder } })
99
+ return item
100
+ },
101
+
102
+ async setPriority(listId, itemId, priority) {
103
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), priority } })
104
+ return item
105
+ },
106
+
107
+ async shareList(listId, userId, role = 'editor') {
108
+ // memory store: attach collaborators in list.metadata.collaborators
109
+ const list = await this.getList(listId)
110
+ const collaborators = (list.metadata?.collaborators as any[]) || []
111
+ collaborators.push({ userId, role })
112
+ list.metadata = { ...(list.metadata || {}), collaborators }
113
+ },
114
+
115
+ async searchItems(listId, query) {
116
+ const list = await this.getList(listId)
117
+ const q = String(query).toLowerCase()
118
+ return list.items.filter(i => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q))
119
+ },
120
+
121
+ async archiveList(listId) {
122
+ const list = await this.getList(listId)
123
+ list.metadata = { ...(list.metadata || {}), archived: true }
85
124
  }
86
125
  }
87
126
 
@@ -6,6 +6,11 @@ export function registerListsProvider(name: string, provider: ListsProvider) {
6
6
  providers[name] = provider
7
7
  }
8
8
 
9
+ // Allow runtime registration from other modules (e.g. via core ModuleRegistry adapters)
10
+ export function registerListsProviderRuntime(name: string, provider: ListsProvider) {
11
+ registerListsProvider(name, provider)
12
+ }
13
+
9
14
  export function getListsProvider(name: string): ListsProvider {
10
15
  const provider = providers[name]
11
16
  if (!provider) throw new Error(`Lists provider "${name}" not found`)
@@ -32,4 +32,13 @@ export interface ListsProvider {
32
32
  deleteItem(listId: string, itemId: string): Promise<void>
33
33
 
34
34
  reorderItems?(listId: string, itemIds: string[]): Promise<void>
35
+
36
+ // Optional advanced features commonly found in task apps
37
+ toggleComplete?(listId: string, itemId: string, completed: boolean): Promise<ListItem>
38
+ setDueDate?(listId: string, itemId: string, dueDate: string | null): Promise<ListItem>
39
+ setReminder?(listId: string, itemId: string, reminder: string | null): Promise<ListItem>
40
+ setPriority?(listId: string, itemId: string, priority: number | null): Promise<ListItem>
41
+ shareList?(listId: string, userId: string, role?: string): Promise<void>
42
+ searchItems?(listId: string, query: string): Promise<ListItem[]>
43
+ archiveList?(listId: string): Promise<void>
35
44
  }
@@ -0,0 +1,16 @@
1
+ import { getListsProvider } from '../registry'
2
+ import { getListsConfig } from '../config'
3
+
4
+ export async function checkListsProviderHealth(providerName?: string) {
5
+ try {
6
+ const cfg = getListsConfig()
7
+ const name = providerName || cfg.provider || 'directus'
8
+ const provider = getListsProvider(name)
9
+ const lists = await provider.listLists()
10
+ return { ok: true, count: Array.isArray(lists) ? lists.length : null }
11
+ } catch (e: any) {
12
+ return { ok: false, error: e?.message || String(e) }
13
+ }
14
+ }
15
+
16
+ export default checkListsProviderHealth
@@ -0,0 +1,7 @@
1
+ export interface ListsConfig {
2
+ provider: string;
3
+ baseUrl?: string;
4
+ apiKey?: string;
5
+ }
6
+ export declare function setListsConfig(newConfig: Partial<ListsConfig>): void;
7
+ export declare function getListsConfig(): ListsConfig;
@@ -0,0 +1,9 @@
1
+ let config = {
2
+ provider: 'directus'
3
+ };
4
+ export function setListsConfig(newConfig) {
5
+ config = { ...config, ...newConfig };
6
+ }
7
+ export function getListsConfig() {
8
+ return config;
9
+ }
@@ -0,0 +1,4 @@
1
+ export declare function useDirectusForm(..._args: any[]): {
2
+ data: any;
3
+ save: () => Promise<never>;
4
+ };
@@ -0,0 +1,10 @@
1
+ // Light stub for isolated typechecks and builds.
2
+ // In the real app this re-exports from the shared layer.
3
+ export function useDirectusForm(..._args) {
4
+ return {
5
+ data: null,
6
+ save: async () => {
7
+ throw new Error('useDirectusForm stub - not available in isolated build');
8
+ }
9
+ };
10
+ }
@@ -0,0 +1,7 @@
1
+ import { type AlternateContext } from '@meeovi/core';
2
+ declare const _default: {
3
+ id: string;
4
+ adapters: {};
5
+ setup(ctx: AlternateContext): Promise<void>;
6
+ };
7
+ export default _default;
@@ -0,0 +1,60 @@
1
+ import { defineAlternateModule, useAlternateEventBus } from '@meeovi/core';
2
+ import { getListsConfig } from './config';
3
+ import { registerListsProviderRuntime } from './registry';
4
+ export default defineAlternateModule({
5
+ id: 'lists',
6
+ adapters: {},
7
+ async setup(ctx) {
8
+ const bus = useAlternateEventBus();
9
+ const config = getListsConfig();
10
+ // If a core adapter was registered under the `lists` key, register it
11
+ // into the local provider registry so UI code can consume it via `useLists()`.
12
+ // If a core adapter was registered under the `lists` key, adapt it
13
+ // into the `ListsProvider` shape and register it into the local registry.
14
+ try {
15
+ const runtimeAdapter = ctx.getAdapter('lists');
16
+ if (runtimeAdapter) {
17
+ const provider = {
18
+ getList: (id) => runtimeAdapter.getList(id),
19
+ listLists: (params) => runtimeAdapter.listLists(params),
20
+ createList: (data) => runtimeAdapter.createList(data),
21
+ updateList: (id, data) => runtimeAdapter.updateList(id, data),
22
+ deleteList: (id) => runtimeAdapter.deleteList(id),
23
+ addItem: (listId, item) => runtimeAdapter.addItem(listId, item),
24
+ updateItem: (listId, itemId, data) => runtimeAdapter.updateItem(listId, itemId, data),
25
+ deleteItem: (listId, itemId) => runtimeAdapter.deleteItem(listId, itemId),
26
+ reorderItems: runtimeAdapter.reorderItems ? (listId, itemIds) => runtimeAdapter.reorderItems(listId, itemIds) : undefined
27
+ };
28
+ registerListsProviderRuntime('core', provider);
29
+ }
30
+ }
31
+ catch (e) {
32
+ // noop
33
+ }
34
+ // Listen for runtime adapter registrations from ModuleRegistry
35
+ bus.on('adapter:registered', (payload) => {
36
+ try {
37
+ if (payload?.key === 'lists') {
38
+ const runtimeAdapter = ctx.getAdapter('lists');
39
+ if (runtimeAdapter) {
40
+ const provider = {
41
+ getList: (id) => runtimeAdapter.getList(id),
42
+ listLists: (params) => runtimeAdapter.listLists(params),
43
+ createList: (data) => runtimeAdapter.createList(data),
44
+ updateList: (id, data) => runtimeAdapter.updateList(id, data),
45
+ deleteList: (id) => runtimeAdapter.deleteList(id),
46
+ addItem: (listId, item) => runtimeAdapter.addItem(listId, item),
47
+ updateItem: (listId, itemId, data) => runtimeAdapter.updateItem(listId, itemId, data),
48
+ deleteItem: (listId, itemId) => runtimeAdapter.deleteItem(listId, itemId),
49
+ reorderItems: runtimeAdapter.reorderItems ? (listId, itemIds) => runtimeAdapter.reorderItems(listId, itemIds) : undefined
50
+ };
51
+ registerListsProviderRuntime('core', provider);
52
+ }
53
+ }
54
+ }
55
+ catch (e) {
56
+ /* noop */
57
+ }
58
+ });
59
+ }
60
+ });
@@ -0,0 +1 @@
1
+ export {};