@lenne.tech/nuxt-extensions 1.20.1 → 1.22.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/CLAUDE.md +65 -0
- package/README.md +122 -0
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +15 -1
- package/dist/runtime/components/transition/LtTransitionFade.d.vue.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionFade.vue.d.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionFadeScale.d.vue.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionFadeScale.vue.d.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlide.d.vue.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlide.vue.d.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlideBottom.d.vue.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlideBottom.vue.d.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlideRevert.d.vue.ts +5 -3
- package/dist/runtime/components/transition/LtTransitionSlideRevert.vue.d.ts +5 -3
- package/dist/runtime/composables/auth/use-lt-auth.js +16 -1
- package/dist/runtime/composables/index.d.ts +2 -0
- package/dist/runtime/composables/index.js +2 -0
- package/dist/runtime/composables/use-lt-ai-settings.d.ts +12 -0
- package/dist/runtime/composables/use-lt-ai-settings.js +32 -0
- package/dist/runtime/composables/use-lt-offline-sync.d.ts +21 -0
- package/dist/runtime/composables/use-lt-offline-sync.js +250 -0
- package/dist/runtime/lib/index.d.ts +2 -0
- package/dist/runtime/lib/index.js +2 -0
- package/dist/runtime/lib/sync-hints.d.ts +46 -0
- package/dist/runtime/lib/sync-hints.js +147 -0
- package/dist/runtime/lib/sync-store.d.ts +89 -0
- package/dist/runtime/lib/sync-store.js +149 -0
- package/dist/runtime/lib/sync.d.ts +89 -0
- package/dist/runtime/lib/sync.js +369 -0
- package/dist/runtime/types/ai.d.ts +21 -0
- package/dist/runtime/types/index.d.ts +2 -1
- package/dist/runtime/types/module.d.ts +7 -0
- package/dist/runtime/types/sync.d.ts +127 -0
- package/dist/runtime/types/sync.js +0 -0
- package/package.json +9 -8
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
/** The id format a sync model declares (§3). One per model, never mixed. */
|
|
3
|
+
export type LtSyncIdFormat = 'objectId' | 'uuidv7';
|
|
4
|
+
/**
|
|
5
|
+
* A record as the device holds it. `version` 0 means "created here, not on the server yet";
|
|
6
|
+
* a tombstone has `deletedAt` set and no `data`.
|
|
7
|
+
*/
|
|
8
|
+
export interface LtSyncRecord<T = Record<string, unknown>> {
|
|
9
|
+
data?: T;
|
|
10
|
+
deletedAt: null | string;
|
|
11
|
+
id: string;
|
|
12
|
+
updatedAt: null | string;
|
|
13
|
+
version: number;
|
|
14
|
+
}
|
|
15
|
+
/** Status of one pushed change (§7) */
|
|
16
|
+
export type LtSyncItemStatus = 'applied' | 'conflict' | 'merged' | 'not_found' | 'rejected' | 'retry';
|
|
17
|
+
/** One result of a push, in the order the changes were sent (§6.6) */
|
|
18
|
+
export interface LtSyncPushResult {
|
|
19
|
+
/** Reason of a `rejected` change, e.g. `VALIDATION_FAILED`, `FORBIDDEN`, `ID_UNAVAILABLE` */
|
|
20
|
+
code?: string;
|
|
21
|
+
details?: unknown;
|
|
22
|
+
id: string;
|
|
23
|
+
mutationId: string;
|
|
24
|
+
/** The server's record on `conflict` and `merged` (a tombstone when it was deleted) */
|
|
25
|
+
record?: {
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
deletedAt?: null | string;
|
|
28
|
+
id: string;
|
|
29
|
+
updatedAt?: null | string;
|
|
30
|
+
version: number;
|
|
31
|
+
};
|
|
32
|
+
status: LtSyncItemStatus;
|
|
33
|
+
version?: number;
|
|
34
|
+
}
|
|
35
|
+
/** What `onConflict` receives when the server did not apply a local change */
|
|
36
|
+
export interface LtSyncConflict<T = Record<string, unknown>> {
|
|
37
|
+
/** The local change the server refused */
|
|
38
|
+
change: {
|
|
39
|
+
data?: Partial<T>;
|
|
40
|
+
op: 'delete' | 'upsert';
|
|
41
|
+
};
|
|
42
|
+
/** The local record as it was before the conflict was resolved */
|
|
43
|
+
local: LtSyncRecord<T> | undefined;
|
|
44
|
+
/** The server's current record; a tombstone (`deletedAt` set) when it was deleted there */
|
|
45
|
+
server: LtSyncRecord<T>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The answer of `onConflict`: keep the server's state (`'server'`), or send `data` again on top
|
|
49
|
+
* of it. A record the server deleted stays deleted: an upsert on a tombstone is a conflict again (§6.3).
|
|
50
|
+
*/
|
|
51
|
+
export type LtSyncConflictResolution<T = Record<string, unknown>> = 'server' | {
|
|
52
|
+
data: Partial<T>;
|
|
53
|
+
};
|
|
54
|
+
/** Outcome of one sync run */
|
|
55
|
+
export interface LtSyncResult {
|
|
56
|
+
applied: number;
|
|
57
|
+
/** Conflicts of this run, after `onConflict` (or the server-wins default) resolved them */
|
|
58
|
+
conflicts: LtSyncPushResult[];
|
|
59
|
+
/** The caller may no longer pull the model (403): its local records and cursor are gone */
|
|
60
|
+
forbidden: boolean;
|
|
61
|
+
/** The server answered 401: sign in again. Nothing was applied, nothing was discarded (§4) */
|
|
62
|
+
needsLogin: boolean;
|
|
63
|
+
/** The server could not be reached */
|
|
64
|
+
offline: boolean;
|
|
65
|
+
rejected: LtSyncPushResult[];
|
|
66
|
+
/** A `410` made the client drop its local records and pull everything again (§8) */
|
|
67
|
+
resynced: boolean;
|
|
68
|
+
/** Changes the server answered `retry`; they go out again with the next sync */
|
|
69
|
+
retried: number;
|
|
70
|
+
/** The server asked to slow down (429 / 503): milliseconds until the next attempt */
|
|
71
|
+
retryAfterMs?: number;
|
|
72
|
+
}
|
|
73
|
+
/** Module options (`ltExtensions.sync`) */
|
|
74
|
+
export interface LtSyncModuleOptions {
|
|
75
|
+
/** Sync base path (default: '/sync' — must match the nest-server sync module) */
|
|
76
|
+
basePath?: string;
|
|
77
|
+
/** Enable the offline-sync composable and the sign-out cleanup (default: false) */
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** Options of `useLtOfflineSync()` */
|
|
81
|
+
export interface UseLtOfflineSyncOptions<T = Record<string, unknown>> {
|
|
82
|
+
/** Sync on start, on `online`, on focus and after a hint (default: true). `false` leaves it to `sync()` */
|
|
83
|
+
auto?: boolean;
|
|
84
|
+
/** Listen for the server's hints (`GET <basePath>/hints`, §9) while the tab is visible (default: true) */
|
|
85
|
+
hints?: boolean;
|
|
86
|
+
/** The id format the model declares (default: 'objectId') */
|
|
87
|
+
idFormat?: LtSyncIdFormat;
|
|
88
|
+
/** Resolve a conflict yourself; without it the server's state wins */
|
|
89
|
+
onConflict?: (conflict: LtSyncConflict<T>) => LtSyncConflictResolution<T> | Promise<LtSyncConflictResolution<T>>;
|
|
90
|
+
/** Records per pull page (default: 200; the server caps it, 1000 at most) */
|
|
91
|
+
pageLimit?: number;
|
|
92
|
+
/** Restrict pull and push to one tenant (`X-Tenant-Id`, §4); records and unsent changes are kept per tenant */
|
|
93
|
+
tenantId?: string;
|
|
94
|
+
}
|
|
95
|
+
/** What `useLtOfflineSync()` returns */
|
|
96
|
+
export interface UseLtOfflineSyncReturn<T = Record<string, unknown>> {
|
|
97
|
+
/** Conflicts of the last sync, after they were resolved */
|
|
98
|
+
conflicts: Readonly<Ref<LtSyncPushResult[]>>;
|
|
99
|
+
/** Create a record with a client id (§3); it waits in the outbox until a sync sends it */
|
|
100
|
+
create: (data: T) => Promise<LtSyncRecord<T>>;
|
|
101
|
+
/** The last unexpected error (not offline, not 401) */
|
|
102
|
+
error: Readonly<Ref<Error | null>>;
|
|
103
|
+
/** The caller may no longer pull this model (403). Unsent changes cannot be pushed under this account */
|
|
104
|
+
forbidden: Readonly<Ref<boolean>>;
|
|
105
|
+
/** One local record, a tombstone included */
|
|
106
|
+
get: (id: string) => Promise<LtSyncRecord<T> | undefined>;
|
|
107
|
+
/** Time of the last sync that reached the server */
|
|
108
|
+
lastSyncedAt: Readonly<Ref<Date | null>>;
|
|
109
|
+
/** The server answered 401. Nothing was discarded; the next sync after signing in sends everything */
|
|
110
|
+
needsLogin: Readonly<Ref<boolean>>;
|
|
111
|
+
/** The browser is offline, or the last request could not reach the server */
|
|
112
|
+
offline: Readonly<Ref<boolean>>;
|
|
113
|
+
/** Unsent local changes of this model */
|
|
114
|
+
pendingCount: Readonly<Ref<number>>;
|
|
115
|
+
/** The live local records, sorted by id. Empty while no user is signed in */
|
|
116
|
+
records: Readonly<Ref<readonly LtSyncRecord<T>[]>>;
|
|
117
|
+
/** Changes the server rejected in the last sync; they are dropped and the record returns to the server's state (§7) */
|
|
118
|
+
rejected: Readonly<Ref<LtSyncPushResult[]>>;
|
|
119
|
+
/** Delete a record: a tombstone on the server (a hard delete is not reachable through sync) */
|
|
120
|
+
remove: (id: string) => Promise<void>;
|
|
121
|
+
/** Push the outbox, then pull until the server has nothing more */
|
|
122
|
+
sync: () => Promise<LtSyncResult>;
|
|
123
|
+
/** A sync of this model is running in this tab */
|
|
124
|
+
syncing: Readonly<Ref<boolean>>;
|
|
125
|
+
/** Change fields of a record */
|
|
126
|
+
update: (id: string, data: Partial<T>) => Promise<LtSyncRecord<T>>;
|
|
127
|
+
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nuxt-extensions",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -102,24 +102,25 @@
|
|
|
102
102
|
"devDependencies": {
|
|
103
103
|
"@better-auth/passkey": "1.7.12",
|
|
104
104
|
"@nuxt/module-builder": "1.0.3",
|
|
105
|
-
"@nuxt/schema": "4.6.
|
|
105
|
+
"@nuxt/schema": "4.6.4",
|
|
106
106
|
"@nuxt/ui": "4.12.0",
|
|
107
107
|
"@playwright/test": "1.64.1",
|
|
108
|
-
"@types/node": "26.6.
|
|
109
|
-
"@vitest/coverage-v8": "5.0
|
|
108
|
+
"@types/node": "26.6.6",
|
|
109
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
110
110
|
"@vue/server-renderer": "3.5.44",
|
|
111
111
|
"better-auth": "1.7.12",
|
|
112
112
|
"cross-spawn": "7.0.6",
|
|
113
|
+
"fake-indexeddb": "6.2.7",
|
|
113
114
|
"happy-dom": "21.0.4",
|
|
114
|
-
"nuxt": "4.6.
|
|
115
|
+
"nuxt": "4.6.4",
|
|
115
116
|
"oxfmt": "0.75.0",
|
|
116
117
|
"oxlint": "1.90.0",
|
|
117
118
|
"oxlint-tsgolint": "7.0.2005",
|
|
118
119
|
"reka-ui": "2.11.0",
|
|
119
120
|
"tus-js-client": "4.3.1",
|
|
120
121
|
"typescript": "5.9.3",
|
|
121
|
-
"vitest": "5.0
|
|
122
|
-
"vue-tsc": "3.3.
|
|
122
|
+
"vitest": "5.1.0",
|
|
123
|
+
"vue-tsc": "3.3.14"
|
|
123
124
|
},
|
|
124
125
|
"keywords": [
|
|
125
126
|
"nuxt",
|
|
@@ -138,5 +139,5 @@
|
|
|
138
139
|
"node": ">= 22",
|
|
139
140
|
"pnpm": "^11.0.0"
|
|
140
141
|
},
|
|
141
|
-
"packageManager": "pnpm@11.28.
|
|
142
|
+
"packageManager": "pnpm@11.28.10+sha512.672216f06e15c109b85471d49d3fbed17e6f03ead258df08c2bf5b4a5c33321ebfbf59476bce81325efada16ace0ac78548fd0bfcd7a51369e939182a3dd50e4"
|
|
142
143
|
}
|