@lunora/angular 1.0.0-alpha.20 → 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/dist/index.d.mts CHANGED
@@ -524,6 +524,32 @@ interface AuthResult {
524
524
  * @experimental
525
525
  */
526
526
  declare const auth: (options?: AuthOptions) => AuthResult;
527
+ /**
528
+ * `AuthGateResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
529
+ * @experimental
530
+ */
531
+ interface AuthGateResult {
532
+ /** `true` once a token is set and the user has resolved. */
533
+ isAuthenticated: Signal<boolean>;
534
+ /** `true` while a token is set but the user hasn't resolved yet. */
535
+ isLoading: Signal<boolean>;
536
+ }
537
+ /**
538
+ * Derived auth-gate signals for template gating (Angular's `\@if` control
539
+ * flow), built on {@link auth}. Angular has no JSX-style `Authenticated` slot
540
+ * component the way React/Vue/Solid do, so this exposes the same three-state
541
+ * logic as two booleans instead: a token with no resolved user yet is
542
+ * `isLoading`; a token with a resolved user is `isAuthenticated`; no token is
543
+ * neither (the signed-out state a template checks for with a plain `\@else`).
544
+ *
545
+ * Call from an injection context (component/service field or constructor):
546
+ * ```ts
547
+ * protected readonly authState = authGate();
548
+ * // template: \@if (authState.isAuthenticated()) { ... } \@else if (authState.isLoading()) { ... }
549
+ * ```
550
+ * @experimental
551
+ */
552
+ declare const authGate: (options?: AuthOptions) => AuthGateResult;
527
553
  /**
528
554
  * DI token carrying the framework-neutral {@link LunoraClient}. Every reactive
529
555
  * primitive in this adapter (`liveQuery`, `mutate`, `connectionStatus`) reads the
@@ -1310,4 +1336,4 @@ interface VoiceAgentResult {
1310
1336
  * @experimental
1311
1337
  */
1312
1338
  declare const voiceAgent: (options: VoiceAgentOptions) => VoiceAgentResult;
1313
- export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagContext, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult, type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, stream, subscription, voiceAgent };
1339
+ export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthGateResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagContext, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult, type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, authGate, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, stream, subscription, voiceAgent };
package/dist/index.d.ts CHANGED
@@ -524,6 +524,32 @@ interface AuthResult {
524
524
  * @experimental
525
525
  */
526
526
  declare const auth: (options?: AuthOptions) => AuthResult;
527
+ /**
528
+ * `AuthGateResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
529
+ * @experimental
530
+ */
531
+ interface AuthGateResult {
532
+ /** `true` once a token is set and the user has resolved. */
533
+ isAuthenticated: Signal<boolean>;
534
+ /** `true` while a token is set but the user hasn't resolved yet. */
535
+ isLoading: Signal<boolean>;
536
+ }
537
+ /**
538
+ * Derived auth-gate signals for template gating (Angular's `\@if` control
539
+ * flow), built on {@link auth}. Angular has no JSX-style `Authenticated` slot
540
+ * component the way React/Vue/Solid do, so this exposes the same three-state
541
+ * logic as two booleans instead: a token with no resolved user yet is
542
+ * `isLoading`; a token with a resolved user is `isAuthenticated`; no token is
543
+ * neither (the signed-out state a template checks for with a plain `\@else`).
544
+ *
545
+ * Call from an injection context (component/service field or constructor):
546
+ * ```ts
547
+ * protected readonly authState = authGate();
548
+ * // template: \@if (authState.isAuthenticated()) { ... } \@else if (authState.isLoading()) { ... }
549
+ * ```
550
+ * @experimental
551
+ */
552
+ declare const authGate: (options?: AuthOptions) => AuthGateResult;
527
553
  /**
528
554
  * DI token carrying the framework-neutral {@link LunoraClient}. Every reactive
529
555
  * primitive in this adapter (`liveQuery`, `mutate`, `connectionStatus`) reads the
@@ -1310,4 +1336,4 @@ interface VoiceAgentResult {
1310
1336
  * @experimental
1311
1337
  */
1312
1338
  declare const voiceAgent: (options: VoiceAgentOptions) => VoiceAgentResult;
1313
- export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagContext, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult, type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, stream, subscription, voiceAgent };
1339
+ export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthGateResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagContext, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult, type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, authGate, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, stream, subscription, voiceAgent };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{agent as e}from"./packem_shared/agent-C-GavfBl.mjs";import{agentChat as m}from"./packem_shared/agentChat-Bay8gA8i.mjs";import{agentState as f}from"./packem_shared/agentState-BAfa0QuC.mjs";import{agentToolEvents as x}from"./packem_shared/agentToolEvents-CBTZ_DsO.mjs";import{auth as i}from"./packem_shared/auth-wTtWvuVS.mjs";import{LUNORA_CLIENT as g,injectLunoraClient as s,provideLunora as c}from"./packem_shared/LUNORA_CLIENT-B0pGE0Tv.mjs";import{connectionStatus as d}from"./packem_shared/connectionStatus-C53UhkPi.mjs";import{flag as v,flags as y}from"./packem_shared/flag-BYFWoQTg.mjs";import{hydratePreloaded as C}from"./packem_shared/hydratePreloaded-BKHgpXzQ.mjs";import{liveQuery as S}from"./packem_shared/liveQuery-DEIqmTPj.mjs";import{mutate as E}from"./packem_shared/mutate-DR8Uo8Hs.mjs";import{mutator as N}from"./packem_shared/mutator-Cw6jpG06.mjs";import{infiniteQuery as T,paginatedQuery as b}from"./packem_shared/infiniteQuery-YvF0in16.mjs";import{presence as K}from"./packem_shared/presence-BMrTlOCF.mjs";import{rateLimit as R}from"./packem_shared/rateLimit-CQazDrPJ.mjs";import{stream as _}from"./packem_shared/stream-CcvHiJf2.mjs";import{subscription as q}from"./packem_shared/subscription-DJeoGEDR.mjs";import{voiceAgent as z}from"./packem_shared/voiceAgent-CwlzrEjH.mjs";import{SKIP as D}from"@lunora/client/query";export{g as LUNORA_CLIENT,D as SKIP,e as agent,m as agentChat,f as agentState,x as agentToolEvents,i as auth,d as connectionStatus,v as flag,y as flags,C as hydratePreloaded,T as infiniteQuery,s as injectLunoraClient,S as liveQuery,E as mutate,N as mutator,b as paginatedQuery,K as presence,c as provideLunora,R as rateLimit,_ as stream,q as subscription,z as voiceAgent};
1
+ import{agent as e}from"./packem_shared/agent-C-GavfBl.mjs";import{agentChat as m}from"./packem_shared/agentChat-Bay8gA8i.mjs";import{agentState as a}from"./packem_shared/agentState-BAfa0QuC.mjs";import{agentToolEvents as x}from"./packem_shared/agentToolEvents-CBTZ_DsO.mjs";import{auth as i,authGate as u}from"./packem_shared/auth-BTEtMnGG.mjs";import{LUNORA_CLIENT as s,injectLunoraClient as c,provideLunora as l}from"./packem_shared/LUNORA_CLIENT-B0pGE0Tv.mjs";import{connectionStatus as L}from"./packem_shared/connectionStatus-C53UhkPi.mjs";import{flag as v,flags as y}from"./packem_shared/flag-BYFWoQTg.mjs";import{hydratePreloaded as Q}from"./packem_shared/hydratePreloaded-BKHgpXzQ.mjs";import{liveQuery as A}from"./packem_shared/liveQuery-DEIqmTPj.mjs";import{mutate as I}from"./packem_shared/mutate-DR8Uo8Hs.mjs";import{mutator as P}from"./packem_shared/mutator-Cw6jpG06.mjs";import{infiniteQuery as b,paginatedQuery as j}from"./packem_shared/infiniteQuery-C-T8hD7-.mjs";import{presence as K}from"./packem_shared/presence-BMrTlOCF.mjs";import{rateLimit as R}from"./packem_shared/rateLimit-CQazDrPJ.mjs";import{stream as _}from"./packem_shared/stream-CcvHiJf2.mjs";import{subscription as q}from"./packem_shared/subscription-DJeoGEDR.mjs";import{voiceAgent as z}from"./packem_shared/voiceAgent-CwlzrEjH.mjs";import{SKIP as D}from"@lunora/client/query";export{s as LUNORA_CLIENT,D as SKIP,e as agent,m as agentChat,a as agentState,x as agentToolEvents,i as auth,u as authGate,L as connectionStatus,v as flag,y as flags,Q as hydratePreloaded,b as infiniteQuery,c as injectLunoraClient,A as liveQuery,I as mutate,P as mutator,j as paginatedQuery,K as presence,l as provideLunora,R as rateLimit,_ as stream,q as subscription,z as voiceAgent};
@@ -0,0 +1 @@
1
+ import{inject as g,DestroyRef as h,signal as u,computed as i}from"@angular/core";import{getIdentityStore as d}from"@lunora/client/auth";import{resolveLunoraClient as k}from"./LUNORA_CLIENT-B0pGE0Tv.mjs";const m=(o={})=>{const e=k(o.client),n=o.destroyRef??g(h),t=d(e),r=u(e.getAuthToken()),s=u(t.getUser()),l=e.onAuthTokenChange(()=>{r.set(e.getAuthToken())}),a=t.subscribe(()=>{s.set(t.getUser())});return n.onDestroy(()=>{l(),a()}),{setToken:c=>{e.setAuthToken(c)},token:r.asReadonly(),user:s.asReadonly()}},A=(o={})=>{const{token:e,user:n}=m(o),t=i(()=>e()!==null&&n()===null);return{isAuthenticated:i(()=>e()!==null&&n()!==null),isLoading:t}};export{m as auth,A as authGate};
@@ -0,0 +1 @@
1
+ import{computed as j,inject as W,DestroyRef as _,signal as C}from"@angular/core";import{initialPages as q,derivePaginationStatus as T,applyLoadMore as z,rebalance as V}from"@lunora/client/pagination";import{resolveLunoraClient as G}from"./LUNORA_CLIENT-B0pGE0Tv.mjs";import{s as H}from"./platform-BGZypnc9.mjs";const X=(e,n)=>e<n?-1:e>n?1:0,I=e=>{if(e===void 0)return"null";if(typeof e=="bigint")throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(s=>I(s)).join(",")}]`;const n=Object.getPrototypeOf(e);if(n!==null&&n!==Object.prototype){const s=e.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${s} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`)}const r=e,y=Object.keys(r).toSorted(X),a=[];for(const s of y){const t=r[s];t!==void 0&&a.push(`${JSON.stringify(s)}:${I(t)}`)}return`{${a.join(",")}}`},F=e=>{let n="";for(let r=0;r<e.length;r+=32768)n+=String.fromCharCode(...e.subarray(r,r+32768));return btoa(n)},f="$lunora.wire$",U=64,v=(e,n=0)=>{if(n>U)throw new RangeError(`wire-codec: value nesting exceeds the ${U}-level limit`);if(e===void 0)return[f,"undefined"];if(e===null)return null;const r=typeof e;if(r==="bigint")return[f,"bigint",e.toString()];if(r==="number"){const t=e;return Number.isNaN(t)?[f,"nan"]:t===1/0?[f,"inf"]:t===-1/0?[f,"-inf"]:t}if(r!=="object")return e;if(e instanceof Date)return[f,"date",v(e.getTime(),n+1)];if(e instanceof Error){const t=e,i={};for(const c of Object.keys(t))t[c]!==void 0&&(i[c]=v(t[c],n+1));const l=[f,"error",t.name,t.message,i];return t.cause!==void 0&&l.push(v(t.cause,n+1)),l}if(e instanceof URL)return[f,"url",e.href];if(e instanceof Map)return[f,"map",[...e.entries()].map(([t,i])=>[v(t,n+1),v(i,n+1)])];if(e instanceof Set)return[f,"set",[...e].map(t=>v(t,n+1))];if(e instanceof ArrayBuffer)return[f,"bytes",F(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const t=e,i=t.constructor.name,l=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);return i==="Uint8Array"?[f,"bytes",F(l)]:[f,"bytes",F(l),i]}if(Array.isArray(e)){const t=e.map(i=>v(i,n+1));return t.length>0&&t[0]===f?[f,"arr",t]:t}const y=Object.getPrototypeOf(e);if(y!==null&&y!==Object.prototype){const t=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${t} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const a=e,s={};for(const t of Object.keys(a)){const i=a[t];i!==void 0&&(s[t]=v(i,n+1))}return s},Y=e=>I(v(e)),S=(e,n)=>`${e}::${Y(n)}`,L=(e,n)=>({...n,paginationOpts:{cursor:e.lower,endCursor:e.upper,numItems:e.numItems}}),B=(e,n,r)=>{const y=G(r.client),a=r.destroyRef===void 0,s=r.destroyRef??W(_),{initialNumItems:t,shardKey:i}=r,l=e.__lunoraRef,c=n==="skip"?{}:n,d=C(q(t)),$=C([]),J=C("LoadingFirstPage"),O=new Map,h=new Map,k=new Set,M=()=>{const p=d().map(w=>{const o=S(l,L(w,c));return h.get(o)});$.set(p);const{status:b}=T(n==="skip",p);J.set(b)},D=(p,b)=>{const w=o=>S(l,L(o,c));for(const o of b){const g=w(o);if(h.has(g))continue;const m=p.find(u=>u.lower===o.lower);if(m){const u=h.get(w(m));u&&h.set(g,u)}}},Q=p=>{const b=new Set;for(const o of p)b.add(S(l,L(o,c)));for(const[o,g]of O)b.has(g.currentKey)||(g.unsub(),O.delete(o),h.delete(g.currentKey));const w=new Set([...O.values()].map(o=>o.currentKey));for(const o of p){const g=L(o,c),m=S(l,g);if(w.has(m))continue;const u={currentKey:m,unsub:void 0};k.add(m);const A=y.subscribe(e,g,K=>{if(h.set(u.currentKey,K),k.delete(u.currentKey),M(),k.size===0){const N=d(),R=V(N,$());R&&(D(N,R),d.set(R),x(R),M())}},{onError:()=>{k.delete(u.currentKey),M()},shardKey:i});u.unsub=A,O.set(m,u),w.add(m)}};let P=!1,E=!1;const x=p=>{if(P){E=!0;return}P=!0;try{let b=p;do E=!1,Q(b),b=d();while(E)}finally{P=!1}};return n!=="skip"&&H(a)&&x(d()),M(),s.onDestroy(()=>{for(const p of O.values())p.unsub();O.clear(),h.clear()}),{loadMore:p=>{if(n==="skip")return;const{nextCursor:b,status:w}=T(!1,$());if(w!=="CanLoadMore")return;const o=z(d(),b,p);if(!o)return;const g=d().at(-1),m=o.at(-2);if(g&&m){const u=S(l,L(g,c)),A=S(l,L(m,c)),K=O.get(u);if(K&&u!==A){const N=h.get(u);N&&h.set(A,N),K.unsub(),O.delete(u),h.delete(u)}}d.set(o),x(d()),M()},pageResults:$,status:J}},re=(e,n,r)=>{const y=B(e,n,r),a=j(()=>{const s=[];for(const t of y.pageResults())t&&s.push(...t.page);return s});return{isLoading:j(()=>{const s=y.status();return s==="LoadingFirstPage"||s==="LoadingMore"}),loadMore:y.loadMore,results:a,status:y.status}},se=(e,n,r)=>{const{initialNumItems:y}=r,a=B(e,n,r),s=j(()=>{const c=[];for(const d of a.pageResults())d&&c.push(d.page);return c}),t=j(()=>a.status()==="LoadingFirstPage"),i=j(()=>a.status()==="CanLoadMore"),l=j(()=>a.status()==="LoadingMore");return{fetchNextPage:c=>{a.loadMore(c??y)},hasNextPage:i,isFetchingNextPage:l,isLoading:t,pages:s,status:a.status}};export{se as infiniteQuery,re as paginatedQuery};
@@ -0,0 +1,57 @@
1
+ import { Signal, DestroyRef } from '@angular/core';
2
+ import { UploadResult, CreateUploadOptions, UploadProtocol } from '@lunora/client/upload';
3
+ export { type ChunkedRestAdapter, type ChunkedRestAdapterOptions, type FingerprintFunction, type HeadersResolver, type MultipartAdapter, type MultipartAdapterOptions, type OnBeforeRequest, type RequestOptions, RestrictionError, type TusAdapter, type TusAdapterOptions, type UploadAdapter, UploadError, type UploadRestrictions, type UploadResult, createChunkedRestAdapter, createMultipartAdapter, createTusAdapter, createUpload } from '@lunora/client/upload';
4
+ export { UploadControl } from '@visulima/storage-client';
5
+ /** The lifecycle of an {@link upload} primitive. */
6
+ type UploadStatus = "error" | "idle" | "paused" | "success" | "uploading";
7
+ /**
8
+ * `UploadOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
9
+ * @experimental
10
+ */
11
+ interface UploadOptions extends Omit<CreateUploadOptions, "protocol"> {
12
+ /** `DestroyRef` whose `onDestroy` aborts any upload still in flight. Defaults to `inject(DestroyRef)`. */
13
+ destroyRef?: DestroyRef;
14
+ /** Which resumable protocol to speak. Default `"tus"`. Multipart isn't wrapped here — call `createMultipartAdapter` directly. */
15
+ protocol?: Exclude<UploadProtocol, "multipart">;
16
+ }
17
+ /**
18
+ * `UploadApi` is part of the experimental `@lunora/angular` API and may change without a major version bump.
19
+ * @experimental
20
+ */
21
+ interface UploadApi {
22
+ /** Abort the in-flight upload. Safe to call at any point, including after success. */
23
+ abort: () => void;
24
+ /** The upload error, or `undefined`. */
25
+ error: Signal<Error | undefined>;
26
+ /** Whether the upload is currently paused. */
27
+ isPaused: Signal<boolean>;
28
+ /** Pause the in-flight upload (TUS / chunked-REST only support this mid-upload). A no-op unless `status() === "uploading"`. */
29
+ pause: () => void;
30
+ /** Upload progress, as the adapter reports it (0–100). */
31
+ progress: Signal<number>;
32
+ /** The resolved upload result, or `undefined` before it finishes. */
33
+ result: Signal<UploadResult | undefined>;
34
+ /** Resume a paused upload. A no-op unless `status() === "paused"`. */
35
+ resume: () => void;
36
+ /** Start (or restart) the upload for `file`. Also resolves/rejects with the same result the `result`/`error` signals settle to. */
37
+ start: (file: File) => Promise<UploadResult>;
38
+ /** The upload lifecycle. */
39
+ status: Signal<UploadStatus>;
40
+ }
41
+ /**
42
+ * Drive a resumable (TUS / chunked-REST) upload as Angular signals: `progress`,
43
+ * `status`, `result` and `error`, plus `start` / `pause` / `resume` / `abort`
44
+ * actions. Defaults to TUS — the resumable path that survives pause/resume and
45
+ * a dropped connection.
46
+ *
47
+ * Call from an injection context (component/service field or constructor):
48
+ * ```ts
49
+ * protected readonly upload = upload({ endpoint: "/api/upload" });
50
+ * onFileSelected(file: File) {
51
+ * this.upload.start(file).catch(() => undefined); // errors also land in `error`
52
+ * }
53
+ * ```
54
+ * @experimental
55
+ */
56
+ declare const upload: (options: UploadOptions) => UploadApi;
57
+ export { UploadApi, UploadOptions, UploadStatus, upload };
@@ -0,0 +1,57 @@
1
+ import { Signal, DestroyRef } from '@angular/core';
2
+ import { UploadResult, CreateUploadOptions, UploadProtocol } from '@lunora/client/upload';
3
+ export { type ChunkedRestAdapter, type ChunkedRestAdapterOptions, type FingerprintFunction, type HeadersResolver, type MultipartAdapter, type MultipartAdapterOptions, type OnBeforeRequest, type RequestOptions, RestrictionError, type TusAdapter, type TusAdapterOptions, type UploadAdapter, UploadError, type UploadRestrictions, type UploadResult, createChunkedRestAdapter, createMultipartAdapter, createTusAdapter, createUpload } from '@lunora/client/upload';
4
+ export { UploadControl } from '@visulima/storage-client';
5
+ /** The lifecycle of an {@link upload} primitive. */
6
+ type UploadStatus = "error" | "idle" | "paused" | "success" | "uploading";
7
+ /**
8
+ * `UploadOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
9
+ * @experimental
10
+ */
11
+ interface UploadOptions extends Omit<CreateUploadOptions, "protocol"> {
12
+ /** `DestroyRef` whose `onDestroy` aborts any upload still in flight. Defaults to `inject(DestroyRef)`. */
13
+ destroyRef?: DestroyRef;
14
+ /** Which resumable protocol to speak. Default `"tus"`. Multipart isn't wrapped here — call `createMultipartAdapter` directly. */
15
+ protocol?: Exclude<UploadProtocol, "multipart">;
16
+ }
17
+ /**
18
+ * `UploadApi` is part of the experimental `@lunora/angular` API and may change without a major version bump.
19
+ * @experimental
20
+ */
21
+ interface UploadApi {
22
+ /** Abort the in-flight upload. Safe to call at any point, including after success. */
23
+ abort: () => void;
24
+ /** The upload error, or `undefined`. */
25
+ error: Signal<Error | undefined>;
26
+ /** Whether the upload is currently paused. */
27
+ isPaused: Signal<boolean>;
28
+ /** Pause the in-flight upload (TUS / chunked-REST only support this mid-upload). A no-op unless `status() === "uploading"`. */
29
+ pause: () => void;
30
+ /** Upload progress, as the adapter reports it (0–100). */
31
+ progress: Signal<number>;
32
+ /** The resolved upload result, or `undefined` before it finishes. */
33
+ result: Signal<UploadResult | undefined>;
34
+ /** Resume a paused upload. A no-op unless `status() === "paused"`. */
35
+ resume: () => void;
36
+ /** Start (or restart) the upload for `file`. Also resolves/rejects with the same result the `result`/`error` signals settle to. */
37
+ start: (file: File) => Promise<UploadResult>;
38
+ /** The upload lifecycle. */
39
+ status: Signal<UploadStatus>;
40
+ }
41
+ /**
42
+ * Drive a resumable (TUS / chunked-REST) upload as Angular signals: `progress`,
43
+ * `status`, `result` and `error`, plus `start` / `pause` / `resume` / `abort`
44
+ * actions. Defaults to TUS — the resumable path that survives pause/resume and
45
+ * a dropped connection.
46
+ *
47
+ * Call from an injection context (component/service field or constructor):
48
+ * ```ts
49
+ * protected readonly upload = upload({ endpoint: "/api/upload" });
50
+ * onFileSelected(file: File) {
51
+ * this.upload.start(file).catch(() => undefined); // errors also land in `error`
52
+ * }
53
+ * ```
54
+ * @experimental
55
+ */
56
+ declare const upload: (options: UploadOptions) => UploadApi;
57
+ export { UploadApi, UploadOptions, UploadStatus, upload };
@@ -0,0 +1 @@
1
+ import{inject as m,DestroyRef as y,signal as s}from"@angular/core";import{createUpload as E}from"@lunora/client/upload";import{RestrictionError as A,UploadError as b,createChunkedRestAdapter as C,createMultipartAdapter as D,createTusAdapter as P,createUpload as S}from"@lunora/client/upload";import{UploadControl as k}from"@visulima/storage-client";const v=l=>{const{destroyRef:i,...u}=l,c=i??m(y),r=E(u),n=s(0),t=s("idle"),d=s(void 0),o=s(void 0),a=s(!1);r.setOnStart(()=>{t.set("uploading")}),r.setOnProgress(e=>{n.set(e)}),r.setOnFinish(e=>{d.set(e),t.set("success")}),r.setOnError(e=>{o.set(e),t.set("error")});const f=e=>(o.set(void 0),d.set(void 0),n.set(0),a.set(!1),t.set("uploading"),r.upload(e)),R=()=>{t()==="uploading"&&(r.pause(),a.set(!0),t.set("paused"))},g=()=>{t()==="paused"&&r.resume().then(()=>{a.set(!1),t.set("uploading")}).catch(e=>{o.set(e instanceof Error?e:new Error(String(e))),t.set("error")})},p=()=>{r.abort()};return c.onDestroy(p),{abort:p,error:o.asReadonly(),isPaused:a.asReadonly(),pause:R,progress:n.asReadonly(),result:d.asReadonly(),resume:g,start:f,status:t.asReadonly()}};export{A as RestrictionError,k as UploadControl,b as UploadError,C as createChunkedRestAdapter,D as createMultipartAdapter,P as createTusAdapter,S as createUpload,v as upload};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/angular",
3
- "version": "1.0.0-alpha.20",
3
+ "version": "1.0.0-alpha.21",
4
4
  "description": "Angular reactive adapter for Lunora — signal-based live queries and mutations",
5
5
  "keywords": [
6
6
  "angular",
@@ -39,14 +39,19 @@
39
39
  "types": "./dist/index.d.ts",
40
40
  "import": "./dist/index.mjs"
41
41
  },
42
+ "./upload": {
43
+ "types": "./dist/upload.d.ts",
44
+ "import": "./dist/upload.mjs"
45
+ },
42
46
  "./package.json": "./package.json"
43
47
  },
