@ai-matrx/records 0.58.117 → 0.58.118
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/CHANGELOG.md +8 -0
- package/dist/core/index.cjs +98 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +55 -1
- package/dist/core/index.d.ts +55 -1
- package/dist/core/index.js +98 -5
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +98 -5
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +55 -1
- package/dist/react/index.d.ts +55 -1
- package/dist/react/index.js +98 -5
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/index.d.cts
CHANGED
|
@@ -102,6 +102,25 @@ declare function intersectIds(sets: Uuid[][]): Uuid[];
|
|
|
102
102
|
*/
|
|
103
103
|
declare const A_PERSONS_REFUSAL: ReadonlySet<RecordsErrorCode>;
|
|
104
104
|
declare function isAPersonsRefusal(code: RecordsErrorCode | string): boolean;
|
|
105
|
+
/** One organization the client could not use, with the sentence that says why. */
|
|
106
|
+
interface OrganizationUnavailable {
|
|
107
|
+
organization_id: Uuid;
|
|
108
|
+
reason: string;
|
|
109
|
+
}
|
|
110
|
+
/** `organizationsOpen`: the organizations whose store answers, and those that do not (with why). */
|
|
111
|
+
interface OrganizationsOpenAnswer {
|
|
112
|
+
open: Uuid[];
|
|
113
|
+
unavailable: OrganizationUnavailable[];
|
|
114
|
+
}
|
|
115
|
+
/** `metadataSearch`: every match, each carrying the organization it came from. */
|
|
116
|
+
interface MetadataSearchAnswer {
|
|
117
|
+
matches: Array<Record<string, unknown> & {
|
|
118
|
+
organization_id: Uuid | null;
|
|
119
|
+
}>;
|
|
120
|
+
/** Organizations asked in order (the ones that answered). */
|
|
121
|
+
organizations_covered: Uuid[];
|
|
122
|
+
unavailable: OrganizationUnavailable[];
|
|
123
|
+
}
|
|
105
124
|
interface RecordsClient {
|
|
106
125
|
/** The config this client was built with, so a screen can show who it is acting as. */
|
|
107
126
|
readonly config: RecordsConfig;
|
|
@@ -1167,6 +1186,13 @@ interface RecordsClient {
|
|
|
1167
1186
|
*/
|
|
1168
1187
|
recordAggregate(args: {
|
|
1169
1188
|
table_id: Uuid;
|
|
1189
|
+
/**
|
|
1190
|
+
* OPTIONAL FILTER (active-org law): with `config.organizationIds` set, the table is looked
|
|
1191
|
+
* up across all the person's organizations and the aggregate runs in the one that OWNS it
|
|
1192
|
+
* (each row then carries that `organization_id`); this narrows the lookup to one. Absent and
|
|
1193
|
+
* no `organizationIds` ⇒ this client's own organization, exactly as before.
|
|
1194
|
+
*/
|
|
1195
|
+
organization_id?: Uuid | null;
|
|
1170
1196
|
/** Field keys whose values become the groups. */
|
|
1171
1197
|
groupBy?: string[];
|
|
1172
1198
|
/** `count` needs no key; every other operation reads one Field. */
|
|
@@ -1516,9 +1542,37 @@ interface RecordsClient {
|
|
|
1516
1542
|
record_id: Uuid;
|
|
1517
1543
|
organization_id?: Uuid | null;
|
|
1518
1544
|
}): Promise<RecordsResult<ReadValue[]>>;
|
|
1545
|
+
/**
|
|
1546
|
+
* Metadata search across EVERY organization the person belongs to (`config.organizationIds`;
|
|
1547
|
+
* `organization_id` narrows to one). Each match carries its own `organization_id`; an
|
|
1548
|
+
* organization whose store is off or refuses is listed in `unavailable` WITH its reason and
|
|
1549
|
+
* never hides the rest. Only when NO organization answers is the whole search a failure (the
|
|
1550
|
+
* first refusal). The door itself is not in the store's catalogue yet, so today every
|
|
1551
|
+
* organization refuses in the door-absent sentence — named, never faked.
|
|
1552
|
+
*/
|
|
1519
1553
|
metadataSearch(args: {
|
|
1520
1554
|
text: string;
|
|
1521
|
-
|
|
1555
|
+
organization_id?: Uuid | null;
|
|
1556
|
+
}): Promise<RecordsResult<MetadataSearchAnswer>>;
|
|
1557
|
+
/**
|
|
1558
|
+
* WHICH of the person's organizations can this client use right now? Asks every organization
|
|
1559
|
+
* in scope (`organization_id` narrows to one) whether its record store is open, so a host can
|
|
1560
|
+
* say "organization X is unavailable: <reason>" instead of silently dropping it. Organization
|
|
1561
|
+
* order is the person's own, never completion order.
|
|
1562
|
+
*/
|
|
1563
|
+
organizationsOpen(args?: {
|
|
1564
|
+
organization_id?: Uuid | null;
|
|
1565
|
+
}): Promise<RecordsResult<OrganizationsOpenAnswer>>;
|
|
1566
|
+
/**
|
|
1567
|
+
* The organization that OWNS a table or record — the one a write to it must run in. A
|
|
1568
|
+
* `table_id` is looked up across all the person's organizations; a `record_id` is opened in its
|
|
1569
|
+
* own. `null` = found in none of them. Never a permission: the store's doors still decide.
|
|
1570
|
+
*/
|
|
1571
|
+
ownerOrganization(args: {
|
|
1572
|
+
table_id?: Uuid | null;
|
|
1573
|
+
record_id?: Uuid | null;
|
|
1574
|
+
organization_id?: Uuid | null;
|
|
1575
|
+
}): Promise<RecordsResult<Uuid | null>>;
|
|
1522
1576
|
/**
|
|
1523
1577
|
* Mint an embed token. `mode` is `read` OR `write` and never both — the store
|
|
1524
1578
|
* refuses a token carrying two decisions. The secret comes back ONCE, because
|
package/dist/core/index.d.ts
CHANGED
|
@@ -102,6 +102,25 @@ declare function intersectIds(sets: Uuid[][]): Uuid[];
|
|
|
102
102
|
*/
|
|
103
103
|
declare const A_PERSONS_REFUSAL: ReadonlySet<RecordsErrorCode>;
|
|
104
104
|
declare function isAPersonsRefusal(code: RecordsErrorCode | string): boolean;
|
|
105
|
+
/** One organization the client could not use, with the sentence that says why. */
|
|
106
|
+
interface OrganizationUnavailable {
|
|
107
|
+
organization_id: Uuid;
|
|
108
|
+
reason: string;
|
|
109
|
+
}
|
|
110
|
+
/** `organizationsOpen`: the organizations whose store answers, and those that do not (with why). */
|
|
111
|
+
interface OrganizationsOpenAnswer {
|
|
112
|
+
open: Uuid[];
|
|
113
|
+
unavailable: OrganizationUnavailable[];
|
|
114
|
+
}
|
|
115
|
+
/** `metadataSearch`: every match, each carrying the organization it came from. */
|
|
116
|
+
interface MetadataSearchAnswer {
|
|
117
|
+
matches: Array<Record<string, unknown> & {
|
|
118
|
+
organization_id: Uuid | null;
|
|
119
|
+
}>;
|
|
120
|
+
/** Organizations asked in order (the ones that answered). */
|
|
121
|
+
organizations_covered: Uuid[];
|
|
122
|
+
unavailable: OrganizationUnavailable[];
|
|
123
|
+
}
|
|
105
124
|
interface RecordsClient {
|
|
106
125
|
/** The config this client was built with, so a screen can show who it is acting as. */
|
|
107
126
|
readonly config: RecordsConfig;
|
|
@@ -1167,6 +1186,13 @@ interface RecordsClient {
|
|
|
1167
1186
|
*/
|
|
1168
1187
|
recordAggregate(args: {
|
|
1169
1188
|
table_id: Uuid;
|
|
1189
|
+
/**
|
|
1190
|
+
* OPTIONAL FILTER (active-org law): with `config.organizationIds` set, the table is looked
|
|
1191
|
+
* up across all the person's organizations and the aggregate runs in the one that OWNS it
|
|
1192
|
+
* (each row then carries that `organization_id`); this narrows the lookup to one. Absent and
|
|
1193
|
+
* no `organizationIds` ⇒ this client's own organization, exactly as before.
|
|
1194
|
+
*/
|
|
1195
|
+
organization_id?: Uuid | null;
|
|
1170
1196
|
/** Field keys whose values become the groups. */
|
|
1171
1197
|
groupBy?: string[];
|
|
1172
1198
|
/** `count` needs no key; every other operation reads one Field. */
|
|
@@ -1516,9 +1542,37 @@ interface RecordsClient {
|
|
|
1516
1542
|
record_id: Uuid;
|
|
1517
1543
|
organization_id?: Uuid | null;
|
|
1518
1544
|
}): Promise<RecordsResult<ReadValue[]>>;
|
|
1545
|
+
/**
|
|
1546
|
+
* Metadata search across EVERY organization the person belongs to (`config.organizationIds`;
|
|
1547
|
+
* `organization_id` narrows to one). Each match carries its own `organization_id`; an
|
|
1548
|
+
* organization whose store is off or refuses is listed in `unavailable` WITH its reason and
|
|
1549
|
+
* never hides the rest. Only when NO organization answers is the whole search a failure (the
|
|
1550
|
+
* first refusal). The door itself is not in the store's catalogue yet, so today every
|
|
1551
|
+
* organization refuses in the door-absent sentence — named, never faked.
|
|
1552
|
+
*/
|
|
1519
1553
|
metadataSearch(args: {
|
|
1520
1554
|
text: string;
|
|
1521
|
-
|
|
1555
|
+
organization_id?: Uuid | null;
|
|
1556
|
+
}): Promise<RecordsResult<MetadataSearchAnswer>>;
|
|
1557
|
+
/**
|
|
1558
|
+
* WHICH of the person's organizations can this client use right now? Asks every organization
|
|
1559
|
+
* in scope (`organization_id` narrows to one) whether its record store is open, so a host can
|
|
1560
|
+
* say "organization X is unavailable: <reason>" instead of silently dropping it. Organization
|
|
1561
|
+
* order is the person's own, never completion order.
|
|
1562
|
+
*/
|
|
1563
|
+
organizationsOpen(args?: {
|
|
1564
|
+
organization_id?: Uuid | null;
|
|
1565
|
+
}): Promise<RecordsResult<OrganizationsOpenAnswer>>;
|
|
1566
|
+
/**
|
|
1567
|
+
* The organization that OWNS a table or record — the one a write to it must run in. A
|
|
1568
|
+
* `table_id` is looked up across all the person's organizations; a `record_id` is opened in its
|
|
1569
|
+
* own. `null` = found in none of them. Never a permission: the store's doors still decide.
|
|
1570
|
+
*/
|
|
1571
|
+
ownerOrganization(args: {
|
|
1572
|
+
table_id?: Uuid | null;
|
|
1573
|
+
record_id?: Uuid | null;
|
|
1574
|
+
organization_id?: Uuid | null;
|
|
1575
|
+
}): Promise<RecordsResult<Uuid | null>>;
|
|
1522
1576
|
/**
|
|
1523
1577
|
* Mint an embed token. `mode` is `read` OR `write` and never both — the store
|
|
1524
1578
|
* refuses a token carrying two decisions. The secret comes back ONCE, because
|
package/dist/core/index.js
CHANGED
|
@@ -40784,6 +40784,13 @@ function createRecordsClient(config) {
|
|
|
40784
40784
|
}
|
|
40785
40785
|
return last;
|
|
40786
40786
|
}
|
|
40787
|
+
const spans = (filter) => Boolean(filter) || (config.organizationIds?.length ?? 0) > 0;
|
|
40788
|
+
const metadataSearchDoor = () => {
|
|
40789
|
+
const refusal = doorAbsent(DOORS.metadataSearch);
|
|
40790
|
+
scream({ code: refusal.code, message: refusal.message, ...refusal.hint ? { hint: refusal.hint } : {}, where: "metadataSearch" });
|
|
40791
|
+
return err(refusal);
|
|
40792
|
+
};
|
|
40793
|
+
const unavailableReason = (error) => `${error.message}${error.hint ? ` ${error.hint}` : ""}`;
|
|
40787
40794
|
const client = {
|
|
40788
40795
|
config,
|
|
40789
40796
|
async recordWrite({ table_id, data }) {
|
|
@@ -42113,7 +42120,23 @@ function createRecordsClient(config) {
|
|
|
42113
42120
|
"dashboardDelete"
|
|
42114
42121
|
);
|
|
42115
42122
|
},
|
|
42116
|
-
async recordAggregate({ table_id, groupBy, measures, bucket, filter, limit, compare }) {
|
|
42123
|
+
async recordAggregate({ table_id, organization_id, groupBy, measures, bucket, filter, limit, compare }) {
|
|
42124
|
+
if (spans(organization_id)) {
|
|
42125
|
+
const owner = await client.ownerOrganization({ table_id, ...organization_id ? { organization_id } : {} });
|
|
42126
|
+
const target = owner.ok && owner.data ? owner.data : organization_id ?? org;
|
|
42127
|
+
if (target !== org) {
|
|
42128
|
+
const answered = await clientFor(target).recordAggregate({
|
|
42129
|
+
table_id,
|
|
42130
|
+
...groupBy ? { groupBy } : {},
|
|
42131
|
+
...measures ? { measures } : {},
|
|
42132
|
+
...bucket !== void 0 ? { bucket } : {},
|
|
42133
|
+
...filter ? { filter } : {},
|
|
42134
|
+
...limit !== void 0 ? { limit } : {},
|
|
42135
|
+
...compare !== void 0 ? { compare } : {}
|
|
42136
|
+
});
|
|
42137
|
+
return answered.ok ? ok(answered.data.map((row) => ({ ...row, organization_id: target }))) : answered;
|
|
42138
|
+
}
|
|
42139
|
+
}
|
|
42117
42140
|
return callDoor(
|
|
42118
42141
|
DOORS.recordAggregate,
|
|
42119
42142
|
{
|
|
@@ -43029,10 +43052,80 @@ function createRecordsClient(config) {
|
|
|
43029
43052
|
"ruleDeclare"
|
|
43030
43053
|
);
|
|
43031
43054
|
},
|
|
43032
|
-
async metadataSearch() {
|
|
43033
|
-
|
|
43034
|
-
|
|
43035
|
-
|
|
43055
|
+
async metadataSearch({ text, organization_id }) {
|
|
43056
|
+
if (spans(organization_id) && readOrgs(organization_id).length > 0) {
|
|
43057
|
+
const targets = readOrgs(organization_id).filter((t) => t !== null);
|
|
43058
|
+
const unavailable = [];
|
|
43059
|
+
const answers = await Promise.all(
|
|
43060
|
+
targets.map(async (t) => {
|
|
43061
|
+
const sub = clientFor(t);
|
|
43062
|
+
const open = await sub.storeIsOpen();
|
|
43063
|
+
if (!open.ok) {
|
|
43064
|
+
unavailable.push({ organization_id: t, reason: unavailableReason(open.error) });
|
|
43065
|
+
return null;
|
|
43066
|
+
}
|
|
43067
|
+
if (!open.data) {
|
|
43068
|
+
unavailable.push({ organization_id: t, reason: "The custom data store is switched off for this organization." });
|
|
43069
|
+
return null;
|
|
43070
|
+
}
|
|
43071
|
+
const found = metadataSearchDoor();
|
|
43072
|
+
if (!found.ok) {
|
|
43073
|
+
unavailable.push({ organization_id: t, reason: unavailableReason(found.error) });
|
|
43074
|
+
return { t, found };
|
|
43075
|
+
}
|
|
43076
|
+
return { t, found };
|
|
43077
|
+
})
|
|
43078
|
+
);
|
|
43079
|
+
const matches = [];
|
|
43080
|
+
const covered = [];
|
|
43081
|
+
let firstRefusal = null;
|
|
43082
|
+
for (const answer of answers) {
|
|
43083
|
+
if (!answer) continue;
|
|
43084
|
+
if (!answer.found.ok) {
|
|
43085
|
+
firstRefusal ??= answer.found.error;
|
|
43086
|
+
continue;
|
|
43087
|
+
}
|
|
43088
|
+
covered.push(answer.t);
|
|
43089
|
+
for (const m of answer.found.data.matches) matches.push({ ...m, organization_id: answer.t });
|
|
43090
|
+
}
|
|
43091
|
+
const inOrder = (a, b) => targets.indexOf(a.organization_id) - targets.indexOf(b.organization_id);
|
|
43092
|
+
unavailable.sort(inOrder);
|
|
43093
|
+
if (covered.length === 0 && firstRefusal) return err(firstRefusal);
|
|
43094
|
+
return ok({ matches, organizations_covered: covered, unavailable });
|
|
43095
|
+
}
|
|
43096
|
+
return metadataSearchDoor();
|
|
43097
|
+
},
|
|
43098
|
+
async organizationsOpen(args) {
|
|
43099
|
+
const targets = readOrgs(args?.organization_id).filter((t) => t !== null);
|
|
43100
|
+
const open = [];
|
|
43101
|
+
const unavailable = [];
|
|
43102
|
+
await Promise.all(
|
|
43103
|
+
targets.map(async (t) => {
|
|
43104
|
+
const result = await clientFor(t).storeIsOpen();
|
|
43105
|
+
if (!result.ok) unavailable.push({ organization_id: t, reason: unavailableReason(result.error) });
|
|
43106
|
+
else if (!result.data)
|
|
43107
|
+
unavailable.push({ organization_id: t, reason: "The custom data store is switched off for this organization." });
|
|
43108
|
+
else open.push(t);
|
|
43109
|
+
})
|
|
43110
|
+
);
|
|
43111
|
+
const position = (id) => targets.indexOf(id);
|
|
43112
|
+
open.sort((a, b) => position(a) - position(b));
|
|
43113
|
+
unavailable.sort((a, b) => position(a.organization_id) - position(b.organization_id));
|
|
43114
|
+
return ok({ open, unavailable });
|
|
43115
|
+
},
|
|
43116
|
+
async ownerOrganization({ table_id, record_id, organization_id }) {
|
|
43117
|
+
if (table_id) {
|
|
43118
|
+
const read = await client.tableRead({ table_id, ...organization_id ? { organization_id } : {} });
|
|
43119
|
+
if (!read.ok) return err(read.error);
|
|
43120
|
+
return ok(read.data ? read.data.organization_id ?? null : null);
|
|
43121
|
+
}
|
|
43122
|
+
if (record_id) {
|
|
43123
|
+
const read = await client.recordRead({ record_id, ...organization_id ? { organization_id } : {} });
|
|
43124
|
+
if (!read.ok) return ok(null);
|
|
43125
|
+
const targets = readOrgs(organization_id);
|
|
43126
|
+
return ok(recordOrgs.get(record_id) ?? organization_id ?? (targets.length === 1 ? targets[0] ?? null : null));
|
|
43127
|
+
}
|
|
43128
|
+
return ok(null);
|
|
43036
43129
|
},
|
|
43037
43130
|
async fieldPropose() {
|
|
43038
43131
|
const refusal = doorAbsent(DOORS.fieldPropose);
|