@lunora/vue 1.0.0-alpha.2 → 1.0.0-alpha.21
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 +2 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +73 -4
- package/dist/index.d.ts +73 -4
- package/dist/index.mjs +3 -1
- package/dist/packem_shared/useFlag-DRtODeBp.mjs +108 -0
- package/dist/packem_shared/useMutator-B_Ahs_ZN.mjs +19 -0
- package/package.json +5 -5
- /package/dist/packem_shared/{Authenticated-_6-uOycE.mjs → AuthLoading-_6-uOycE.mjs} +0 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Component, Ref, InjectionKey, App, DeepReadonly, MaybeRefOrGetter,
|
|
2
|
-
import { Preloaded, LunoraClient, User, ConnectionStatus, FunctionReference, ReturnOf, ArgsOf, MutationCallOptions } from '@lunora/client';
|
|
3
|
-
export type { ArgsOf, FunctionReference, LunoraClient, MutationCallOptions, OptimisticLocalStore, OptimisticUpdate, Preloaded, ReturnOf, Unsubscribe, User } from '@lunora/client';
|
|
1
|
+
import { Component, Ref, InjectionKey, App, DeepReadonly, MaybeRefOrGetter, ComputedRef, ShallowRef } from 'vue';
|
|
2
|
+
import { Preloaded, LunoraClient, User, ConnectionStatus, FunctionReference, ReturnOf, ArgsOf, MutationCallOptions, MutatorHandle } from '@lunora/client';
|
|
3
|
+
export type { ArgsOf, FunctionReference, LunoraClient, MutationCallOptions, MutatorHandle, MutatorTransaction, OptimisticLocalStore, OptimisticUpdate, Preloaded, ReturnOf, Unsubscribe, User } from '@lunora/client';
|
|
4
4
|
import { PaginationStatus } from '@lunora/client/pagination';
|
|
5
5
|
export type { PaginationResult, PaginationStatus } from '@lunora/client/pagination';
|
|
6
6
|
import { RateLimitStatus, RateLimitConfig } from '@lunora/ratelimit';
|
|
@@ -101,6 +101,42 @@ declare const useAuth: () => UseAuthResult;
|
|
|
101
101
|
* Call inside `setup()` / an active effect scope.
|
|
102
102
|
*/
|
|
103
103
|
declare const useConnectionStatus: () => Readonly<Ref<ConnectionStatus>>;
|
|
104
|
+
/** A targeting context merged on top of the app's default (`defineFlags({ identify })`). */
|
|
105
|
+
type FlagContext = Record<string, unknown>;
|
|
106
|
+
/** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
|
|
107
|
+
type FlagValue = boolean | number | string | {
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
} | unknown[] | null;
|
|
110
|
+
/**
|
|
111
|
+
* Subscribe to a single feature flag, live over Lunora's WebSocket.
|
|
112
|
+
*
|
|
113
|
+
* The returned `ref` holds `defaultValue` until the first evaluation lands, then
|
|
114
|
+
* the server's resolved value — re-pushed whenever the provider re-evaluates
|
|
115
|
+
* (e.g. a flag is toggled in Cloudflare Flagship). The flag's kind is inferred
|
|
116
|
+
* from `defaultValue`'s runtime type, so `useFlag("dark", false)` reads a boolean
|
|
117
|
+
* and `useFlag("hero", "control")` a string.
|
|
118
|
+
*
|
|
119
|
+
* `key` and `context` may be plain values, `ref`s, or getters: passing a reactive
|
|
120
|
+
* source makes the subscription reactive — when it changes the old subscription
|
|
121
|
+
* is torn down and a fresh one opens. `context` supplies a per-call targeting
|
|
122
|
+
* context merged on top of the app's default `identify` targeting key.
|
|
123
|
+
*
|
|
124
|
+
* Evaluation runs through whatever OpenFeature provider the app wired in
|
|
125
|
+
* `lunora/flags.ts`; the read never throws — a provider error resolves the
|
|
126
|
+
* default (the same fail-open contract as server-side `ctx.flags`). Call inside
|
|
127
|
+
* `setup()` (or any active effect scope); the subscription tears down on unmount.
|
|
128
|
+
*/
|
|
129
|
+
declare const useFlag: <T extends FlagValue>(key: MaybeRefOrGetter<string>, defaultValue: T, context?: MaybeRefOrGetter<FlagContext | undefined>) => Readonly<Ref<T>>;
|
|
130
|
+
/**
|
|
131
|
+
* Subscribe to several feature flags at once, live over Lunora's WebSocket.
|
|
132
|
+
*
|
|
133
|
+
* Pass a record of `key → defaultValue`; each flag's kind is inferred from its
|
|
134
|
+
* default, and the returned `ref` holds the same-shaped record with resolved
|
|
135
|
+
* values (the defaults until each evaluation lands). A single `context` applies
|
|
136
|
+
* to every flag and may be reactive. This is the batched form of {@link useFlag}
|
|
137
|
+
* — one watcher manages one subscription per key.
|
|
138
|
+
*/
|
|
139
|
+
declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: MaybeRefOrGetter<FlagContext | undefined>) => Readonly<Ref<T>>;
|
|
104
140
|
/**
|
|
105
141
|
* The reactive handle returned by {@link useMutation} — the Vue counterpart to
|
|
106
142
|
* React's `useMutation`, re-expressed with refs. The surface is identical across
|
|
@@ -136,6 +172,39 @@ interface MutationHandle<F extends FunctionReference> {
|
|
|
136
172
|
* adapter-specific.
|
|
137
173
|
*/
|
|
138
174
|
declare const useMutation: <F extends FunctionReference>(function_: F) => MutationHandle<F>;
|
|
175
|
+
/**
|
|
176
|
+
* The reactive handle returned by {@link useMutator} — the Vue counterpart to
|
|
177
|
+
* `@lunora/react`'s `useMutator`, re-expressed with refs. The surface is
|
|
178
|
+
* identical across the Lunora adapters (`@lunora/solid`, `/svelte`):
|
|
179
|
+
* `error`/`isError`/`pending` are refs you read in a template and `mutate` is an
|
|
180
|
+
* awaitable that resolves once the write is persisted (or rejects).
|
|
181
|
+
*/
|
|
182
|
+
interface MutatorHook<TArgs> {
|
|
183
|
+
/** The latest invocation's error, or `undefined`. */
|
|
184
|
+
error: Ref<Error | undefined>;
|
|
185
|
+
/** `true` when the latest invocation rejected. */
|
|
186
|
+
isError: ComputedRef<boolean>;
|
|
187
|
+
/** Run the mutator; resolves once the write is persisted, rejects on failure. */
|
|
188
|
+
mutate: (args: TArgs) => Promise<void>;
|
|
189
|
+
/** `true` while ANY invocation from this handle is in flight (ref-counted, so overlapping calls compose). */
|
|
190
|
+
pending: Ref<boolean>;
|
|
191
|
+
/** Clear the latest `error` back to idle. */
|
|
192
|
+
reset: () => void;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Ergonomic `{ mutate, pending, error, isError, reset }` wrapper over a bound
|
|
196
|
+
* custom-mutator handle from `@lunora/db`'s `bindMutators` — the Vue equivalent
|
|
197
|
+
* of `@lunora/react`'s `useMutator`. The optimistic overlay and
|
|
198
|
+
* server-authoritative push are owned by the bound handle (and TanStack DB's
|
|
199
|
+
* optimistic-transaction layer rebases pending overlays on every sync tick);
|
|
200
|
+
* this composable only surfaces reactive state for the in-flight/error
|
|
201
|
+
* lifecycle. Reads stay on the existing TanStack `useLiveQuery`; no new query
|
|
202
|
+
* composable is needed.
|
|
203
|
+
*
|
|
204
|
+
* `pending` is ref-counted across overlapping invocations of THIS handle, so it
|
|
205
|
+
* clears only once every concurrent call has settled.
|
|
206
|
+
*/
|
|
207
|
+
declare const useMutator: <TArgs = Record<string, unknown>>(handle: MutatorHandle<TArgs>) => MutatorHook<TArgs>;
|
|
139
208
|
/** The args a paginated query exposes minus the framework-supplied page cursor. */
|
|
140
209
|
type PaginatedArgs<F extends FunctionReference> = Omit<ArgsOf<F>, "paginationOpts">;
|
|
141
210
|
/** The element type of the `page` array a paginated query returns. */
|
|
@@ -345,4 +414,4 @@ interface UseSubscriptionResult<T> {
|
|
|
345
414
|
* high-frequency streams.
|
|
346
415
|
*/
|
|
347
416
|
declare const useSubscription: <F extends FunctionReference>(function_: F, args: MaybeRefOrGetter<ArgsOf<F> | "skip">, options?: UseQueryOptions) => UseSubscriptionResult<ReturnOf<F>>;
|
|
348
|
-
export { AuthLoading, Authenticated, type HeartbeatReference, LUNORA_INJECTION_KEY, type ListPresentReference, type MutationHandle, type PageItemOf, type PaginatedArgs, Unauthenticated, type UseAuthResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseSubscriptionResult, createLunora, hydratePreloaded, provideLunora, subscribeToQuery, useAuth, useConnectionStatus, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePresence, useQuery, useRateLimit, useSubscription };
|
|
417
|
+
export { AuthLoading, Authenticated, type FlagContext, type FlagValue, type HeartbeatReference, LUNORA_INJECTION_KEY, type ListPresentReference, type MutationHandle, type MutatorHook, type PageItemOf, type PaginatedArgs, Unauthenticated, type UseAuthResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseSubscriptionResult, createLunora, hydratePreloaded, provideLunora, subscribeToQuery, useAuth, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePresence, useQuery, useRateLimit, useSubscription };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Component, Ref, InjectionKey, App, DeepReadonly, MaybeRefOrGetter,
|
|
2
|
-
import { Preloaded, LunoraClient, User, ConnectionStatus, FunctionReference, ReturnOf, ArgsOf, MutationCallOptions } from '@lunora/client';
|
|
3
|
-
export type { ArgsOf, FunctionReference, LunoraClient, MutationCallOptions, OptimisticLocalStore, OptimisticUpdate, Preloaded, ReturnOf, Unsubscribe, User } from '@lunora/client';
|
|
1
|
+
import { Component, Ref, InjectionKey, App, DeepReadonly, MaybeRefOrGetter, ComputedRef, ShallowRef } from 'vue';
|
|
2
|
+
import { Preloaded, LunoraClient, User, ConnectionStatus, FunctionReference, ReturnOf, ArgsOf, MutationCallOptions, MutatorHandle } from '@lunora/client';
|
|
3
|
+
export type { ArgsOf, FunctionReference, LunoraClient, MutationCallOptions, MutatorHandle, MutatorTransaction, OptimisticLocalStore, OptimisticUpdate, Preloaded, ReturnOf, Unsubscribe, User } from '@lunora/client';
|
|
4
4
|
import { PaginationStatus } from '@lunora/client/pagination';
|
|
5
5
|
export type { PaginationResult, PaginationStatus } from '@lunora/client/pagination';
|
|
6
6
|
import { RateLimitStatus, RateLimitConfig } from '@lunora/ratelimit';
|
|
@@ -101,6 +101,42 @@ declare const useAuth: () => UseAuthResult;
|
|
|
101
101
|
* Call inside `setup()` / an active effect scope.
|
|
102
102
|
*/
|
|
103
103
|
declare const useConnectionStatus: () => Readonly<Ref<ConnectionStatus>>;
|
|
104
|
+
/** A targeting context merged on top of the app's default (`defineFlags({ identify })`). */
|
|
105
|
+
type FlagContext = Record<string, unknown>;
|
|
106
|
+
/** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
|
|
107
|
+
type FlagValue = boolean | number | string | {
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
} | unknown[] | null;
|
|
110
|
+
/**
|
|
111
|
+
* Subscribe to a single feature flag, live over Lunora's WebSocket.
|
|
112
|
+
*
|
|
113
|
+
* The returned `ref` holds `defaultValue` until the first evaluation lands, then
|
|
114
|
+
* the server's resolved value — re-pushed whenever the provider re-evaluates
|
|
115
|
+
* (e.g. a flag is toggled in Cloudflare Flagship). The flag's kind is inferred
|
|
116
|
+
* from `defaultValue`'s runtime type, so `useFlag("dark", false)` reads a boolean
|
|
117
|
+
* and `useFlag("hero", "control")` a string.
|
|
118
|
+
*
|
|
119
|
+
* `key` and `context` may be plain values, `ref`s, or getters: passing a reactive
|
|
120
|
+
* source makes the subscription reactive — when it changes the old subscription
|
|
121
|
+
* is torn down and a fresh one opens. `context` supplies a per-call targeting
|
|
122
|
+
* context merged on top of the app's default `identify` targeting key.
|
|
123
|
+
*
|
|
124
|
+
* Evaluation runs through whatever OpenFeature provider the app wired in
|
|
125
|
+
* `lunora/flags.ts`; the read never throws — a provider error resolves the
|
|
126
|
+
* default (the same fail-open contract as server-side `ctx.flags`). Call inside
|
|
127
|
+
* `setup()` (or any active effect scope); the subscription tears down on unmount.
|
|
128
|
+
*/
|
|
129
|
+
declare const useFlag: <T extends FlagValue>(key: MaybeRefOrGetter<string>, defaultValue: T, context?: MaybeRefOrGetter<FlagContext | undefined>) => Readonly<Ref<T>>;
|
|
130
|
+
/**
|
|
131
|
+
* Subscribe to several feature flags at once, live over Lunora's WebSocket.
|
|
132
|
+
*
|
|
133
|
+
* Pass a record of `key → defaultValue`; each flag's kind is inferred from its
|
|
134
|
+
* default, and the returned `ref` holds the same-shaped record with resolved
|
|
135
|
+
* values (the defaults until each evaluation lands). A single `context` applies
|
|
136
|
+
* to every flag and may be reactive. This is the batched form of {@link useFlag}
|
|
137
|
+
* — one watcher manages one subscription per key.
|
|
138
|
+
*/
|
|
139
|
+
declare const useFlags: <T extends Record<string, FlagValue>>(flags: T, context?: MaybeRefOrGetter<FlagContext | undefined>) => Readonly<Ref<T>>;
|
|
104
140
|
/**
|
|
105
141
|
* The reactive handle returned by {@link useMutation} — the Vue counterpart to
|
|
106
142
|
* React's `useMutation`, re-expressed with refs. The surface is identical across
|
|
@@ -136,6 +172,39 @@ interface MutationHandle<F extends FunctionReference> {
|
|
|
136
172
|
* adapter-specific.
|
|
137
173
|
*/
|
|
138
174
|
declare const useMutation: <F extends FunctionReference>(function_: F) => MutationHandle<F>;
|
|
175
|
+
/**
|
|
176
|
+
* The reactive handle returned by {@link useMutator} — the Vue counterpart to
|
|
177
|
+
* `@lunora/react`'s `useMutator`, re-expressed with refs. The surface is
|
|
178
|
+
* identical across the Lunora adapters (`@lunora/solid`, `/svelte`):
|
|
179
|
+
* `error`/`isError`/`pending` are refs you read in a template and `mutate` is an
|
|
180
|
+
* awaitable that resolves once the write is persisted (or rejects).
|
|
181
|
+
*/
|
|
182
|
+
interface MutatorHook<TArgs> {
|
|
183
|
+
/** The latest invocation's error, or `undefined`. */
|
|
184
|
+
error: Ref<Error | undefined>;
|
|
185
|
+
/** `true` when the latest invocation rejected. */
|
|
186
|
+
isError: ComputedRef<boolean>;
|
|
187
|
+
/** Run the mutator; resolves once the write is persisted, rejects on failure. */
|
|
188
|
+
mutate: (args: TArgs) => Promise<void>;
|
|
189
|
+
/** `true` while ANY invocation from this handle is in flight (ref-counted, so overlapping calls compose). */
|
|
190
|
+
pending: Ref<boolean>;
|
|
191
|
+
/** Clear the latest `error` back to idle. */
|
|
192
|
+
reset: () => void;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Ergonomic `{ mutate, pending, error, isError, reset }` wrapper over a bound
|
|
196
|
+
* custom-mutator handle from `@lunora/db`'s `bindMutators` — the Vue equivalent
|
|
197
|
+
* of `@lunora/react`'s `useMutator`. The optimistic overlay and
|
|
198
|
+
* server-authoritative push are owned by the bound handle (and TanStack DB's
|
|
199
|
+
* optimistic-transaction layer rebases pending overlays on every sync tick);
|
|
200
|
+
* this composable only surfaces reactive state for the in-flight/error
|
|
201
|
+
* lifecycle. Reads stay on the existing TanStack `useLiveQuery`; no new query
|
|
202
|
+
* composable is needed.
|
|
203
|
+
*
|
|
204
|
+
* `pending` is ref-counted across overlapping invocations of THIS handle, so it
|
|
205
|
+
* clears only once every concurrent call has settled.
|
|
206
|
+
*/
|
|
207
|
+
declare const useMutator: <TArgs = Record<string, unknown>>(handle: MutatorHandle<TArgs>) => MutatorHook<TArgs>;
|
|
139
208
|
/** The args a paginated query exposes minus the framework-supplied page cursor. */
|
|
140
209
|
type PaginatedArgs<F extends FunctionReference> = Omit<ArgsOf<F>, "paginationOpts">;
|
|
141
210
|
/** The element type of the `page` array a paginated query returns. */
|
|
@@ -345,4 +414,4 @@ interface UseSubscriptionResult<T> {
|
|
|
345
414
|
* high-frequency streams.
|
|
346
415
|
*/
|
|
347
416
|
declare const useSubscription: <F extends FunctionReference>(function_: F, args: MaybeRefOrGetter<ArgsOf<F> | "skip">, options?: UseQueryOptions) => UseSubscriptionResult<ReturnOf<F>>;
|
|
348
|
-
export { AuthLoading, Authenticated, type HeartbeatReference, LUNORA_INJECTION_KEY, type ListPresentReference, type MutationHandle, type PageItemOf, type PaginatedArgs, Unauthenticated, type UseAuthResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseSubscriptionResult, createLunora, hydratePreloaded, provideLunora, subscribeToQuery, useAuth, useConnectionStatus, useInfiniteQuery, useLunora, useMutation, usePaginatedQuery, usePresence, useQuery, useRateLimit, useSubscription };
|
|
417
|
+
export { AuthLoading, Authenticated, type FlagContext, type FlagValue, type HeartbeatReference, LUNORA_INJECTION_KEY, type ListPresentReference, type MutationHandle, type MutatorHook, type PageItemOf, type PaginatedArgs, Unauthenticated, type UseAuthResult, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UsePaginatedQueryOptions, type UsePaginatedQueryResult, type UsePresenceOptions, type UsePresenceResult, type UseQueryOptions, type UseRateLimitOptions, type UseRateLimitResult, type UseSubscriptionResult, createLunora, hydratePreloaded, provideLunora, subscribeToQuery, useAuth, useConnectionStatus, useFlag, useFlags, useInfiniteQuery, useLunora, useMutation, useMutator, usePaginatedQuery, usePresence, useQuery, useRateLimit, useSubscription };
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { AuthLoading, Authenticated, Unauthenticated } from './packem_shared/
|
|
1
|
+
export { AuthLoading, Authenticated, Unauthenticated } from './packem_shared/AuthLoading-_6-uOycE.mjs';
|
|
2
2
|
export { hydratePreloaded } from './packem_shared/hydratePreloaded-rVY68iFv.mjs';
|
|
3
3
|
export { LUNORA_INJECTION_KEY, createLunora, provideLunora, useLunora } from './packem_shared/LUNORA_INJECTION_KEY-DtFXLDQ_.mjs';
|
|
4
4
|
export { useAuth } from './packem_shared/useAuth-C2aEzXXH.mjs';
|
|
5
5
|
export { default as useConnectionStatus } from './packem_shared/useConnectionStatus-CDgduQYe.mjs';
|
|
6
|
+
export { useFlag, useFlags } from './packem_shared/useFlag-DRtODeBp.mjs';
|
|
6
7
|
export { useMutation } from './packem_shared/useMutation-DWubV5pv.mjs';
|
|
8
|
+
export { useMutator } from './packem_shared/useMutator-B_Ahs_ZN.mjs';
|
|
7
9
|
export { useInfiniteQuery, usePaginatedQuery } from './packem_shared/useInfiniteQuery-CEfbw7Nw.mjs';
|
|
8
10
|
export { usePresence } from './packem_shared/usePresence-BeN5xaZM.mjs';
|
|
9
11
|
export { subscribeToQuery, useQuery } from './packem_shared/subscribeToQuery-Cv5YL-SI.mjs';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { shallowRef, watch, toValue } from 'vue';
|
|
2
|
+
import { useLunora } from './LUNORA_INJECTION_KEY-DtFXLDQ_.mjs';
|
|
3
|
+
|
|
4
|
+
const compareKeys = (a, b) => {
|
|
5
|
+
if (a < b) {
|
|
6
|
+
return -1;
|
|
7
|
+
}
|
|
8
|
+
return a > b ? 1 : 0;
|
|
9
|
+
};
|
|
10
|
+
const stableStringify = (value) => {
|
|
11
|
+
if (value === void 0) {
|
|
12
|
+
return "null";
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "bigint") {
|
|
15
|
+
throw new TypeError("stableStringify: cannot use a bigint in a cache key (query/subscription/shape args) — pass it as a string");
|
|
16
|
+
}
|
|
17
|
+
if (value === null || typeof value !== "object") {
|
|
18
|
+
return JSON.stringify(value);
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
22
|
+
}
|
|
23
|
+
const proto = Object.getPrototypeOf(value);
|
|
24
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
25
|
+
const name = value.constructor?.name ?? "value";
|
|
26
|
+
throw new TypeError(
|
|
27
|
+
`stableStringify: cannot use a ${name} in a cache key (query/subscription/shape args) — only plain objects, arrays, and JSON primitives are supported`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const record = value;
|
|
31
|
+
const keys = Object.keys(record).toSorted(compareKeys);
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const key of keys) {
|
|
34
|
+
const raw = record[key];
|
|
35
|
+
if (raw === void 0) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
|
|
39
|
+
}
|
|
40
|
+
return `{${parts.join(",")}}`;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const FLAGS_EVAL_PATH = "__lunora_flags__:eval";
|
|
44
|
+
const flagKind = (value) => {
|
|
45
|
+
const kind = typeof value;
|
|
46
|
+
if (kind === "boolean" || kind === "number" || kind === "string") {
|
|
47
|
+
return kind;
|
|
48
|
+
}
|
|
49
|
+
return "object";
|
|
50
|
+
};
|
|
51
|
+
const flagsReference = { __lunoraRef: FLAGS_EVAL_PATH };
|
|
52
|
+
const serializeContext = (context) => context === void 0 ? "" : stableStringify(context);
|
|
53
|
+
const useFlag = (key, defaultValue, context) => {
|
|
54
|
+
const client = useLunora();
|
|
55
|
+
const type = flagKind(defaultValue);
|
|
56
|
+
const value = shallowRef(defaultValue);
|
|
57
|
+
watch(
|
|
58
|
+
() => `${toValue(key)}\0${serializeContext(toValue(context))}`,
|
|
59
|
+
(_serialized, _previous, onCleanup) => {
|
|
60
|
+
const currentKey = toValue(key);
|
|
61
|
+
const currentContext = toValue(context);
|
|
62
|
+
value.value = defaultValue;
|
|
63
|
+
let unsubscribe;
|
|
64
|
+
try {
|
|
65
|
+
unsubscribe = client.subscribe(flagsReference, { context: currentContext, default: defaultValue, key: currentKey, type }, (next) => {
|
|
66
|
+
value.value = next;
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
onCleanup(unsubscribe);
|
|
72
|
+
},
|
|
73
|
+
{ immediate: true }
|
|
74
|
+
);
|
|
75
|
+
return value;
|
|
76
|
+
};
|
|
77
|
+
const useFlags = (flags, context) => {
|
|
78
|
+
const client = useLunora();
|
|
79
|
+
const values = shallowRef(flags);
|
|
80
|
+
const spec = stableStringify(flags);
|
|
81
|
+
watch(
|
|
82
|
+
() => `${spec}\0${serializeContext(toValue(context))}`,
|
|
83
|
+
(_serialized, _previous, onCleanup) => {
|
|
84
|
+
const currentContext = toValue(context);
|
|
85
|
+
values.value = flags;
|
|
86
|
+
const unsubscribes = [];
|
|
87
|
+
for (const [key, defaultValue] of Object.entries(flags)) {
|
|
88
|
+
try {
|
|
89
|
+
unsubscribes.push(
|
|
90
|
+
client.subscribe(flagsReference, { context: currentContext, default: defaultValue, key, type: flagKind(defaultValue) }, (next) => {
|
|
91
|
+
values.value = { ...values.value, [key]: next };
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
onCleanup(() => {
|
|
98
|
+
for (const unsubscribe of unsubscribes) {
|
|
99
|
+
unsubscribe();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
{ immediate: true }
|
|
104
|
+
);
|
|
105
|
+
return values;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { useFlag, useFlags };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createMutatorRunner } from '@lunora/client';
|
|
2
|
+
import { shallowRef, computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
const useMutator = (handle) => {
|
|
5
|
+
const error = shallowRef(void 0);
|
|
6
|
+
const pending = shallowRef(false);
|
|
7
|
+
const isError = computed(() => error.value !== void 0);
|
|
8
|
+
const { mutate, reset } = createMutatorRunner(handle, {
|
|
9
|
+
setError: (value) => {
|
|
10
|
+
error.value = value;
|
|
11
|
+
},
|
|
12
|
+
setPending: (value) => {
|
|
13
|
+
pending.value = value;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return { error, isError, mutate, pending, reset };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { useMutator };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/vue",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.21",
|
|
4
4
|
"description": "Vue adapter for Lunora — live composables, optimistic mutations, and reactive loaders",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "packages/vue"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist",
|
|
28
|
+
"./dist",
|
|
29
29
|
"README.md",
|
|
30
30
|
"LICENSE.md",
|
|
31
31
|
"__assets__"
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@lunora/client": "1.0.0-alpha.
|
|
58
|
-
"@lunora/ratelimit": "1.0.0-alpha.
|
|
59
|
-
"@lunora/runtime": "1.0.0-alpha.
|
|
57
|
+
"@lunora/client": "1.0.0-alpha.13",
|
|
58
|
+
"@lunora/ratelimit": "1.0.0-alpha.3",
|
|
59
|
+
"@lunora/runtime": "1.0.0-alpha.12"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"vue": "^3.5.0"
|
|
File without changes
|