@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.
Files changed (47) hide show
  1. package/README.md +5 -5
  2. package/lib/api/iam/authentication.js +6 -4
  3. package/lib/api/iam/healthz.js +2 -2
  4. package/lib/api/iam/oauth2.js +18 -12
  5. package/lib/api/iam/secrets.js +1 -2
  6. package/lib/api/mcp/observability.d.ts +9 -2
  7. package/lib/api/mcp/observability.js +40 -15
  8. package/lib/client/auth/strategies.js +1 -1
  9. package/lib/client/mixins/IAMMixin.js +2 -2
  10. package/lib/components/checkout/StripeCheckout.d.ts +1 -1
  11. package/lib/components/checkout/StripeCheckout.js +1 -1
  12. package/lib/components/display/VisuallyHidden.d.ts +1 -2
  13. package/lib/components/sharing/sandboxSharing.js +7 -1
  14. package/lib/config/Configuration.d.ts +4 -4
  15. package/lib/config/Configuration.js +7 -7
  16. package/lib/config/index.d.ts +1 -0
  17. package/lib/config/index.js +1 -0
  18. package/lib/config/planes.d.ts +45 -0
  19. package/lib/config/planes.js +53 -0
  20. package/lib/hooks/useBackdrop.d.ts +4 -5
  21. package/lib/hooks/useCache.d.ts +52 -19
  22. package/lib/hooks/useCache.js +310 -198
  23. package/lib/hooks/useContents.js +3 -0
  24. package/lib/hooks/useMcp.js +1 -1
  25. package/lib/hooks/useNavigate.js +10 -0
  26. package/lib/hooks/useWindowSize.js +1 -1
  27. package/lib/models/IAMProvidersSpecs.d.ts +10 -5
  28. package/lib/models/IAMProvidersSpecs.js +0 -23
  29. package/lib/models/URN.d.ts +37 -0
  30. package/lib/models/URN.js +59 -1
  31. package/lib/navigation/adapters/react-router.d.ts +1 -1
  32. package/lib/navigation/adapters/react-router.js +3 -0
  33. package/lib/state/sessionEnd.js +1 -1
  34. package/lib/state/substates/CoreState.js +3 -16
  35. package/lib/state/substates/IAMState.d.ts +3 -3
  36. package/lib/state/substates/NavigationState.d.ts +92 -0
  37. package/lib/state/substates/NavigationState.js +304 -0
  38. package/lib/state/substates/index.d.ts +1 -0
  39. package/lib/state/substates/index.js +1 -0
  40. package/lib/utils/Handles.js +1 -1
  41. package/lib/utils/Screencapture.d.ts +15 -0
  42. package/lib/utils/Screencapture.js +26 -23
  43. package/lib/views/mcp/McpDashboard.js +15 -10
  44. package/lib/views/mcp/McpHome.js +13 -6
  45. package/lib/views/mcp/types.d.ts +4 -0
  46. package/package.json +9 -7
  47. package/scripts/generate-mcp-types.py +15 -9
@@ -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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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
- ...DEFAULT_QUERY_OPTIONS,
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: { kind },
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 (accessToken) => {
7271
+ mutationFn: async () => {
7071
7272
  return requestDatalayer({
7072
- url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
7073
- method: 'POST',
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 ({ linkedinUserUrn, postText, accessToken, }) => {
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/proxy/request`,
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 ({ linkedinUserUrn, postText, uploadObject, accessToken, }) => {
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/proxy/request`,
7296
+ url: `${configuration.iamUrl}/api/iam/v1/social/linkedin/posts`,
7226
7297
  method: 'POST',
7227
7298
  body: {
7228
- request_method: 'GET',
7229
- request_url: url,
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
- * Proxy PUT request
7257
- */
7258
- const useProxyPUT = () => {
7259
- return useMutation({
7260
- mutationFn: async ({ url, body, token, }) => {
7261
- return requestDatalayer({
7262
- url: `${configuration.iamUrl}/api/iam/v1/proxy/request`,
7263
- method: 'POST',
7264
- body: {
7265
- request_method: 'PUT',
7266
- request_url: url,
7267
- request_token: token,
7268
- request_body: body,
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,
@@ -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
  };
@@ -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.jupyterMcpServerUrl);
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 -----------------------------------------------------------------