@raster-app/react 0.0.0-stage → 0.1.0-canary-2e1834e
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/LICENSE +21 -0
- package/README.md +136 -2
- package/dist/context.d.ts +30 -0
- package/dist/context.js +50 -0
- package/dist/context.js.map +1 -0
- package/dist/icons.d.ts +22 -0
- package/dist/icons.js +21 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/picker-parts.d.ts +71 -0
- package/dist/picker-parts.js +150 -0
- package/dist/picker-parts.js.map +1 -0
- package/dist/picker-sign-in.d.ts +7 -0
- package/dist/picker-sign-in.js +18 -0
- package/dist/picker-sign-in.js.map +1 -0
- package/dist/query-keys.d.ts +13 -0
- package/dist/query-keys.js +14 -0
- package/dist/query-keys.js.map +1 -0
- package/dist/raster-picker.d.ts +23 -0
- package/dist/raster-picker.js +188 -0
- package/dist/raster-picker.js.map +1 -0
- package/dist/state-store.d.ts +18 -0
- package/dist/state-store.js +47 -0
- package/dist/state-store.js.map +1 -0
- package/dist/styles.css +1000 -0
- package/dist/use-asset-search.d.ts +28 -0
- package/dist/use-asset-search.js +82 -0
- package/dist/use-asset-search.js.map +1 -0
- package/dist/use-asset-upload.d.ts +46 -0
- package/dist/use-asset-upload.js +73 -0
- package/dist/use-asset-upload.js.map +1 -0
- package/dist/use-assets.d.ts +24 -0
- package/dist/use-assets.js +63 -0
- package/dist/use-assets.js.map +1 -0
- package/dist/use-libraries.d.ts +19 -0
- package/dist/use-libraries.js +57 -0
- package/dist/use-libraries.js.map +1 -0
- package/dist/use-navigation.d.ts +31 -0
- package/dist/use-navigation.js +111 -0
- package/dist/use-navigation.js.map +1 -0
- package/dist/use-organizations.d.ts +36 -0
- package/dist/use-organizations.js +23 -0
- package/dist/use-organizations.js.map +1 -0
- package/dist/use-promote-variant.d.ts +43 -0
- package/dist/use-promote-variant.js +65 -0
- package/dist/use-promote-variant.js.map +1 -0
- package/dist/use-session.d.ts +22 -0
- package/dist/use-session.js +37 -0
- package/dist/use-session.js.map +1 -0
- package/dist/use-sign-in.d.ts +34 -0
- package/dist/use-sign-in.js +79 -0
- package/dist/use-sign-in.js.map +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { type Organization, type RasterApiError, type RasterClient } from '@raster-app/sdk';
|
|
3
|
+
/** `/me`, which both the organization list and each library's access are read from. */
|
|
4
|
+
export declare function meQuery(client: RasterClient, identity: string | null): {
|
|
5
|
+
queryKey: readonly ["raster", string | null, "me"];
|
|
6
|
+
queryFn: () => Promise<{
|
|
7
|
+
organizationId: string;
|
|
8
|
+
organizationName: string | null;
|
|
9
|
+
plan: string | null;
|
|
10
|
+
libraries: {
|
|
11
|
+
library: string;
|
|
12
|
+
access: "read" | "write";
|
|
13
|
+
}[];
|
|
14
|
+
user?: {
|
|
15
|
+
name: string | null;
|
|
16
|
+
};
|
|
17
|
+
} | {
|
|
18
|
+
organizations: {
|
|
19
|
+
organizationId: string;
|
|
20
|
+
organizationName: string | null;
|
|
21
|
+
plan: string | null;
|
|
22
|
+
libraries: {
|
|
23
|
+
library: string;
|
|
24
|
+
access: "read" | "write";
|
|
25
|
+
}[];
|
|
26
|
+
}[];
|
|
27
|
+
user: {
|
|
28
|
+
name: string | null;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Every organization the current credential reaches, resolved from `/me`. An account in none
|
|
34
|
+
* gets an empty list.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useOrganizations(): UseQueryResult<Array<Organization>, RasterApiError>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { meOrganizationList, } from '@raster-app/sdk';
|
|
3
|
+
import { useRasterClient } from './context.js';
|
|
4
|
+
import { rasterKeys } from './query-keys.js';
|
|
5
|
+
import { useCredentialIdentity } from './use-session.js';
|
|
6
|
+
/** `/me`, which both the organization list and each library's access are read from. */
|
|
7
|
+
export function meQuery(client, identity) {
|
|
8
|
+
return { queryKey: rasterKeys.me(identity), queryFn: () => client.me() };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Every organization the current credential reaches, resolved from `/me`. An account in none
|
|
12
|
+
* gets an empty list.
|
|
13
|
+
*/
|
|
14
|
+
export function useOrganizations() {
|
|
15
|
+
const client = useRasterClient();
|
|
16
|
+
const identity = useCredentialIdentity();
|
|
17
|
+
return useQuery({
|
|
18
|
+
...meQuery(client, identity),
|
|
19
|
+
select: meOrganizationList,
|
|
20
|
+
enabled: identity !== null,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=use-organizations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-organizations.js","sourceRoot":"","sources":["../src/use-organizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAErE,OAAO,EAKN,kBAAkB,GAClB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAExD,uFAAuF;AACvF,MAAM,UAAU,OAAO,CAAC,MAAoB,EAAE,QAAuB;IACpE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAA;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC/B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAEhC,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAA;IAExC,OAAO,QAAQ,CAA0C;QACxD,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC;QAC5B,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE,QAAQ,KAAK,IAAI;KAC1B,CAAC,CAAA;AACH,CAAC","sourcesContent":["import { type UseQueryResult, useQuery } from '@tanstack/react-query'\n\nimport {\n\ttype Me,\n\ttype Organization,\n\ttype RasterApiError,\n\ttype RasterClient,\n\tmeOrganizationList,\n} from '@raster-app/sdk'\n\nimport { useRasterClient } from './context.js'\nimport { rasterKeys } from './query-keys.js'\nimport { useCredentialIdentity } from './use-session.js'\n\n/** `/me`, which both the organization list and each library's access are read from. */\nexport function meQuery(client: RasterClient, identity: string | null) {\n\treturn { queryKey: rasterKeys.me(identity), queryFn: () => client.me() }\n}\n\n/**\n * Every organization the current credential reaches, resolved from `/me`. An account in none\n * gets an empty list.\n */\nexport function useOrganizations(): UseQueryResult<Array<Organization>, RasterApiError> {\n\tconst client = useRasterClient()\n\n\tconst identity = useCredentialIdentity()\n\n\treturn useQuery<Me, RasterApiError, Array<Organization>>({\n\t\t...meQuery(client, identity),\n\t\tselect: meOrganizationList,\n\t\tenabled: identity !== null,\n\t})\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Asset, RasterApiError } from '@raster-app/sdk';
|
|
2
|
+
type PromoteOptions = {
|
|
3
|
+
organizationId: string;
|
|
4
|
+
libraryId: string;
|
|
5
|
+
/** The asset whose default is being replaced. */
|
|
6
|
+
assetId: string;
|
|
7
|
+
/** The variant to make the new default. */
|
|
8
|
+
variantId: string;
|
|
9
|
+
/**
|
|
10
|
+
* Applied to the asset in the library's cached list straight away, and kept until the swap
|
|
11
|
+
* lands or fails — say, to show the variant as the default already.
|
|
12
|
+
*/
|
|
13
|
+
optimistic?: (asset: Asset) => Asset;
|
|
14
|
+
};
|
|
15
|
+
export type PromoteState = {
|
|
16
|
+
status: 'idle';
|
|
17
|
+
} | {
|
|
18
|
+
status: 'promoting';
|
|
19
|
+
}
|
|
20
|
+
/** The swap landed; `asset` is the asset as it now reads. */
|
|
21
|
+
| {
|
|
22
|
+
status: 'done';
|
|
23
|
+
asset: Asset;
|
|
24
|
+
}
|
|
25
|
+
/** Raster took the promote, but had not swapped the files when the wait gave up. */
|
|
26
|
+
| {
|
|
27
|
+
status: 'timed-out';
|
|
28
|
+
} | {
|
|
29
|
+
status: 'error';
|
|
30
|
+
error: RasterApiError;
|
|
31
|
+
};
|
|
32
|
+
export type VariantPromote = {
|
|
33
|
+
state: PromoteState;
|
|
34
|
+
/**
|
|
35
|
+
* Promote a variant to be its asset's default, then wait until Raster has swapped the files
|
|
36
|
+
* (`assets.waitUntilPromoted`). Resolves with the asset as it then reads; null leaves the
|
|
37
|
+
* rest to `state`.
|
|
38
|
+
*/
|
|
39
|
+
promote: (options: PromoteOptions) => Promise<Asset | null>;
|
|
40
|
+
reset: () => void;
|
|
41
|
+
};
|
|
42
|
+
export declare function usePromoteVariant(): VariantPromote;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useMutation, useQueryClient, } from '@tanstack/react-query';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { useRasterClient } from './context.js';
|
|
4
|
+
import { rasterKeys } from './query-keys.js';
|
|
5
|
+
import { useCredentialIdentity } from './use-session.js';
|
|
6
|
+
function replaceAsset(data, assetId, update) {
|
|
7
|
+
if (data === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
return {
|
|
10
|
+
...data,
|
|
11
|
+
pages: data.pages.map((page) => page.map((asset) => (asset.id === assetId ? update(asset) : asset))),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function promoteState(mutation) {
|
|
15
|
+
if (mutation.isPending)
|
|
16
|
+
return { status: 'promoting' };
|
|
17
|
+
if (mutation.isError)
|
|
18
|
+
return { status: 'error', error: mutation.error };
|
|
19
|
+
if (!mutation.isSuccess)
|
|
20
|
+
return { status: 'idle' };
|
|
21
|
+
return mutation.data === null ? { status: 'timed-out' } : { status: 'done', asset: mutation.data };
|
|
22
|
+
}
|
|
23
|
+
export function usePromoteVariant() {
|
|
24
|
+
const client = useRasterClient();
|
|
25
|
+
const queryClient = useQueryClient();
|
|
26
|
+
const identity = useCredentialIdentity();
|
|
27
|
+
const mutation = useMutation({
|
|
28
|
+
mutationFn: async (options) => {
|
|
29
|
+
await client.assets.promote(options);
|
|
30
|
+
return client.assets.waitUntilPromoted(options);
|
|
31
|
+
},
|
|
32
|
+
onMutate: async ({ organizationId, libraryId, assetId, optimistic }) => {
|
|
33
|
+
if (optimistic === undefined)
|
|
34
|
+
return;
|
|
35
|
+
const key = rasterKeys.assets(identity, organizationId, libraryId);
|
|
36
|
+
// A page landing after this would put the asset back as it was.
|
|
37
|
+
await queryClient.cancelQueries({ queryKey: key });
|
|
38
|
+
queryClient.setQueryData(key, (data) => replaceAsset(data, assetId, optimistic));
|
|
39
|
+
},
|
|
40
|
+
// Landed, the asset as it now reads replaces the cached one. Otherwise the list is read
|
|
41
|
+
// again, which also undoes an optimistic update that never landed.
|
|
42
|
+
onSettled: (asset, _error, { organizationId, libraryId, assetId }) => {
|
|
43
|
+
const key = rasterKeys.assets(identity, organizationId, libraryId);
|
|
44
|
+
if (asset) {
|
|
45
|
+
queryClient.setQueryData(key, (data) => replaceAsset(data, assetId, () => asset));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
void queryClient.invalidateQueries({ queryKey: key });
|
|
49
|
+
}
|
|
50
|
+
void queryClient.invalidateQueries({ queryKey: rasterKeys.search(identity, organizationId) });
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const { mutateAsync, reset } = mutation;
|
|
54
|
+
const state = promoteState(mutation);
|
|
55
|
+
const promote = useCallback(async (options) => {
|
|
56
|
+
try {
|
|
57
|
+
return await mutateAsync(options);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}, [mutateAsync]);
|
|
63
|
+
return { state, promote, reset };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=use-promote-variant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-promote-variant.js","sourceRoot":"","sources":["../src/use-promote-variant.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype InfiniteData,\n\ttype UseMutationResult,\n\tuseMutation,\n\tuseQueryClient,\n} from '@tanstack/react-query'\nimport { useCallback } from 'react'\n\nimport type { Asset, RasterApiError } from '@raster-app/sdk'\n\nimport { useRasterClient } from './context.js'\nimport { rasterKeys } from './query-keys.js'\nimport { useCredentialIdentity } from './use-session.js'\n\ntype PromoteOptions = {\n\torganizationId: string\n\tlibraryId: string\n\t/** The asset whose default is being replaced. */\n\tassetId: string\n\t/** The variant to make the new default. */\n\tvariantId: string\n\t/**\n\t * Applied to the asset in the library's cached list straight away, and kept until the swap\n\t * lands or fails — say, to show the variant as the default already.\n\t */\n\toptimistic?: (asset: Asset) => Asset\n}\n\nexport type PromoteState =\n\t| { status: 'idle' }\n\t| { status: 'promoting' }\n\t/** The swap landed; `asset` is the asset as it now reads. */\n\t| { status: 'done'; asset: Asset }\n\t/** Raster took the promote, but had not swapped the files when the wait gave up. */\n\t| { status: 'timed-out' }\n\t| { status: 'error'; error: RasterApiError }\n\nexport type VariantPromote = {\n\tstate: PromoteState\n\t/**\n\t * Promote a variant to be its asset's default, then wait until Raster has swapped the files\n\t * (`assets.waitUntilPromoted`). Resolves with the asset as it then reads; null leaves the\n\t * rest to `state`.\n\t */\n\tpromote: (options: PromoteOptions) => Promise<Asset | null>\n\treset: () => void\n}\n\ntype AssetPages = InfiniteData<Array<Asset>>\n\nfunction replaceAsset(\n\tdata: AssetPages | undefined,\n\tassetId: string,\n\tupdate: (asset: Asset) => Asset\n): AssetPages | undefined {\n\tif (data === undefined) return undefined\n\treturn {\n\t\t...data,\n\t\tpages: data.pages.map((page) =>\n\t\t\tpage.map((asset) => (asset.id === assetId ? update(asset) : asset))\n\t\t),\n\t}\n}\n\nfunction promoteState(\n\tmutation: UseMutationResult<Asset | null, RasterApiError, PromoteOptions>\n): PromoteState {\n\tif (mutation.isPending) return { status: 'promoting' }\n\tif (mutation.isError) return { status: 'error', error: mutation.error }\n\tif (!mutation.isSuccess) return { status: 'idle' }\n\treturn mutation.data === null ? { status: 'timed-out' } : { status: 'done', asset: mutation.data }\n}\n\nexport function usePromoteVariant(): VariantPromote {\n\tconst client = useRasterClient()\n\tconst queryClient = useQueryClient()\n\n\tconst identity = useCredentialIdentity()\n\tconst mutation = useMutation<Asset | null, RasterApiError, PromoteOptions>({\n\t\tmutationFn: async (options) => {\n\t\t\tawait client.assets.promote(options)\n\t\t\treturn client.assets.waitUntilPromoted(options)\n\t\t},\n\t\tonMutate: async ({ organizationId, libraryId, assetId, optimistic }) => {\n\t\t\tif (optimistic === undefined) return\n\t\t\tconst key = rasterKeys.assets(identity, organizationId, libraryId)\n\t\t\t// A page landing after this would put the asset back as it was.\n\t\t\tawait queryClient.cancelQueries({ queryKey: key })\n\t\t\tqueryClient.setQueryData<AssetPages>(key, (data) => replaceAsset(data, assetId, optimistic))\n\t\t},\n\t\t// Landed, the asset as it now reads replaces the cached one. Otherwise the list is read\n\t\t// again, which also undoes an optimistic update that never landed.\n\t\tonSettled: (asset, _error, { organizationId, libraryId, assetId }) => {\n\t\t\tconst key = rasterKeys.assets(identity, organizationId, libraryId)\n\t\t\tif (asset) {\n\t\t\t\tqueryClient.setQueryData<AssetPages>(key, (data) =>\n\t\t\t\t\treplaceAsset(data, assetId, () => asset)\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tvoid queryClient.invalidateQueries({ queryKey: key })\n\t\t\t}\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: rasterKeys.search(identity, organizationId) })\n\t\t},\n\t})\n\n\tconst { mutateAsync, reset } = mutation\n\tconst state = promoteState(mutation)\n\n\tconst promote = useCallback<VariantPromote['promote']>(\n\t\tasync (options) => {\n\t\t\ttry {\n\t\t\t\treturn await mutateAsync(options)\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\t\t[mutateAsync]\n\t)\n\n\treturn { state, promote, reset }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type StoredCredentials } from '@raster-app/sdk';
|
|
2
|
+
export type RasterSession = {
|
|
3
|
+
/** `undefined` until the store has been read. Distinguishes "loading" from "signed out". */
|
|
4
|
+
credentials: StoredCredentials | undefined;
|
|
5
|
+
/** Whether there is any credential to call the API with — a session or an API key. */
|
|
6
|
+
isConfigured: boolean;
|
|
7
|
+
/** Whether the credential is an OAuth session specifically (an API key is not). */
|
|
8
|
+
isSignedIn: boolean;
|
|
9
|
+
/** The account name, for "Signed in as …". Null for an API key. */
|
|
10
|
+
userName: string | null;
|
|
11
|
+
signOut: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The current credential, re-rendering on every change — a sign-in, a token refresh, a
|
|
15
|
+
* sign-out. `useSyncExternalStore` also catches a change between render and effect.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useSession(): RasterSession;
|
|
18
|
+
/**
|
|
19
|
+
* The sign-in every query key is scoped by, or null while there is none to fetch with. A token
|
|
20
|
+
* refresh keeps it; a different account, or the same one signing in again, changes it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useCredentialIdentity(): string | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useCallback, useEffect, useSyncExternalStore } from 'react';
|
|
2
|
+
import { isConfigured as isConfiguredCredentials } from '@raster-app/sdk';
|
|
3
|
+
import { useRasterClient } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* The current credential, re-rendering on every change — a sign-in, a token refresh, a
|
|
6
|
+
* sign-out. `useSyncExternalStore` also catches a change between render and effect.
|
|
7
|
+
*/
|
|
8
|
+
export function useSession() {
|
|
9
|
+
const client = useRasterClient();
|
|
10
|
+
const credentials = useSyncExternalStore(useCallback((listener) => client.session.subscribe(listener), [client]), () => client.session.current(), () => undefined);
|
|
11
|
+
const signOut = useCallback(() => client.auth.signOut(), [client]);
|
|
12
|
+
// Hydration is single-flight in the SDK, so calling it from every mounted consumer costs
|
|
13
|
+
// one store read in total.
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
void client.session.hydrate().catch(() => {
|
|
16
|
+
// A store that cannot be read leaves the client signed out, so the host shows its
|
|
17
|
+
// sign-in screen.
|
|
18
|
+
});
|
|
19
|
+
}, [client]);
|
|
20
|
+
return {
|
|
21
|
+
credentials,
|
|
22
|
+
isConfigured: isConfiguredCredentials(credentials),
|
|
23
|
+
isSignedIn: credentials?.oauth != null,
|
|
24
|
+
userName: credentials?.userName ?? null,
|
|
25
|
+
signOut,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The sign-in every query key is scoped by, or null while there is none to fetch with. A token
|
|
30
|
+
* refresh keeps it; a different account, or the same one signing in again, changes it.
|
|
31
|
+
*/
|
|
32
|
+
export function useCredentialIdentity() {
|
|
33
|
+
const client = useRasterClient();
|
|
34
|
+
const { isConfigured } = useSession();
|
|
35
|
+
return isConfigured ? client.session.credentialIdentity() : null;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=use-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-session.js","sourceRoot":"","sources":["../src/use-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEpE,OAAO,EAA0B,YAAY,IAAI,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAEjG,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAc9C;;;GAGG;AACH,MAAM,UAAU,UAAU;IACzB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAEhC,MAAM,WAAW,GAAG,oBAAoB,CACvC,WAAW,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EACvE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAC9B,GAAG,EAAE,CAAC,SAAS,CACf,CAAA;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAElE,yFAAyF;IACzF,2BAA2B;IAC3B,SAAS,CAAC,GAAG,EAAE;QACd,KAAK,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACxC,kFAAkF;YAClF,kBAAkB;QACnB,CAAC,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,OAAO;QACN,WAAW;QACX,YAAY,EAAE,uBAAuB,CAAC,WAAW,CAAC;QAClD,UAAU,EAAE,WAAW,EAAE,KAAK,IAAI,IAAI;QACtC,QAAQ,EAAE,WAAW,EAAE,QAAQ,IAAI,IAAI;QACvC,OAAO;KACP,CAAA;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACpC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAChC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CAAA;IAErC,OAAO,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACjE,CAAC","sourcesContent":["import { useCallback, useEffect, useSyncExternalStore } from 'react'\n\nimport { type StoredCredentials, isConfigured as isConfiguredCredentials } from '@raster-app/sdk'\n\nimport { useRasterClient } from './context.js'\n\nexport type RasterSession = {\n\t/** `undefined` until the store has been read. Distinguishes \"loading\" from \"signed out\". */\n\tcredentials: StoredCredentials | undefined\n\t/** Whether there is any credential to call the API with — a session or an API key. */\n\tisConfigured: boolean\n\t/** Whether the credential is an OAuth session specifically (an API key is not). */\n\tisSignedIn: boolean\n\t/** The account name, for \"Signed in as …\". Null for an API key. */\n\tuserName: string | null\n\tsignOut: () => Promise<void>\n}\n\n/**\n * The current credential, re-rendering on every change — a sign-in, a token refresh, a\n * sign-out. `useSyncExternalStore` also catches a change between render and effect.\n */\nexport function useSession(): RasterSession {\n\tconst client = useRasterClient()\n\n\tconst credentials = useSyncExternalStore(\n\t\tuseCallback((listener) => client.session.subscribe(listener), [client]),\n\t\t() => client.session.current(),\n\t\t() => undefined\n\t)\n\n\tconst signOut = useCallback(() => client.auth.signOut(), [client])\n\n\t// Hydration is single-flight in the SDK, so calling it from every mounted consumer costs\n\t// one store read in total.\n\tuseEffect(() => {\n\t\tvoid client.session.hydrate().catch(() => {\n\t\t\t// A store that cannot be read leaves the client signed out, so the host shows its\n\t\t\t// sign-in screen.\n\t\t})\n\t}, [client])\n\n\treturn {\n\t\tcredentials,\n\t\tisConfigured: isConfiguredCredentials(credentials),\n\t\tisSignedIn: credentials?.oauth != null,\n\t\tuserName: credentials?.userName ?? null,\n\t\tsignOut,\n\t}\n}\n\n/**\n * The sign-in every query key is scoped by, or null while there is none to fetch with. A token\n * refresh keeps it; a different account, or the same one signing in again, changes it.\n */\nexport function useCredentialIdentity(): string | null {\n\tconst client = useRasterClient()\n\tconst { isConfigured } = useSession()\n\n\treturn isConfigured ? client.session.credentialIdentity() : null\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type DeviceAuthorization, type RasterApiError } from '@raster-app/sdk';
|
|
2
|
+
export type SignInState = {
|
|
3
|
+
status: 'idle';
|
|
4
|
+
} | {
|
|
5
|
+
status: 'connecting';
|
|
6
|
+
}
|
|
7
|
+
/** The device grant is waiting for the user to approve in their browser. */
|
|
8
|
+
| {
|
|
9
|
+
status: 'awaitingApproval';
|
|
10
|
+
device: DeviceAuthorization;
|
|
11
|
+
} | {
|
|
12
|
+
status: 'error';
|
|
13
|
+
error: RasterApiError;
|
|
14
|
+
};
|
|
15
|
+
export type SignIn = {
|
|
16
|
+
state: SignInState;
|
|
17
|
+
/** Device grant (RFC 8628). Works in every host. */
|
|
18
|
+
signInWithDevice: () => Promise<void>;
|
|
19
|
+
/** Verify and adopt a pasted organization API key. */
|
|
20
|
+
connectApiKey: (apiKey: string) => Promise<void>;
|
|
21
|
+
/** Abandon a device sign-in that is still waiting. */
|
|
22
|
+
cancel: () => void;
|
|
23
|
+
reset: () => void;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Drives both ways in — the device grant and an API key — and holds their state: a device
|
|
27
|
+
* grant waiting on the user, or the error that ended an attempt. Read the credential itself
|
|
28
|
+
* with `useSession`. `scope` and `organizationId` go to the device grant, as
|
|
29
|
+
* `auth.signInWithDevice` takes them.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useSignIn(options?: {
|
|
32
|
+
scope?: 'user' | 'org';
|
|
33
|
+
organizationId?: string;
|
|
34
|
+
}): SignIn;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { isAbortError, toRasterApiError, } from '@raster-app/sdk';
|
|
3
|
+
import { useRasterClient } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Drives both ways in — the device grant and an API key — and holds their state: a device
|
|
6
|
+
* grant waiting on the user, or the error that ended an attempt. Read the credential itself
|
|
7
|
+
* with `useSession`. `scope` and `organizationId` go to the device grant, as
|
|
8
|
+
* `auth.signInWithDevice` takes them.
|
|
9
|
+
*/
|
|
10
|
+
export function useSignIn(options = {}) {
|
|
11
|
+
const { scope, organizationId } = options;
|
|
12
|
+
const client = useRasterClient();
|
|
13
|
+
const [state, setState] = useState({ status: 'idle' });
|
|
14
|
+
const controllerRef = useRef(null);
|
|
15
|
+
const mountedRef = useRef(true);
|
|
16
|
+
/** Ignore results that arrive after unmount; `setState` on a dead component is noise. */
|
|
17
|
+
const settle = useCallback((next) => {
|
|
18
|
+
if (mountedRef.current)
|
|
19
|
+
setState(next);
|
|
20
|
+
}, []);
|
|
21
|
+
const fail = useCallback((caught) => {
|
|
22
|
+
// A cancellation is not a failure to report — the user asked for it.
|
|
23
|
+
if (isAbortError(caught)) {
|
|
24
|
+
settle({ status: 'idle' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
settle({ status: 'error', error: toRasterApiError(caught) });
|
|
28
|
+
}, [settle]);
|
|
29
|
+
const signInWithDevice = useCallback(async () => {
|
|
30
|
+
controllerRef.current?.abort();
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
controllerRef.current = controller;
|
|
33
|
+
settle({ status: 'connecting' });
|
|
34
|
+
try {
|
|
35
|
+
await client.auth.signInWithDevice({
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
onCode: (device) => settle({ status: 'awaitingApproval', device }),
|
|
38
|
+
scope,
|
|
39
|
+
organizationId,
|
|
40
|
+
});
|
|
41
|
+
settle({ status: 'idle' });
|
|
42
|
+
}
|
|
43
|
+
catch (caught) {
|
|
44
|
+
fail(caught);
|
|
45
|
+
}
|
|
46
|
+
}, [client, settle, fail, scope, organizationId]);
|
|
47
|
+
const connectApiKey = useCallback(async (apiKey) => {
|
|
48
|
+
settle({ status: 'connecting' });
|
|
49
|
+
try {
|
|
50
|
+
await client.auth.connect({ apiKey });
|
|
51
|
+
settle({ status: 'idle' });
|
|
52
|
+
}
|
|
53
|
+
catch (caught) {
|
|
54
|
+
fail(caught);
|
|
55
|
+
}
|
|
56
|
+
}, [client, settle, fail]);
|
|
57
|
+
const cancel = useCallback(() => {
|
|
58
|
+
controllerRef.current?.abort();
|
|
59
|
+
settle({ status: 'idle' });
|
|
60
|
+
}, [settle]);
|
|
61
|
+
const reset = useCallback(() => settle({ status: 'idle' }), [settle]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
mountedRef.current = true;
|
|
64
|
+
return () => {
|
|
65
|
+
mountedRef.current = false;
|
|
66
|
+
// A device sign-in polls indefinitely. Leaving it running after unmount would keep
|
|
67
|
+
// hitting the token endpoint for a screen nobody is looking at.
|
|
68
|
+
controllerRef.current?.abort();
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
71
|
+
return {
|
|
72
|
+
state,
|
|
73
|
+
signInWithDevice,
|
|
74
|
+
connectApiKey,
|
|
75
|
+
cancel,
|
|
76
|
+
reset,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=use-sign-in.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-sign-in.js","sourceRoot":"","sources":["../src/use-sign-in.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport {\n\ttype DeviceAuthorization,\n\ttype RasterApiError,\n\tisAbortError,\n\ttoRasterApiError,\n} from '@raster-app/sdk'\n\nimport { useRasterClient } from './context.js'\n\nexport type SignInState =\n\t| { status: 'idle' }\n\t| { status: 'connecting' }\n\t/** The device grant is waiting for the user to approve in their browser. */\n\t| { status: 'awaitingApproval'; device: DeviceAuthorization }\n\t| { status: 'error'; error: RasterApiError }\n\nexport type SignIn = {\n\tstate: SignInState\n\t/** Device grant (RFC 8628). Works in every host. */\n\tsignInWithDevice: () => Promise<void>\n\t/** Verify and adopt a pasted organization API key. */\n\tconnectApiKey: (apiKey: string) => Promise<void>\n\t/** Abandon a device sign-in that is still waiting. */\n\tcancel: () => void\n\treset: () => void\n}\n\n/**\n * Drives both ways in — the device grant and an API key — and holds their state: a device\n * grant waiting on the user, or the error that ended an attempt. Read the credential itself\n * with `useSession`. `scope` and `organizationId` go to the device grant, as\n * `auth.signInWithDevice` takes them.\n */\nexport function useSignIn(\n\toptions: { scope?: 'user' | 'org'; organizationId?: string } = {}\n): SignIn {\n\tconst { scope, organizationId } = options\n\tconst client = useRasterClient()\n\n\tconst [state, setState] = useState<SignInState>({ status: 'idle' })\n\n\tconst controllerRef = useRef<AbortController | null>(null)\n\tconst mountedRef = useRef(true)\n\n\t/** Ignore results that arrive after unmount; `setState` on a dead component is noise. */\n\tconst settle = useCallback((next: SignInState) => {\n\t\tif (mountedRef.current) setState(next)\n\t}, [])\n\n\tconst fail = useCallback(\n\t\t(caught: unknown) => {\n\t\t\t// A cancellation is not a failure to report — the user asked for it.\n\t\t\tif (isAbortError(caught)) {\n\t\t\t\tsettle({ status: 'idle' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsettle({ status: 'error', error: toRasterApiError(caught) })\n\t\t},\n\t\t[settle]\n\t)\n\n\tconst signInWithDevice = useCallback(async () => {\n\t\tcontrollerRef.current?.abort()\n\t\tconst controller = new AbortController()\n\t\tcontrollerRef.current = controller\n\n\t\tsettle({ status: 'connecting' })\n\t\ttry {\n\t\t\tawait client.auth.signInWithDevice({\n\t\t\t\tsignal: controller.signal,\n\t\t\t\tonCode: (device) => settle({ status: 'awaitingApproval', device }),\n\t\t\t\tscope,\n\t\t\t\torganizationId,\n\t\t\t})\n\t\t\tsettle({ status: 'idle' })\n\t\t} catch (caught) {\n\t\t\tfail(caught)\n\t\t}\n\t}, [client, settle, fail, scope, organizationId])\n\n\tconst connectApiKey = useCallback(\n\t\tasync (apiKey: string) => {\n\t\t\tsettle({ status: 'connecting' })\n\t\t\ttry {\n\t\t\t\tawait client.auth.connect({ apiKey })\n\t\t\t\tsettle({ status: 'idle' })\n\t\t\t} catch (caught) {\n\t\t\t\tfail(caught)\n\t\t\t}\n\t\t},\n\t\t[client, settle, fail]\n\t)\n\n\tconst cancel = useCallback(() => {\n\t\tcontrollerRef.current?.abort()\n\t\tsettle({ status: 'idle' })\n\t}, [settle])\n\n\tconst reset = useCallback(() => settle({ status: 'idle' }), [settle])\n\n\tuseEffect(() => {\n\t\tmountedRef.current = true\n\t\treturn () => {\n\t\t\tmountedRef.current = false\n\t\t\t// A device sign-in polls indefinitely. Leaving it running after unmount would keep\n\t\t\t// hitting the token endpoint for a screen nobody is looking at.\n\t\t\tcontrollerRef.current?.abort()\n\t\t}\n\t}, [])\n\n\treturn {\n\t\tstate,\n\t\tsignInWithDevice,\n\t\tconnectApiKey,\n\t\tcancel,\n\t\treset,\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"name": "@raster-app/react",
|
|
3
|
+
"version": "0.1.0-canary-2e1834e",
|
|
4
|
+
"description": "React provider and hooks for the Raster SDK: session, organizations, libraries, assets, search and uploads.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://raster.app/docs",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"raster",
|
|
9
|
+
"dam",
|
|
10
|
+
"assets",
|
|
11
|
+
"react",
|
|
12
|
+
"hooks",
|
|
13
|
+
"figma",
|
|
14
|
+
"sanity",
|
|
15
|
+
"plugin"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"sideEffects": [
|
|
19
|
+
"*.css"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./styles.css": "./dist/styles.css",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=24"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/styles.css dist/",
|
|
38
|
+
"prepack": "bun run build",
|
|
39
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
40
|
+
"check-types": "tsc --noEmit",
|
|
41
|
+
"lint": "biome lint --error-on-warnings .",
|
|
42
|
+
"test": "bun test"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@raster-app/sdk": "0.1.0-canary-2e1834e",
|
|
46
|
+
"@tanstack/react-query": "^5.103.1"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": "^18 || ^19"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/react": "^19.2.2",
|
|
53
|
+
"react": "^19.2.0"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
6
58
|
}
|