44
48
  "publishConfig": {
45
49
  "access": "public"
46
50
  },
47
51
  "dependencies": {
48
- "@lunora/client": "1.0.0-alpha.35",
49
- "@lunora/ratelimit": "1.0.0-alpha.14"
52
+ "@lunora/client": "1.0.0-alpha.36",
53
+ "@lunora/ratelimit": "1.0.0-alpha.15",
54
+ "@visulima/storage-client": "1.0.0"
50
55
  },
51
56
  "peerDependencies": {
52
57
  "@angular/core": "^19.2.0 || ^20.0.0 || ^21.0.0 || ^22.0.0"
@@ -1 +0,0 @@
1
- import{inject as l,DestroyRef as h,signal as s}from"@angular/core";import{getIdentityStore as k}from"@lunora/client/auth";import{resolveLunoraClient as m}from"./LUNORA_CLIENT-B0pGE0Tv.mjs";const T=(o={})=>{const e=m(o.client),i=o.destroyRef??l(h),t=k(e),r=s(e.getAuthToken()),n=s(t.getUser()),u=e.onAuthTokenChange(()=>{r.set(e.getAuthToken())}),a=t.subscribe(()=>{n.set(t.getUser())});return i.onDestroy(()=>{u(),a()}),{setToken:g=>{e.setAuthToken(g)},token:r.asReadonly(),user:n.asReadonly()}};export{T as auth};
@@ -1 +0,0 @@
1
- import{computed as K,inject as j,DestroyRef as q,signal as O}from"@angular/core";import{initialPages as z,derivePaginationStatus as E,applyLoadMore as A,rebalance as J}from"@lunora/client/pagination";import{resolveLunoraClient as T}from"./LUNORA_CLIENT-B0pGE0Tv.mjs";import{s as B}from"./platform-BGZypnc9.mjs";const M=(r,s)=>`${r}::${JSON.stringify(s)}`,b=(r,s)=>({...s,paginationOpts:{cursor:r.lower,endCursor:r.upper,numItems:r.numItems}}),Q=(r,s,d)=>{const y=T(d.client),f=d.destroyRef===void 0,p=d.destroyRef??j(q),{initialNumItems:L,shardKey:x}=d,m=r.__lunoraRef,u=s==="skip"?{}:s,o=O(z(L)),R=O([]),D=O("LoadingFirstPage"),h=new Map,l=new Map,v=new Set,w=()=>{const n=o().map(g=>{const e=M(m,b(g,u));return l.get(e)});R.set(n);const{status:i}=E(s==="skip",n);D.set(i)},_=(n,i)=>{const g=e=>M(m,b(e,u));for(const e of i){const a=g(e);if(l.has(a))continue;const c=n.find(t=>t.lower===e.lower);if(c){const t=l.get(g(c));t&&l.set(a,t)}}},$=n=>{const i=new Set;for(const e of n)i.add(M(m,b(e,u)));for(const[e,a]of h)i.has(a.currentKey)||(a.unsub(),h.delete(e),l.delete(a.currentKey));const g=new Set([...h.values()].map(e=>e.currentKey));for(const e of n){const a=b(e,u),c=M(m,a);if(g.has(c))continue;const t={currentKey:c,unsub:void 0};v.add(c);const F=y.subscribe(r,a,N=>{if(l.set(t.currentKey,N),v.delete(t.currentKey),w(),v.size===0){const P=o(),S=J(P,R());S&&(_(P,S),o.set(S),C(S),w())}},{onError:()=>{v.delete(t.currentKey),w()},shardKey:x});t.unsub=F,h.set(c,t),g.add(c)}};let I=!1,k=!1;const C=n=>{if(I){k=!0;return}I=!0;try{let i=n;do k=!1,$(i),i=o();while(k)}finally{I=!1}};return s!=="skip"&&B(f)&&C(o()),w(),p.onDestroy(()=>{for(const n of h.values())n.unsub();h.clear(),l.clear()}),{loadMore:n=>{if(s==="skip")return;const{nextCursor:i,status:g}=E(!1,R());if(g!=="CanLoadMore")return;const e=A(o(),i,n);if(!e)return;const a=o().at(-1),c=e.at(-2);if(a&&c){const t=M(m,b(a,u)),F=M(m,b(c,u)),N=h.get(t);if(N&&t!==F){const P=l.get(t);P&&l.set(F,P),N.unsub(),h.delete(t),l.delete(t)}}o.set(e),C(o()),w()},pageResults:R,status:D}},W=(r,s,d)=>{const y=Q(r,s,d),f=K(()=>{const p=[];for(const L of y.pageResults())L&&p.push(...L.page);return p});return{isLoading:K(()=>{const p=y.status();return p==="LoadingFirstPage"||p==="LoadingMore"}),loadMore:y.loadMore,results:f,status:y.status}},X=(r,s,d)=>{const{initialNumItems:y}=d,f=Q(r,s,d),p=K(()=>{const u=[];for(const o of f.pageResults())o&&u.push(o.page);return u}),L=K(()=>f.status()==="LoadingFirstPage"),x=K(()=>f.status()==="CanLoadMore"),m=K(()=>f.status()==="LoadingMore");return{fetchNextPage:u=>{f.loadMore(u??y)},hasNextPage:x,isFetchingNextPage:m,isLoading:L,pages:p,status:f.status}};export{X as infiniteQuery,W as paginatedQuery};