@fias/arche-sdk 2.30.0 → 2.32.0
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/entity-ops.d.ts +10 -2
- package/dist/entity-ops.d.ts.map +1 -1
- package/dist/entity-ops.js +11 -3
- package/dist/entity-ops.js.map +1 -1
- package/dist/generated/permissions.d.ts +1 -1
- package/dist/generated/permissions.d.ts.map +1 -1
- package/dist/generated/permissions.js +6 -0
- package/dist/generated/permissions.js.map +1 -1
- package/dist/hooks.d.ts +61 -4
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +197 -18
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.test.js +185 -1
- package/dist/hooks.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -11
- package/dist/types.d.ts +279 -22
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/default/AGENTS.md +142 -31
- package/templates/default/CLAUDE.md +142 -31
package/dist/hooks.js
CHANGED
|
@@ -16,6 +16,7 @@ exports.useSurface = useSurface;
|
|
|
16
16
|
exports.useFiasNavigation = useFiasNavigation;
|
|
17
17
|
exports.usePersistentState = usePersistentState;
|
|
18
18
|
exports.useFiasChrome = useFiasChrome;
|
|
19
|
+
exports.useOpener = useOpener;
|
|
19
20
|
exports.useStepNavigation = useStepNavigation;
|
|
20
21
|
exports.useFiasDataStore = useFiasDataStore;
|
|
21
22
|
exports.useFiasWorkspaces = useFiasWorkspaces;
|
|
@@ -34,6 +35,7 @@ exports.useDataSubscription = useDataSubscription;
|
|
|
34
35
|
exports.useVaultUserDocuments = useVaultUserDocuments;
|
|
35
36
|
exports.useCreativeLibrary = useCreativeLibrary;
|
|
36
37
|
exports.useFiasConnections = useFiasConnections;
|
|
38
|
+
exports.useFiasWebSources = useFiasWebSources;
|
|
37
39
|
exports.useFiasX = useFiasX;
|
|
38
40
|
exports.useFiasSchedules = useFiasSchedules;
|
|
39
41
|
exports.useFiasLinkedIn = useFiasLinkedIn;
|
|
@@ -107,6 +109,8 @@ function createDebouncedWriter(bridge) {
|
|
|
107
109
|
}
|
|
108
110
|
return { schedule, flush };
|
|
109
111
|
}
|
|
112
|
+
/** The platform's `resolve_people` limit per call; `resolvePeople` pages above it. */
|
|
113
|
+
const RESOLVE_PEOPLE_PAGE = 100;
|
|
110
114
|
/**
|
|
111
115
|
* Access the current user's profile information.
|
|
112
116
|
* Requires the `user:profile:read` permission in fias-plugin.json.
|
|
@@ -438,6 +442,20 @@ function useImageEntities(filter) {
|
|
|
438
442
|
* run, so we use the same 2-minute ceiling as the image/audio surfaces.
|
|
439
443
|
*/
|
|
440
444
|
const CLIENT_ENTITY_INVOKE_TIMEOUT_MS = 120000;
|
|
445
|
+
/**
|
|
446
|
+
* Whether a `client_entity_invoke` failed because the host has no executor
|
|
447
|
+
* for the entity (an older Fias, or the dev harness): the capability is simply
|
|
448
|
+
* not available there. Hosts answer `CLIENT_ENTITY_UNAVAILABLE`; older ones
|
|
449
|
+
* only said so in the message ("no client-side executor …"), which is still
|
|
450
|
+
* recognised. Any other bridge error is the app's to fix.
|
|
451
|
+
*/
|
|
452
|
+
function isClientEntityUnavailable(err) {
|
|
453
|
+
if (!(err instanceof bridge_1.FiasBridgeError))
|
|
454
|
+
return false;
|
|
455
|
+
if (err.code === 'CLIENT_ENTITY_UNAVAILABLE')
|
|
456
|
+
return true;
|
|
457
|
+
return err.code === 'BRIDGE_ERROR' && err.message.includes('no client-side executor');
|
|
458
|
+
}
|
|
441
459
|
/**
|
|
442
460
|
* Invoke a client-side ("on-device") entity through the bridge's generic
|
|
443
461
|
* `client_entity_invoke` op. Client-side entities execute entirely in the
|
|
@@ -757,6 +775,7 @@ const INERT_CHROME = {
|
|
|
757
775
|
sidebarCollapsed: false,
|
|
758
776
|
hostExpandedBar: true,
|
|
759
777
|
hostExpandButton: true,
|
|
778
|
+
opener: null,
|
|
760
779
|
};
|
|
761
780
|
/**
|
|
762
781
|
* The host chrome around your arche — Fias's standard expand/contract (the
|
|
@@ -819,6 +838,7 @@ function useFiasChrome() {
|
|
|
819
838
|
const openAssistant = (0, react_1.useCallback)(() => bridge.openAssistant(), [bridge]);
|
|
820
839
|
return (0, react_1.useMemo)(() => ({
|
|
821
840
|
...(chrome ?? INERT_CHROME),
|
|
841
|
+
opener: chrome?.opener ?? null,
|
|
822
842
|
supported: supported && chrome !== null,
|
|
823
843
|
setFocusMode,
|
|
824
844
|
setSidebarCollapsed,
|
|
@@ -837,6 +857,72 @@ function useFiasChrome() {
|
|
|
837
857
|
openAssistant,
|
|
838
858
|
]);
|
|
839
859
|
}
|
|
860
|
+
/** The person answers the send sheet; it waits on them, like the picker. */
|
|
861
|
+
const SEND_TO_OPENER_TIMEOUT_MS = 30 * 60 * 1000;
|
|
862
|
+
/**
|
|
863
|
+
* Where the person came from, when a Fias tool opened your arche — the forge
|
|
864
|
+
* page's "Open FiasWorks Studio", the Arche Builder's (ADR-037 D8) — and the
|
|
865
|
+
* two things you can do about it:
|
|
866
|
+
*
|
|
867
|
+
* - `returnTo()` takes the person back there.
|
|
868
|
+
* - `send(file)` offers a Vault file YOUR arche created ("Use in {name}"):
|
|
869
|
+
* Fias shows the person a sheet naming the file and the destination, and
|
|
870
|
+
* their Send moves it — into a forge's `public/`, or an Arche Builder
|
|
871
|
+
* arche's asset library. Requires `opener:send`.
|
|
872
|
+
*
|
|
873
|
+
* `opener` is null when nothing opened your arche; show neither then. Reading
|
|
874
|
+
* it does not take the host chrome over (unlike `useFiasChrome`).
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* const { opener, send, returnTo } = useOpener();
|
|
878
|
+
* {opener && <button onClick={returnTo}>Return to {opener.name}</button>}
|
|
879
|
+
* {opener && <button onClick={() => send({ fileId, name: 'pumpkin.png' })}>
|
|
880
|
+
* Use in {opener.name}</button>}
|
|
881
|
+
*/
|
|
882
|
+
function useOpener() {
|
|
883
|
+
const bridge = useBridge();
|
|
884
|
+
const [opener, setOpener] = (0, react_1.useState)(bridge.getChrome()?.opener ?? null);
|
|
885
|
+
(0, react_1.useEffect)(() => {
|
|
886
|
+
let cancelled = false;
|
|
887
|
+
bridge.waitForInit().then(() => {
|
|
888
|
+
if (!cancelled)
|
|
889
|
+
setOpener(bridge.getChrome()?.opener ?? null);
|
|
890
|
+
});
|
|
891
|
+
const unsubscribe = bridge.onChromeUpdate((next) => setOpener(next?.opener ?? null));
|
|
892
|
+
return () => {
|
|
893
|
+
cancelled = true;
|
|
894
|
+
unsubscribe();
|
|
895
|
+
};
|
|
896
|
+
}, [bridge]);
|
|
897
|
+
const returnTo = (0, react_1.useCallback)(() => {
|
|
898
|
+
if (bridge.getChrome()?.opener)
|
|
899
|
+
bridge.navigateHost('opener');
|
|
900
|
+
}, [bridge]);
|
|
901
|
+
const send = (0, react_1.useCallback)(async (file) => {
|
|
902
|
+
if (!bridge.getChrome()?.opener) {
|
|
903
|
+
throw new bridge_1.FiasBridgeError('Nothing opened this arche, so there is nowhere to send', 'NO_OPENER');
|
|
904
|
+
}
|
|
905
|
+
let answer;
|
|
906
|
+
try {
|
|
907
|
+
answer = await bridge.request('client_entity_invoke', {
|
|
908
|
+
entityId: entity_ops_1.SEND_TO_OPENER_ENTITY_ID,
|
|
909
|
+
input: { fileId: file.fileId, ...(file.name ? { name: file.name } : {}) },
|
|
910
|
+
}, SEND_TO_OPENER_TIMEOUT_MS);
|
|
911
|
+
}
|
|
912
|
+
catch (err) {
|
|
913
|
+
// A host without it (an older Fias, the dev harness).
|
|
914
|
+
if (isClientEntityUnavailable(err)) {
|
|
915
|
+
throw new bridge_1.FiasBridgeError('Sending to another arche is not available here', 'SEND_UNAVAILABLE');
|
|
916
|
+
}
|
|
917
|
+
throw err;
|
|
918
|
+
}
|
|
919
|
+
if (answer && 'error' in answer) {
|
|
920
|
+
throw new bridge_1.FiasBridgeError(answer.error.message, answer.error.code);
|
|
921
|
+
}
|
|
922
|
+
return answer;
|
|
923
|
+
}, [bridge]);
|
|
924
|
+
return (0, react_1.useMemo)(() => ({ opener, returnTo, send }), [opener, returnTo, send]);
|
|
925
|
+
}
|
|
840
926
|
/**
|
|
841
927
|
* Manage step navigation for multi-node plugins.
|
|
842
928
|
* Listens for step_navigate messages from the host to support
|
|
@@ -1074,6 +1160,17 @@ function useFiasDataStore() {
|
|
|
1074
1160
|
batch,
|
|
1075
1161
|
]);
|
|
1076
1162
|
}
|
|
1163
|
+
/** A member target on the wire: a typed username, or the app's ref (ADR-040 §4). */
|
|
1164
|
+
function memberTargetPayload(member) {
|
|
1165
|
+
if (typeof member === 'string')
|
|
1166
|
+
return { username: member };
|
|
1167
|
+
if (member && typeof member === 'object' && typeof member.ref === 'string') {
|
|
1168
|
+
return { ref: member.ref };
|
|
1169
|
+
}
|
|
1170
|
+
// Most often `m.username` from a roster: apps created after app user refs
|
|
1171
|
+
// get `ref` there instead (ADR-040 §4).
|
|
1172
|
+
throw new Error('useFiasWorkspaces: name the member by { ref } from listMembers() (or a username a person typed)');
|
|
1173
|
+
}
|
|
1077
1174
|
/**
|
|
1078
1175
|
* Access workspace tenancy — team/org workspaces with role-gated membership
|
|
1079
1176
|
* (owner/admin/member/viewer) over `workspace`-scoped Data Store collections
|
|
@@ -1087,7 +1184,8 @@ function useFiasDataStore() {
|
|
|
1087
1184
|
* @example
|
|
1088
1185
|
* const workspaces = useFiasWorkspaces();
|
|
1089
1186
|
* const ws = await workspaces.create('Acme Corp');
|
|
1090
|
-
*
|
|
1187
|
+
* // a person picks who to add, in a sheet Fias draws (ADR-040):
|
|
1188
|
+
* const { added } = await workspaces.addPeople(ws.workspaceId);
|
|
1091
1189
|
* // or, inside a Fias org workspace, its members' shared space:
|
|
1092
1190
|
* const shared = await workspaces.openForWorkspace();
|
|
1093
1191
|
* // then, with useFiasDataStore():
|
|
@@ -1118,10 +1216,41 @@ function useFiasWorkspaces() {
|
|
|
1118
1216
|
const archive = (0, react_1.useCallback)(async (workspaceId) => {
|
|
1119
1217
|
await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'archive', { workspaceId });
|
|
1120
1218
|
}, [bridge]);
|
|
1219
|
+
const resolvePeople = (0, react_1.useCallback)(async (refs, workspaceId) => {
|
|
1220
|
+
// Any length, in order: nothing to ask for an empty list, and the
|
|
1221
|
+
// platform's 100-per-call limit is the SDK's to page, not the app's.
|
|
1222
|
+
const people = [];
|
|
1223
|
+
for (let i = 0; i < refs.length; i += RESOLVE_PEOPLE_PAGE) {
|
|
1224
|
+
const page = refs.slice(i, i + RESOLVE_PEOPLE_PAGE);
|
|
1225
|
+
const res = await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'resolve_people', workspaceId === undefined ? { refs: page } : { refs: page, workspaceId });
|
|
1226
|
+
people.push(...res.people);
|
|
1227
|
+
}
|
|
1228
|
+
return people;
|
|
1229
|
+
}, [bridge]);
|
|
1121
1230
|
const listMembers = (0, react_1.useCallback)(async (workspaceId) => {
|
|
1122
1231
|
const res = await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'list_members', { workspaceId });
|
|
1123
1232
|
return res.members;
|
|
1124
1233
|
}, [bridge]);
|
|
1234
|
+
const addPeople = (0, react_1.useCallback)(async (workspaceId, options) => {
|
|
1235
|
+
let answer;
|
|
1236
|
+
try {
|
|
1237
|
+
answer = await bridge.request('client_entity_invoke', {
|
|
1238
|
+
entityId: entity_ops_1.WORKSPACE_ADD_PEOPLE_ENTITY_ID,
|
|
1239
|
+
input: options?.role ? { workspaceId, role: options.role } : { workspaceId },
|
|
1240
|
+
}, ADD_PEOPLE_SHEET_TIMEOUT_MS);
|
|
1241
|
+
}
|
|
1242
|
+
catch (err) {
|
|
1243
|
+
// A host without the sheet (an older Fias or dev harness) has no
|
|
1244
|
+
// executor for it: the sheet is simply not available there.
|
|
1245
|
+
if (isClientEntityUnavailable(err)) {
|
|
1246
|
+
throw new bridge_1.FiasBridgeError('Adding people is not available here', 'ADD_PEOPLE_UNAVAILABLE');
|
|
1247
|
+
}
|
|
1248
|
+
throw err;
|
|
1249
|
+
}
|
|
1250
|
+
if ('error' in answer)
|
|
1251
|
+
throw new bridge_1.FiasBridgeError(answer.error.message, answer.error.code);
|
|
1252
|
+
return { added: answer.added };
|
|
1253
|
+
}, [bridge]);
|
|
1125
1254
|
const addMember = (0, react_1.useCallback)(async (workspaceId, username, role, expiresAt) => {
|
|
1126
1255
|
const res = await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'add_member', {
|
|
1127
1256
|
workspaceId,
|
|
@@ -1131,14 +1260,14 @@ function useFiasWorkspaces() {
|
|
|
1131
1260
|
});
|
|
1132
1261
|
return res.member;
|
|
1133
1262
|
}, [bridge]);
|
|
1134
|
-
const updateMember = (0, react_1.useCallback)(async (workspaceId,
|
|
1135
|
-
const res = await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'update_member', { workspaceId,
|
|
1263
|
+
const updateMember = (0, react_1.useCallback)(async (workspaceId, member, role) => {
|
|
1264
|
+
const res = await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'update_member', { workspaceId, ...memberTargetPayload(member), role });
|
|
1136
1265
|
return res.member;
|
|
1137
1266
|
}, [bridge]);
|
|
1138
|
-
const removeMember = (0, react_1.useCallback)(async (workspaceId,
|
|
1267
|
+
const removeMember = (0, react_1.useCallback)(async (workspaceId, member) => {
|
|
1139
1268
|
await (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.PLUGIN_WORKSPACES_ENTITY_ID, 'remove_member', {
|
|
1140
1269
|
workspaceId,
|
|
1141
|
-
|
|
1270
|
+
...memberTargetPayload(member),
|
|
1142
1271
|
});
|
|
1143
1272
|
}, [bridge]);
|
|
1144
1273
|
return (0, react_1.useMemo)(() => ({
|
|
@@ -1148,9 +1277,11 @@ function useFiasWorkspaces() {
|
|
|
1148
1277
|
get,
|
|
1149
1278
|
archive,
|
|
1150
1279
|
listMembers,
|
|
1280
|
+
addPeople,
|
|
1151
1281
|
addMember,
|
|
1152
1282
|
updateMember,
|
|
1153
1283
|
removeMember,
|
|
1284
|
+
resolvePeople,
|
|
1154
1285
|
}), [
|
|
1155
1286
|
create,
|
|
1156
1287
|
openForWorkspace,
|
|
@@ -1158,9 +1289,11 @@ function useFiasWorkspaces() {
|
|
|
1158
1289
|
get,
|
|
1159
1290
|
archive,
|
|
1160
1291
|
listMembers,
|
|
1292
|
+
addPeople,
|
|
1161
1293
|
addMember,
|
|
1162
1294
|
updateMember,
|
|
1163
1295
|
removeMember,
|
|
1296
|
+
resolvePeople,
|
|
1164
1297
|
]);
|
|
1165
1298
|
}
|
|
1166
1299
|
/** The refusals that mean "no access here" — the workspaces `get` verb's two answers. */
|
|
@@ -1245,13 +1378,10 @@ function useFiasAccess(workspaceId) {
|
|
|
1245
1378
|
}
|
|
1246
1379
|
catch (err) {
|
|
1247
1380
|
// A host without the sheet (an older Fias, or an older dev harness) has
|
|
1248
|
-
// no executor for it
|
|
1249
|
-
//
|
|
1250
|
-
//
|
|
1251
|
-
|
|
1252
|
-
if (err instanceof bridge_1.FiasBridgeError &&
|
|
1253
|
-
err.code === 'BRIDGE_ERROR' &&
|
|
1254
|
-
err.message.includes('no client-side executor')) {
|
|
1381
|
+
// no executor for it: the sheet is simply not available. Any other
|
|
1382
|
+
// bridge error (a missing permission, a call loop) is the app's to fix,
|
|
1383
|
+
// so it passes through unchanged.
|
|
1384
|
+
if (isClientEntityUnavailable(err)) {
|
|
1255
1385
|
throw new bridge_1.FiasBridgeError('Managing access is not available here', 'ACCESS_UNAVAILABLE');
|
|
1256
1386
|
}
|
|
1257
1387
|
throw err;
|
|
@@ -1295,6 +1425,8 @@ const lastAccessAnswers = new Map();
|
|
|
1295
1425
|
const FOCUS_REFETCH_MIN_MS = 30000;
|
|
1296
1426
|
/** How long a person may keep the access sheet open before the call gives up. */
|
|
1297
1427
|
const ACCESS_SHEET_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1428
|
+
/** The Add people sheet waits on a person, like the access sheet. */
|
|
1429
|
+
const ADD_PEOPLE_SHEET_TIMEOUT_MS = ACCESS_SHEET_TIMEOUT_MS;
|
|
1298
1430
|
const sharedAccessByBridge = new WeakMap();
|
|
1299
1431
|
function peekSharedAccess(bridge, workspaceId) {
|
|
1300
1432
|
return sharedAccessByBridge.get(bridge)?.get(workspaceId);
|
|
@@ -2958,7 +3090,7 @@ async function fetchCommunityAssetUrls(bridge, assetIds) {
|
|
|
2958
3090
|
return hits;
|
|
2959
3091
|
}
|
|
2960
3092
|
/**
|
|
2961
|
-
* The user's Creative Library (ADR-
|
|
3093
|
+
* The user's Creative Library (ADR-037): their styles and characters, made in
|
|
2962
3094
|
* FiasWorks Studio or any arche they allow, owned by them — or, in a team
|
|
2963
3095
|
* workspace that approved this arche, the workspace's. Requires
|
|
2964
3096
|
* `vault:creative:read`; `create` / `update` / `remove` / `restore` also need
|
|
@@ -2998,9 +3130,7 @@ function useCreativeLibrary() {
|
|
|
2998
3130
|
catch (err) {
|
|
2999
3131
|
// A host without the picker (an older Fias, the dev harness) has no
|
|
3000
3132
|
// executor for it: the picker is simply not available there.
|
|
3001
|
-
if (err
|
|
3002
|
-
err.code === 'BRIDGE_ERROR' &&
|
|
3003
|
-
err.message.includes('no client-side executor')) {
|
|
3133
|
+
if (isClientEntityUnavailable(err)) {
|
|
3004
3134
|
throw new bridge_1.FiasBridgeError('The Creative Library picker is not available here', 'PICKER_UNAVAILABLE');
|
|
3005
3135
|
}
|
|
3006
3136
|
throw err;
|
|
@@ -3047,6 +3177,54 @@ function useFiasConnections() {
|
|
|
3047
3177
|
}, [bridge]);
|
|
3048
3178
|
return (0, react_1.useMemo)(() => ({ list, connect, release }), [list, connect, release]);
|
|
3049
3179
|
}
|
|
3180
|
+
/**
|
|
3181
|
+
* Pages on websites Fias approved, read as DATA (ADR-038). Requires
|
|
3182
|
+
* `web:sources:read`.
|
|
3183
|
+
*
|
|
3184
|
+
* The app names a page on an approved site and a form — the fields it wants —
|
|
3185
|
+
* and gets back values of those kinds (numbers, dates, yes/no, the app's own
|
|
3186
|
+
* choices, lists of them), each `null` when the page does not say. It never
|
|
3187
|
+
* receives the page's text, not even a short label. One copy of each page serves every app, fetched at most
|
|
3188
|
+
* once an hour; a fresh fetch and its reading are charged to the user (well
|
|
3189
|
+
* under a cent for a typical page), and a cached result is free.
|
|
3190
|
+
*
|
|
3191
|
+
* @example
|
|
3192
|
+
* const web = useFiasWebSources();
|
|
3193
|
+
* const result = await web.extract<{
|
|
3194
|
+
* rates: Array<{ termMonths: number | null; ira: boolean | null; apy: number | null }>;
|
|
3195
|
+
* }>({
|
|
3196
|
+
* url: 'https://www.becu.org/everyday-banking/certificates',
|
|
3197
|
+
* form: {
|
|
3198
|
+
* description: 'Share certificate rates',
|
|
3199
|
+
* fields: {
|
|
3200
|
+
* rates: {
|
|
3201
|
+
* kind: 'list',
|
|
3202
|
+
* item: {
|
|
3203
|
+
* termMonths: { kind: 'integer', min: 1, max: 120, description: 'Term in months' },
|
|
3204
|
+
* ira: { kind: 'boolean', description: 'An IRA certificate' },
|
|
3205
|
+
* apy: { kind: 'number', min: 0, max: 25, description: 'APY in percent' },
|
|
3206
|
+
* },
|
|
3207
|
+
* },
|
|
3208
|
+
* },
|
|
3209
|
+
* },
|
|
3210
|
+
* });
|
|
3211
|
+
* // Show result.site.name and result.fetchedAt beside the figures.
|
|
3212
|
+
*/
|
|
3213
|
+
function useFiasWebSources() {
|
|
3214
|
+
const bridge = useBridge();
|
|
3215
|
+
const listSites = (0, react_1.useCallback)(async (options) => (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.WEB_SOURCES_ENTITY_ID, 'list_sites', {
|
|
3216
|
+
...(options?.category !== undefined ? { category: options.category } : {}),
|
|
3217
|
+
...(options?.search !== undefined ? { search: options.search } : {}),
|
|
3218
|
+
...(options?.limit !== undefined ? { limit: options.limit } : {}),
|
|
3219
|
+
...(options?.offset !== undefined ? { offset: options.offset } : {}),
|
|
3220
|
+
}), [bridge]);
|
|
3221
|
+
const extract = (0, react_1.useCallback)(async (request) => (0, entity_ops_1.invokeEntityOp)(bridge, entity_ops_1.WEB_SOURCES_ENTITY_ID, 'extract', {
|
|
3222
|
+
url: request.url,
|
|
3223
|
+
form: request.form,
|
|
3224
|
+
...(request.maxAgeSeconds !== undefined ? { maxAgeSeconds: request.maxAgeSeconds } : {}),
|
|
3225
|
+
}), [bridge]);
|
|
3226
|
+
return (0, react_1.useMemo)(() => ({ listSites, extract }), [listSites, extract]);
|
|
3227
|
+
}
|
|
3050
3228
|
/**
|
|
3051
3229
|
* X, as an account the workspace this app is open in let it use (ADR-029 §3).
|
|
3052
3230
|
*
|
|
@@ -3151,7 +3329,8 @@ function useFiasLinkedIn() {
|
|
|
3151
3329
|
* The From address is always the Company's — you only choose recipients and
|
|
3152
3330
|
* content.
|
|
3153
3331
|
*
|
|
3154
|
-
* Name members by
|
|
3332
|
+
* Name members by their ref (`useFiasUser().ref`, or refs you stored); you
|
|
3333
|
+
* never see their addresses. Every email carries
|
|
3155
3334
|
* a one-click unsubscribe, and people who used it are skipped (`muted`).
|
|
3156
3335
|
*
|
|
3157
3336
|
* @example
|
|
@@ -3159,7 +3338,7 @@ function useFiasLinkedIn() {
|
|
|
3159
3338
|
* const email = useCompanyEmail();
|
|
3160
3339
|
* const result = await email.send({
|
|
3161
3340
|
* requestKey: `digest-${weekId}`,
|
|
3162
|
-
* to:
|
|
3341
|
+
* to: assigneeRefs.map((ref) => ({ ref })),
|
|
3163
3342
|
* subject: 'Your weekly digest',
|
|
3164
3343
|
* text: digestText,
|
|
3165
3344
|
* html: digestHtml,
|