@datalayer/core 1.2.10 → 1.2.13
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 +5 -5
- package/lib/api/iam/authentication.js +6 -4
- package/lib/api/iam/healthz.js +2 -2
- package/lib/api/iam/oauth2.js +18 -12
- package/lib/api/iam/secrets.js +1 -2
- package/lib/api/mcp/observability.d.ts +9 -2
- package/lib/api/mcp/observability.js +40 -15
- package/lib/client/auth/strategies.js +1 -1
- package/lib/client/mixins/IAMMixin.js +2 -2
- package/lib/components/checkout/StripeCheckout.d.ts +1 -1
- package/lib/components/checkout/StripeCheckout.js +1 -1
- package/lib/components/display/VisuallyHidden.d.ts +1 -2
- package/lib/components/sharing/sandboxSharing.js +7 -1
- package/lib/config/Configuration.d.ts +4 -4
- package/lib/config/Configuration.js +7 -7
- package/lib/config/index.d.ts +1 -0
- package/lib/config/index.js +1 -0
- package/lib/config/planes.d.ts +45 -0
- package/lib/config/planes.js +53 -0
- package/lib/hooks/useBackdrop.d.ts +4 -5
- package/lib/hooks/useCache.d.ts +52 -19
- package/lib/hooks/useCache.js +310 -198
- package/lib/hooks/useContents.js +3 -0
- package/lib/hooks/useMcp.js +1 -1
- package/lib/hooks/useNavigate.js +10 -0
- package/lib/hooks/useWindowSize.js +1 -1
- package/lib/models/IAMProvidersSpecs.d.ts +10 -5
- package/lib/models/IAMProvidersSpecs.js +0 -23
- package/lib/models/URN.d.ts +37 -0
- package/lib/models/URN.js +59 -1
- package/lib/navigation/adapters/react-router.d.ts +1 -1
- package/lib/navigation/adapters/react-router.js +3 -0
- package/lib/state/sessionEnd.js +1 -1
- package/lib/state/substates/CoreState.js +3 -16
- package/lib/state/substates/IAMState.d.ts +3 -3
- package/lib/state/substates/NavigationState.d.ts +92 -0
- package/lib/state/substates/NavigationState.js +304 -0
- package/lib/state/substates/index.d.ts +1 -0
- package/lib/state/substates/index.js +1 -0
- package/lib/utils/Handles.js +1 -1
- package/lib/utils/Screencapture.d.ts +15 -0
- package/lib/utils/Screencapture.js +26 -23
- package/lib/views/mcp/McpDashboard.js +15 -10
- package/lib/views/mcp/McpHome.js +13 -6
- package/lib/views/mcp/types.d.ts +4 -0
- package/package.json +9 -7
- package/scripts/generate-mcp-types.py +15 -9
package/lib/hooks/useCache.js
CHANGED
|
@@ -58,6 +58,19 @@ const DEFAULT_QUERY_OPTIONS = {
|
|
|
58
58
|
// Ensure queries prioritize cache over network when data is fresh
|
|
59
59
|
networkMode: 'online',
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* A list of what a space holds — all of it, or the items of one type. Every
|
|
63
|
+
* such list takes these options (`spaceListsRefetchOnMount.unit.test.ts`
|
|
64
|
+
* holds them to it). Refetched every time a page mounts it: an item
|
|
65
|
+
* created elsewhere — a cell has no create mutation here to clear this cache —
|
|
66
|
+
* would otherwise stay missing from the list for the five minutes the default
|
|
67
|
+
* options keep it fresh, and coming back to the list is exactly when somebody
|
|
68
|
+
* looks for it.
|
|
69
|
+
*/
|
|
70
|
+
const LIST_QUERY_OPTIONS = {
|
|
71
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
72
|
+
refetchOnMount: 'always',
|
|
73
|
+
};
|
|
61
74
|
/**
|
|
62
75
|
* Refresh what a visitor sees, not just what the editor is looking at.
|
|
63
76
|
*
|
|
@@ -727,6 +740,10 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
727
740
|
*/
|
|
728
741
|
if (raw.publicUrl)
|
|
729
742
|
meta.publicUrl = raw.publicUrl;
|
|
743
|
+
if (raw.shareCardUrl)
|
|
744
|
+
meta.shareCardUrl = raw.shareCardUrl;
|
|
745
|
+
if (raw.shareCardVersion)
|
|
746
|
+
meta.shareCardVersion = raw.shareCardVersion;
|
|
730
747
|
return meta;
|
|
731
748
|
};
|
|
732
749
|
/**
|
|
@@ -859,6 +876,32 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
859
876
|
owner: toItemOwner(raw),
|
|
860
877
|
};
|
|
861
878
|
};
|
|
879
|
+
/**
|
|
880
|
+
* A published agentic UI application: what it answers, and its definition
|
|
881
|
+
* as the library hands it over — `model_s`, the string the Studio reads
|
|
882
|
+
* the definition from, left unparsed because the Studio owns its shape.
|
|
883
|
+
*/
|
|
884
|
+
const toApp = (raw) => {
|
|
885
|
+
return {
|
|
886
|
+
id: raw.uid,
|
|
887
|
+
type: 'app',
|
|
888
|
+
name: raw.name_t,
|
|
889
|
+
description: raw.description_t,
|
|
890
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
891
|
+
public: raw.is_public_b ?? false,
|
|
892
|
+
model_s: raw.model_s,
|
|
893
|
+
creationDate: raw.creation_ts_dt
|
|
894
|
+
? new Date(raw.creation_ts_dt)
|
|
895
|
+
: undefined,
|
|
896
|
+
lastUpdateDate: raw.last_update_ts_dt
|
|
897
|
+
? new Date(raw.last_update_ts_dt)
|
|
898
|
+
: undefined,
|
|
899
|
+
lastPublicationDate: raw.published_ts_dt
|
|
900
|
+
? new Date(raw.published_ts_dt)
|
|
901
|
+
: undefined,
|
|
902
|
+
owner: toItemOwner(raw),
|
|
903
|
+
};
|
|
904
|
+
};
|
|
862
905
|
/** An evaluator of the platform's catalogue (B5-01), published as an agent is. */
|
|
863
906
|
const toEvaluator = (raw) => {
|
|
864
907
|
return {
|
|
@@ -1371,6 +1414,10 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1371
1414
|
return toInvestigation(item);
|
|
1372
1415
|
case 'evaluator':
|
|
1373
1416
|
return toEvaluator(item);
|
|
1417
|
+
// Without this a published app answered `{}`, as a deck once did: a
|
|
1418
|
+
// card with no type and no name.
|
|
1419
|
+
case 'app':
|
|
1420
|
+
return toApp(item);
|
|
1374
1421
|
case 'sitecard':
|
|
1375
1422
|
return toSiteCard(item);
|
|
1376
1423
|
default:
|
|
@@ -2341,7 +2388,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2341
2388
|
}
|
|
2342
2389
|
return [];
|
|
2343
2390
|
},
|
|
2344
|
-
...
|
|
2391
|
+
...LIST_QUERY_OPTIONS,
|
|
2345
2392
|
enabled: !!spaceId,
|
|
2346
2393
|
});
|
|
2347
2394
|
};
|
|
@@ -2473,7 +2520,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2473
2520
|
}
|
|
2474
2521
|
return [];
|
|
2475
2522
|
},
|
|
2476
|
-
...
|
|
2523
|
+
...LIST_QUERY_OPTIONS,
|
|
2477
2524
|
enabled: !!spaceId,
|
|
2478
2525
|
});
|
|
2479
2526
|
};
|
|
@@ -2648,7 +2695,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2648
2695
|
}
|
|
2649
2696
|
return [];
|
|
2650
2697
|
},
|
|
2651
|
-
...
|
|
2698
|
+
...LIST_QUERY_OPTIONS,
|
|
2652
2699
|
enabled: !!spaceId,
|
|
2653
2700
|
});
|
|
2654
2701
|
};
|
|
@@ -4156,7 +4203,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4156
4203
|
return [];
|
|
4157
4204
|
},
|
|
4158
4205
|
enabled: !!spaceId,
|
|
4159
|
-
...
|
|
4206
|
+
...LIST_QUERY_OPTIONS,
|
|
4160
4207
|
});
|
|
4161
4208
|
};
|
|
4162
4209
|
/**
|
|
@@ -4343,7 +4390,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4343
4390
|
return [];
|
|
4344
4391
|
},
|
|
4345
4392
|
enabled: !!spaceId,
|
|
4346
|
-
...
|
|
4393
|
+
...LIST_QUERY_OPTIONS,
|
|
4347
4394
|
});
|
|
4348
4395
|
};
|
|
4349
4396
|
/**
|
|
@@ -4434,7 +4481,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4434
4481
|
return [];
|
|
4435
4482
|
},
|
|
4436
4483
|
enabled: !!spaceId,
|
|
4437
|
-
...
|
|
4484
|
+
...LIST_QUERY_OPTIONS,
|
|
4438
4485
|
});
|
|
4439
4486
|
};
|
|
4440
4487
|
/**
|
|
@@ -4474,7 +4521,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4474
4521
|
return [];
|
|
4475
4522
|
},
|
|
4476
4523
|
enabled: !!spaceId,
|
|
4477
|
-
...
|
|
4524
|
+
...LIST_QUERY_OPTIONS,
|
|
4478
4525
|
});
|
|
4479
4526
|
};
|
|
4480
4527
|
/**
|
|
@@ -4543,7 +4590,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4543
4590
|
return [];
|
|
4544
4591
|
},
|
|
4545
4592
|
enabled: !!spaceId,
|
|
4546
|
-
...
|
|
4593
|
+
...LIST_QUERY_OPTIONS,
|
|
4547
4594
|
});
|
|
4548
4595
|
};
|
|
4549
4596
|
/**
|
|
@@ -4637,7 +4684,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4637
4684
|
return [];
|
|
4638
4685
|
},
|
|
4639
4686
|
enabled: !!spaceId,
|
|
4640
|
-
...
|
|
4687
|
+
...LIST_QUERY_OPTIONS,
|
|
4641
4688
|
});
|
|
4642
4689
|
};
|
|
4643
4690
|
/**
|
|
@@ -6328,6 +6375,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6328
6375
|
'evalset',
|
|
6329
6376
|
'dataset',
|
|
6330
6377
|
'agent',
|
|
6378
|
+
// An agentic UI application, published from the Studio.
|
|
6379
|
+
'app',
|
|
6331
6380
|
];
|
|
6332
6381
|
const librarySearchQueryString = ({ q, types, max = 20, offset = 0, sort = 'relevance', featured = false, owner, }) => {
|
|
6333
6382
|
const normalizedTypes = Array.from(new Set((types || [])
|
|
@@ -6853,6 +6902,157 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6853
6902
|
onSuccess: invalidateArtifactVisibility,
|
|
6854
6903
|
});
|
|
6855
6904
|
};
|
|
6905
|
+
/** Publish a public artifact natively to LinkedIn through Library's saga. */
|
|
6906
|
+
const usePublishLinkedInShare = () => {
|
|
6907
|
+
return useMutation({
|
|
6908
|
+
mutationFn: async ({ itemId, commentary, idempotencyKey, imageDataUrl, imageAltText, }) => {
|
|
6909
|
+
const resp = await requestDatalayer({
|
|
6910
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/linkedin`,
|
|
6911
|
+
method: 'POST',
|
|
6912
|
+
headers: { 'Idempotency-Key': idempotencyKey },
|
|
6913
|
+
body: { commentary, imageDataUrl, imageAltText },
|
|
6914
|
+
});
|
|
6915
|
+
if (!resp.success) {
|
|
6916
|
+
throw new Error(resp.message || 'Failed to publish to LinkedIn');
|
|
6917
|
+
}
|
|
6918
|
+
return resp.share;
|
|
6919
|
+
},
|
|
6920
|
+
});
|
|
6921
|
+
};
|
|
6922
|
+
const useUnpublishLinkedInShare = () => {
|
|
6923
|
+
return useMutation({
|
|
6924
|
+
mutationFn: async (idempotencyKey) => {
|
|
6925
|
+
const resp = await requestDatalayer({
|
|
6926
|
+
url: `${configuration.libraryUrl}/api/library/v1/shares/linkedin/${encodeURIComponent(idempotencyKey)}`,
|
|
6927
|
+
method: 'DELETE',
|
|
6928
|
+
});
|
|
6929
|
+
if (!resp.success) {
|
|
6930
|
+
throw new Error(resp.message || 'Failed to unpublish from LinkedIn');
|
|
6931
|
+
}
|
|
6932
|
+
return resp.share;
|
|
6933
|
+
},
|
|
6934
|
+
});
|
|
6935
|
+
};
|
|
6936
|
+
const useLinkedInShares = (itemId, enabled = true) => {
|
|
6937
|
+
return useQuery({
|
|
6938
|
+
queryKey: ['library', 'social-shares', 'linkedin', itemId],
|
|
6939
|
+
queryFn: async () => {
|
|
6940
|
+
const resp = await requestDatalayer({
|
|
6941
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/linkedin`,
|
|
6942
|
+
method: 'GET',
|
|
6943
|
+
notifyOnError: false,
|
|
6944
|
+
});
|
|
6945
|
+
if (!resp.success) {
|
|
6946
|
+
throw new Error(resp.message || 'Failed to load LinkedIn publications');
|
|
6947
|
+
}
|
|
6948
|
+
return Array.isArray(resp.shares) ? resp.shares : [];
|
|
6949
|
+
},
|
|
6950
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
6951
|
+
enabled: Boolean(configuration.libraryUrl) && Boolean(itemId) && enabled,
|
|
6952
|
+
});
|
|
6953
|
+
};
|
|
6954
|
+
/**
|
|
6955
|
+
* Post a public artifact to X through the Library's saga: the author's
|
|
6956
|
+
* text, then the artifact's attributed share-card link (X draws the card),
|
|
6957
|
+
* and optionally one picked result as an image.
|
|
6958
|
+
*/
|
|
6959
|
+
const usePublishXShare = () => {
|
|
6960
|
+
return useMutation({
|
|
6961
|
+
mutationFn: async ({ itemId, text, idempotencyKey, imageDataUrl, }) => {
|
|
6962
|
+
const resp = await requestDatalayer({
|
|
6963
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/x`,
|
|
6964
|
+
method: 'POST',
|
|
6965
|
+
headers: { 'Idempotency-Key': idempotencyKey },
|
|
6966
|
+
body: { text, imageDataUrl },
|
|
6967
|
+
});
|
|
6968
|
+
if (!resp.success) {
|
|
6969
|
+
throw new Error(resp.message || 'Failed to post to X');
|
|
6970
|
+
}
|
|
6971
|
+
return resp.share;
|
|
6972
|
+
},
|
|
6973
|
+
});
|
|
6974
|
+
};
|
|
6975
|
+
const useUnpublishXShare = () => {
|
|
6976
|
+
return useMutation({
|
|
6977
|
+
mutationFn: async (idempotencyKey) => {
|
|
6978
|
+
const resp = await requestDatalayer({
|
|
6979
|
+
url: `${configuration.libraryUrl}/api/library/v1/shares/x/${encodeURIComponent(idempotencyKey)}`,
|
|
6980
|
+
method: 'DELETE',
|
|
6981
|
+
});
|
|
6982
|
+
if (!resp.success) {
|
|
6983
|
+
throw new Error(resp.message || 'Failed to remove the X post');
|
|
6984
|
+
}
|
|
6985
|
+
return resp.share;
|
|
6986
|
+
},
|
|
6987
|
+
});
|
|
6988
|
+
};
|
|
6989
|
+
const useXShares = (itemId, enabled = true) => {
|
|
6990
|
+
return useQuery({
|
|
6991
|
+
queryKey: ['library', 'social-shares', 'x', itemId],
|
|
6992
|
+
queryFn: async () => {
|
|
6993
|
+
const resp = await requestDatalayer({
|
|
6994
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/x`,
|
|
6995
|
+
method: 'GET',
|
|
6996
|
+
notifyOnError: false,
|
|
6997
|
+
});
|
|
6998
|
+
if (!resp.success) {
|
|
6999
|
+
throw new Error(resp.message || 'Failed to load X posts');
|
|
7000
|
+
}
|
|
7001
|
+
return Array.isArray(resp.shares) ? resp.shares : [];
|
|
7002
|
+
},
|
|
7003
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
7004
|
+
enabled: Boolean(configuration.libraryUrl) && Boolean(itemId) && enabled,
|
|
7005
|
+
});
|
|
7006
|
+
};
|
|
7007
|
+
/** Publish one selected public result through the durable Bluesky saga. */
|
|
7008
|
+
const usePublishBlueskyShare = () => {
|
|
7009
|
+
return useMutation({
|
|
7010
|
+
mutationFn: async ({ itemId, finding, cellId, outputIndex, idempotencyKey, imageDataUrl, imageAltText, }) => {
|
|
7011
|
+
const resp = await requestDatalayer({
|
|
7012
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/bluesky`,
|
|
7013
|
+
method: 'POST',
|
|
7014
|
+
headers: { 'Idempotency-Key': idempotencyKey },
|
|
7015
|
+
body: { finding, cellId, outputIndex, imageDataUrl, imageAltText },
|
|
7016
|
+
});
|
|
7017
|
+
if (!resp.success) {
|
|
7018
|
+
throw new Error(resp.message || 'Failed to publish to Bluesky');
|
|
7019
|
+
}
|
|
7020
|
+
return resp.share;
|
|
7021
|
+
},
|
|
7022
|
+
});
|
|
7023
|
+
};
|
|
7024
|
+
const useUnpublishBlueskyShare = () => {
|
|
7025
|
+
return useMutation({
|
|
7026
|
+
mutationFn: async (idempotencyKey) => {
|
|
7027
|
+
const resp = await requestDatalayer({
|
|
7028
|
+
url: `${configuration.libraryUrl}/api/library/v1/shares/bluesky/${encodeURIComponent(idempotencyKey)}`,
|
|
7029
|
+
method: 'DELETE',
|
|
7030
|
+
});
|
|
7031
|
+
if (!resp.success) {
|
|
7032
|
+
throw new Error(resp.message || 'Failed to unpublish from Bluesky');
|
|
7033
|
+
}
|
|
7034
|
+
return resp.share;
|
|
7035
|
+
},
|
|
7036
|
+
});
|
|
7037
|
+
};
|
|
7038
|
+
const useBlueskyShares = (itemId, enabled = true) => {
|
|
7039
|
+
return useQuery({
|
|
7040
|
+
queryKey: ['library', 'social-shares', 'bluesky', itemId],
|
|
7041
|
+
queryFn: async () => {
|
|
7042
|
+
const resp = await requestDatalayer({
|
|
7043
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/shares/bluesky`,
|
|
7044
|
+
method: 'GET',
|
|
7045
|
+
notifyOnError: false,
|
|
7046
|
+
});
|
|
7047
|
+
if (!resp.success) {
|
|
7048
|
+
throw new Error(resp.message || 'Failed to load Bluesky publications');
|
|
7049
|
+
}
|
|
7050
|
+
return Array.isArray(resp.shares) ? resp.shares : [];
|
|
7051
|
+
},
|
|
7052
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
7053
|
+
enabled: Boolean(configuration.libraryUrl) && Boolean(itemId) && enabled,
|
|
7054
|
+
});
|
|
7055
|
+
};
|
|
6856
7056
|
/**
|
|
6857
7057
|
* Tell the library that an artifact was reused: cloned into a space, or
|
|
6858
7058
|
* instantiated as a runtime. The reuse itself belongs to the service that
|
|
@@ -6860,11 +7060,14 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6860
7060
|
*/
|
|
6861
7061
|
const useCountItemReuse = () => {
|
|
6862
7062
|
return useMutation({
|
|
6863
|
-
mutationFn: async ({ itemId, kind, }) => {
|
|
7063
|
+
mutationFn: async ({ itemId, kind, signupSource, shareRef, }) => {
|
|
6864
7064
|
const resp = await requestDatalayer({
|
|
6865
7065
|
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/reused`,
|
|
6866
7066
|
method: 'POST',
|
|
6867
|
-
body: {
|
|
7067
|
+
body: {
|
|
7068
|
+
kind,
|
|
7069
|
+
...(signupSource && shareRef ? { signupSource, shareRef } : {}),
|
|
7070
|
+
},
|
|
6868
7071
|
});
|
|
6869
7072
|
if (!resp.success) {
|
|
6870
7073
|
throw new Error(resp.message || 'Failed to count the reuse');
|
|
@@ -7062,215 +7265,113 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7062
7265
|
},
|
|
7063
7266
|
});
|
|
7064
7267
|
};
|
|
7065
|
-
/**
|
|
7066
|
-
* Get LinkedIn profile via proxy
|
|
7067
|
-
*/
|
|
7268
|
+
/** Get the server-connected LinkedIn profile. */
|
|
7068
7269
|
const useGetLinkedinProfile = () => {
|
|
7069
7270
|
return useMutation({
|
|
7070
|
-
mutationFn: async (
|
|
7271
|
+
mutationFn: async () => {
|
|
7071
7272
|
return requestDatalayer({
|
|
7072
|
-
url: `${configuration.iamUrl}/api/iam/v1/
|
|
7073
|
-
method: '
|
|
7074
|
-
body: {
|
|
7075
|
-
request_method: 'GET',
|
|
7076
|
-
request_url: 'https://api.linkedin.com/v2/userinfo',
|
|
7077
|
-
request_token: accessToken,
|
|
7078
|
-
},
|
|
7273
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/linkedin/profile`,
|
|
7274
|
+
method: 'GET',
|
|
7079
7275
|
});
|
|
7080
7276
|
},
|
|
7081
7277
|
});
|
|
7082
7278
|
};
|
|
7083
|
-
/**
|
|
7084
|
-
* Post LinkedIn share
|
|
7085
|
-
*/
|
|
7279
|
+
/** Publish a LinkedIn text post through the server-held connection. */
|
|
7086
7280
|
const usePostLinkedinShare = () => {
|
|
7087
7281
|
return useMutation({
|
|
7088
|
-
mutationFn: async ({
|
|
7089
|
-
const postShareRequest = {
|
|
7090
|
-
author: linkedinUserUrn,
|
|
7091
|
-
lifecycleState: 'PUBLISHED',
|
|
7092
|
-
specificContent: {
|
|
7093
|
-
'com.linkedin.ugc.ShareContent': {
|
|
7094
|
-
shareCommentary: { text: postText },
|
|
7095
|
-
shareMediaCategory: 'NONE',
|
|
7096
|
-
},
|
|
7097
|
-
},
|
|
7098
|
-
visibility: {
|
|
7099
|
-
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
|
|
7100
|
-
},
|
|
7101
|
-
};
|
|
7282
|
+
mutationFn: async ({ postText }) => {
|
|
7102
7283
|
return requestDatalayer({
|
|
7103
|
-
url: `${configuration.iamUrl}/api/iam/v1/
|
|
7284
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/linkedin/posts`,
|
|
7104
7285
|
method: 'POST',
|
|
7105
|
-
body: {
|
|
7106
|
-
request_method: 'POST',
|
|
7107
|
-
request_url: 'https://api.linkedin.com/v2/ugcPosts',
|
|
7108
|
-
request_token: accessToken,
|
|
7109
|
-
request_body: postShareRequest,
|
|
7110
|
-
},
|
|
7286
|
+
body: { commentary: postText },
|
|
7111
7287
|
});
|
|
7112
7288
|
},
|
|
7113
7289
|
});
|
|
7114
7290
|
};
|
|
7115
|
-
/**
|
|
7116
|
-
* Post LinkedIn share with image upload
|
|
7117
|
-
*/
|
|
7291
|
+
/** Publish a LinkedIn post, optionally with one uploaded image. */
|
|
7118
7292
|
const usePostLinkedinShareWithUpload = () => {
|
|
7119
7293
|
return useMutation({
|
|
7120
|
-
mutationFn: async ({
|
|
7121
|
-
let shareRequest;
|
|
7122
|
-
if (uploadObject) {
|
|
7123
|
-
// Step 1: Register upload
|
|
7124
|
-
const registerUploadRequest = {
|
|
7125
|
-
registerUploadRequest: {
|
|
7126
|
-
recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
|
|
7127
|
-
owner: linkedinUserUrn,
|
|
7128
|
-
serviceRelationships: [
|
|
7129
|
-
{
|
|
7130
|
-
relationshipType: 'OWNER',
|
|
7131
|
-
identifier: 'urn:li:userGeneratedContent',
|
|
7132
|
-
},
|
|
7133
|
-
],
|
|
7134
|
-
},
|
|
7135
|
-
};
|
|
7136
|
-
const registerResp = await requestDatalayer({
|
|
7137
|
-
url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
|
|
7138
|
-
method: 'POST',
|
|
7139
|
-
body: {
|
|
7140
|
-
request_method: 'POST',
|
|
7141
|
-
request_url: 'https://api.linkedin.com/v2/assets?action=registerUpload',
|
|
7142
|
-
request_token: accessToken,
|
|
7143
|
-
request_body: registerUploadRequest,
|
|
7144
|
-
},
|
|
7145
|
-
});
|
|
7146
|
-
const asset = registerResp.response.value.asset;
|
|
7147
|
-
const uploadUrl = registerResp.response.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl;
|
|
7148
|
-
// Step 2: Upload image
|
|
7149
|
-
await requestDatalayer({
|
|
7150
|
-
url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
|
|
7151
|
-
method: 'POST',
|
|
7152
|
-
body: {
|
|
7153
|
-
request_method: 'PUT',
|
|
7154
|
-
request_url: uploadUrl,
|
|
7155
|
-
request_token: accessToken,
|
|
7156
|
-
request_body: {
|
|
7157
|
-
uploadURL: uploadUrl,
|
|
7158
|
-
content: uploadObject,
|
|
7159
|
-
userURN: linkedinUserUrn,
|
|
7160
|
-
},
|
|
7161
|
-
},
|
|
7162
|
-
});
|
|
7163
|
-
// Step 3: Create share with media
|
|
7164
|
-
shareRequest = {
|
|
7165
|
-
author: linkedinUserUrn,
|
|
7166
|
-
lifecycleState: 'PUBLISHED',
|
|
7167
|
-
specificContent: {
|
|
7168
|
-
'com.linkedin.ugc.ShareContent': {
|
|
7169
|
-
shareCommentary: { text: postText },
|
|
7170
|
-
shareMediaCategory: 'IMAGE',
|
|
7171
|
-
media: [
|
|
7172
|
-
{
|
|
7173
|
-
status: 'READY',
|
|
7174
|
-
description: { text: 'Datalayer Notebook' },
|
|
7175
|
-
media: asset,
|
|
7176
|
-
title: { text: 'Datalayer Notebook' },
|
|
7177
|
-
},
|
|
7178
|
-
],
|
|
7179
|
-
},
|
|
7180
|
-
},
|
|
7181
|
-
visibility: {
|
|
7182
|
-
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
|
|
7183
|
-
},
|
|
7184
|
-
};
|
|
7185
|
-
}
|
|
7186
|
-
else {
|
|
7187
|
-
// Text-only share (no image upload)
|
|
7188
|
-
shareRequest = {
|
|
7189
|
-
author: linkedinUserUrn,
|
|
7190
|
-
lifecycleState: 'PUBLISHED',
|
|
7191
|
-
specificContent: {
|
|
7192
|
-
'com.linkedin.ugc.ShareContent': {
|
|
7193
|
-
shareCommentary: { text: postText },
|
|
7194
|
-
shareMediaCategory: 'NONE',
|
|
7195
|
-
},
|
|
7196
|
-
},
|
|
7197
|
-
visibility: {
|
|
7198
|
-
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
|
|
7199
|
-
},
|
|
7200
|
-
};
|
|
7201
|
-
}
|
|
7202
|
-
return requestDatalayer({
|
|
7203
|
-
url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
|
|
7204
|
-
method: 'POST',
|
|
7205
|
-
body: {
|
|
7206
|
-
request_method: 'POST',
|
|
7207
|
-
request_url: 'https://api.linkedin.com/v2/ugcPosts',
|
|
7208
|
-
request_token: accessToken,
|
|
7209
|
-
request_body: shareRequest,
|
|
7210
|
-
},
|
|
7211
|
-
});
|
|
7212
|
-
},
|
|
7213
|
-
});
|
|
7214
|
-
};
|
|
7215
|
-
// ============================================================================
|
|
7216
|
-
// Proxy Operations
|
|
7217
|
-
// ============================================================================
|
|
7218
|
-
/**
|
|
7219
|
-
* Proxy GET request
|
|
7220
|
-
*/
|
|
7221
|
-
const useProxyGET = () => {
|
|
7222
|
-
return useMutation({
|
|
7223
|
-
mutationFn: async ({ url, token }) => {
|
|
7294
|
+
mutationFn: async ({ postText, uploadObject, }) => {
|
|
7224
7295
|
return requestDatalayer({
|
|
7225
|
-
url: `${configuration.iamUrl}/api/iam/v1/
|
|
7296
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/linkedin/posts`,
|
|
7226
7297
|
method: 'POST',
|
|
7227
7298
|
body: {
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
request_token: token,
|
|
7231
|
-
},
|
|
7232
|
-
});
|
|
7233
|
-
},
|
|
7234
|
-
});
|
|
7235
|
-
};
|
|
7236
|
-
/**
|
|
7237
|
-
* Proxy POST request
|
|
7238
|
-
*/
|
|
7239
|
-
const useProxyPOST = () => {
|
|
7240
|
-
return useMutation({
|
|
7241
|
-
mutationFn: async ({ url, body, token, }) => {
|
|
7242
|
-
return requestDatalayer({
|
|
7243
|
-
url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
|
|
7244
|
-
method: 'POST',
|
|
7245
|
-
body: {
|
|
7246
|
-
request_method: 'POST',
|
|
7247
|
-
request_url: url,
|
|
7248
|
-
request_token: token,
|
|
7249
|
-
request_body: body,
|
|
7299
|
+
commentary: postText,
|
|
7300
|
+
image_data_url: uploadObject,
|
|
7250
7301
|
},
|
|
7251
7302
|
});
|
|
7252
7303
|
},
|
|
7253
7304
|
});
|
|
7254
7305
|
};
|
|
7306
|
+
/** Connect-only AT Protocol account operations. */
|
|
7307
|
+
const useGetBlueskyConnection = () => useMutation({
|
|
7308
|
+
mutationFn: async () => requestDatalayer({
|
|
7309
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/bluesky/profile`,
|
|
7310
|
+
method: 'GET',
|
|
7311
|
+
notifyOnError: false,
|
|
7312
|
+
}),
|
|
7313
|
+
});
|
|
7255
7314
|
/**
|
|
7256
|
-
*
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7315
|
+
* Begin a Bluesky connection.
|
|
7316
|
+
*
|
|
7317
|
+
* `returnOrigin` is where the reader should come back to — their own origin,
|
|
7318
|
+
* so a connection begun on a developer's machine does not end on
|
|
7319
|
+
* production. IAM takes the origin only and builds the address itself, so
|
|
7320
|
+
* this cannot ask to be sent anywhere.
|
|
7321
|
+
*/
|
|
7322
|
+
const useConnectBluesky = () => useMutation({
|
|
7323
|
+
mutationFn: async (asked) => {
|
|
7324
|
+
const { identifier, returnOrigin } = typeof asked === 'string'
|
|
7325
|
+
? { identifier: asked, returnOrigin: undefined }
|
|
7326
|
+
: asked;
|
|
7327
|
+
return requestDatalayer({
|
|
7328
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/bluesky/connect`,
|
|
7329
|
+
method: 'POST',
|
|
7330
|
+
body: returnOrigin
|
|
7331
|
+
? { identifier, return_origin: returnOrigin }
|
|
7332
|
+
: { identifier },
|
|
7333
|
+
});
|
|
7334
|
+
},
|
|
7335
|
+
});
|
|
7336
|
+
const useDisconnectBluesky = () => useMutation({
|
|
7337
|
+
mutationFn: async () => requestDatalayer({
|
|
7338
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/bluesky/connection`,
|
|
7339
|
+
method: 'DELETE',
|
|
7340
|
+
}),
|
|
7341
|
+
});
|
|
7342
|
+
/**
|
|
7343
|
+
* The X connection, as IAM reports it: whether X is configured on this
|
|
7344
|
+
* deployment, whether the caller connected it, and the account (no token).
|
|
7345
|
+
*/
|
|
7346
|
+
const useXConnection = (enabled = true) => useQuery({
|
|
7347
|
+
queryKey: ['iam', 'social', 'x', 'status'],
|
|
7348
|
+
queryFn: async () => requestDatalayer({
|
|
7349
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/x/status`,
|
|
7350
|
+
method: 'GET',
|
|
7351
|
+
notifyOnError: false,
|
|
7352
|
+
}),
|
|
7353
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
7354
|
+
enabled: Boolean(configuration.iamUrl) && enabled,
|
|
7355
|
+
});
|
|
7356
|
+
/**
|
|
7357
|
+
* Begin connecting X. Answers the start link to open at top level;
|
|
7358
|
+
* `returnOrigin` is where to come back to (an origin only: IAM builds the
|
|
7359
|
+
* path, so this cannot ask to be sent anywhere).
|
|
7360
|
+
*/
|
|
7361
|
+
const useConnectX = () => useMutation({
|
|
7362
|
+
mutationFn: async (returnOrigin) => requestDatalayer({
|
|
7363
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/x/connect`,
|
|
7364
|
+
method: 'POST',
|
|
7365
|
+
body: returnOrigin ? { return_origin: returnOrigin } : {},
|
|
7366
|
+
}),
|
|
7367
|
+
});
|
|
7368
|
+
const useDisconnectX = () => useMutation({
|
|
7369
|
+
mutationFn: async () => requestDatalayer({
|
|
7370
|
+
url: `${configuration.iamUrl}/api/iam/v1/social/x/connection`,
|
|
7371
|
+
method: 'DELETE',
|
|
7372
|
+
}),
|
|
7373
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['iam', 'social', 'x'] }),
|
|
7374
|
+
});
|
|
7274
7375
|
// ============================================================================
|
|
7275
7376
|
// Waiting List & Growth
|
|
7276
7377
|
// ============================================================================
|
|
@@ -8319,11 +8420,22 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
8319
8420
|
useGetLinkedinProfile,
|
|
8320
8421
|
usePostLinkedinShare,
|
|
8321
8422
|
usePostLinkedinShareWithUpload,
|
|
8423
|
+
useGetBlueskyConnection,
|
|
8424
|
+
useConnectBluesky,
|
|
8425
|
+
useDisconnectBluesky,
|
|
8426
|
+
useXConnection,
|
|
8427
|
+
useConnectX,
|
|
8428
|
+
useDisconnectX,
|
|
8429
|
+
usePublishXShare,
|
|
8430
|
+
useUnpublishXShare,
|
|
8431
|
+
useXShares,
|
|
8432
|
+
usePublishLinkedInShare,
|
|
8433
|
+
useUnpublishLinkedInShare,
|
|
8434
|
+
useLinkedInShares,
|
|
8435
|
+
usePublishBlueskyShare,
|
|
8436
|
+
useUnpublishBlueskyShare,
|
|
8437
|
+
useBlueskyShares,
|
|
8322
8438
|
useRegisterToWaitingList,
|
|
8323
|
-
// Proxy
|
|
8324
|
-
useProxyGET,
|
|
8325
|
-
useProxyPOST,
|
|
8326
|
-
useProxyPUT,
|
|
8327
8439
|
// Users
|
|
8328
8440
|
useUser,
|
|
8329
8441
|
useUserByHandle,
|
package/lib/hooks/useContents.js
CHANGED
|
@@ -156,6 +156,9 @@ export const useContentSources = (filters = {}) => {
|
|
|
156
156
|
queryFn: () => listSources(token ?? '', filters, contentsUrl),
|
|
157
157
|
enabled: Boolean(token && contentsUrl),
|
|
158
158
|
staleTime: 30_000,
|
|
159
|
+
// Coming back to a list is when somebody looks for what they just made:
|
|
160
|
+
// it is read again, whatever made it.
|
|
161
|
+
refetchOnMount: 'always',
|
|
159
162
|
refetchOnWindowFocus: true,
|
|
160
163
|
});
|
|
161
164
|
};
|
package/lib/hooks/useMcp.js
CHANGED
|
@@ -34,7 +34,7 @@ import { deleteMcpPolicy, getAdmittedClients, getMcpPolicy, setMcpPolicy, } from
|
|
|
34
34
|
import { createServiceAgent, listServiceAgents, revokeServiceAgent, rotateServiceAgentKey, } from '../api/iam/serviceAgents.js';
|
|
35
35
|
import { useCoreStore, useIAMStore } from '../state/index.js';
|
|
36
36
|
import { queryKeys } from './useCache.js';
|
|
37
|
-
const useMcpServerUrl = () => useCoreStore(state => state.configuration.
|
|
37
|
+
const useMcpServerUrl = () => useCoreStore(state => state.configuration.datalayerMcpServerUrl);
|
|
38
38
|
const useIamUrl = () => useCoreStore(state => state.configuration.iamUrl);
|
|
39
39
|
const useOtelUrl = () => useCoreStore(state => state.configuration.otelUrl);
|
|
40
40
|
// -- Tasks -----------------------------------------------------------------
|