@aglyn/plugins-data 1.0.0-beta.243 → 1.0.0-beta.246
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-data",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.246",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,15 +25,15 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
34
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
35
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
36
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.246",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.246",
|
|
30
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.246",
|
|
31
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.246",
|
|
32
|
+
"@aglyn/shared-util-http": "1.0.0-beta.246",
|
|
33
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.246",
|
|
34
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.246",
|
|
35
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.246",
|
|
36
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.246",
|
|
37
37
|
"@swc/helpers": "0.5.23"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
@@ -15,4 +15,10 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { PluginRecordIndex } from '@aglyn/aglyn/plugin-manager/plugin-record-index';
|
|
18
|
+
/** What `facts.recordPage` says of one dataset's record pages on a site. */
|
|
19
|
+
export interface DatasetIndexedRecordPage {
|
|
20
|
+
screenId: string;
|
|
21
|
+
base: string;
|
|
22
|
+
slugField: string;
|
|
23
|
+
}
|
|
18
24
|
export declare const datasetRecordIndex: PluginRecordIndex;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
1
2
|
/**
|
|
2
3
|
* @license
|
|
3
4
|
* Copyright 2026 Aglyn LLC
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
|
|
18
19
|
import { scopedToHost } from "@aglyn/tenant-data-admin/server/organizations";
|
|
19
20
|
import { datasetIndexedRecord } from "../model/dataset-record.js";
|
|
21
|
+
import { DATASET_RECORD_PAGES_COLLECTION, RECORD_PAGES_MAX_PER_SITE, parseRecordPageBinding } from "../record-pages/record-pages.js";
|
|
20
22
|
/**
|
|
21
23
|
* The data plugin's `dataset` index (AGL-3080): the workspace's datasets, as
|
|
22
24
|
* another surface reads them — an AI job drafting an automation that names a
|
|
@@ -30,9 +32,41 @@ import { datasetIndexedRecord } from "../model/dataset-record.js";
|
|
|
30
32
|
*
|
|
31
33
|
* Each record is `datasetIndexedRecord`'s (`model/dataset-record.ts`): the
|
|
32
34
|
* name, the fields and the scope tokens, never a record of the dataset.
|
|
35
|
+
*
|
|
36
|
+
* Narrowed to a site, each also says where its records have pages of their
|
|
37
|
+
* own on THAT site (AGL-3747), as `facts.recordPage`: the template screen,
|
|
38
|
+
* the base and the address field of the site's record template for it
|
|
39
|
+
* (AGL-3475), `null` where the site keeps none. A planner reads it to link a list's cards to
|
|
40
|
+
* record pages made before its plan; one small read of the site's record
|
|
41
|
+
* templates, bounded by `RECORD_PAGES_MAX_PER_SITE`, per answer.
|
|
33
42
|
*/ function datasets(orgId) {
|
|
34
43
|
return firebaseAdmin.app().firestore().collection('orgs').doc(orgId).collection('datasets');
|
|
35
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The site's record templates by the dataset they render, the first in the
|
|
47
|
+
* site's order where a dataset has more than one.
|
|
48
|
+
*/ async function siteRecordPages(hostId) {
|
|
49
|
+
const snapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection(DATASET_RECORD_PAGES_COLLECTION).limit(RECORD_PAGES_MAX_PER_SITE).get();
|
|
50
|
+
const pages = new Map();
|
|
51
|
+
for (const doc of snapshot.docs){
|
|
52
|
+
const binding = parseRecordPageBinding(doc.id, doc.data());
|
|
53
|
+
if (!binding || pages.has(binding.datasetId)) continue;
|
|
54
|
+
pages.set(binding.datasetId, {
|
|
55
|
+
screenId: binding.screenId,
|
|
56
|
+
base: binding.base,
|
|
57
|
+
slugField: binding.slugField
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return pages;
|
|
61
|
+
}
|
|
62
|
+
/** A record with its record page on the site, where one is kept. */ function withRecordPage(record, pages) {
|
|
63
|
+
var _pages_get;
|
|
64
|
+
return _extends({}, record, {
|
|
65
|
+
facts: _extends({}, record.facts, {
|
|
66
|
+
recordPage: (_pages_get = pages.get(record.id)) != null ? _pages_get : null
|
|
67
|
+
})
|
|
68
|
+
});
|
|
69
|
+
}
|
|
36
70
|
export const datasetRecordIndex = {
|
|
37
71
|
async list ({ orgId, hostId, limit }) {
|
|
38
72
|
if (!orgId || limit <= 0) return {
|
|
@@ -44,9 +78,14 @@ export const datasetRecordIndex = {
|
|
|
44
78
|
// One more than asked, so `truncated` is a fact rather than a guess;
|
|
45
79
|
// deleted datasets are filtered after the read.
|
|
46
80
|
const snapshot = await query.limit(limit + 1).get();
|
|
47
|
-
const records = snapshot.docs.map((doc)=>datasetIndexedRecord(doc.id, doc.data())).filter((record)=>record !== null);
|
|
81
|
+
const records = snapshot.docs.map((doc)=>datasetIndexedRecord(doc.id, doc.data())).filter((record)=>record !== null).slice(0, limit);
|
|
82
|
+
if (!hostId || !records.length) return {
|
|
83
|
+
records,
|
|
84
|
+
truncated: snapshot.size > limit
|
|
85
|
+
};
|
|
86
|
+
const pages = await siteRecordPages(hostId);
|
|
48
87
|
return {
|
|
49
|
-
records: records.
|
|
88
|
+
records: records.map((record)=>withRecordPage(record, pages)),
|
|
50
89
|
truncated: snapshot.size > limit
|
|
51
90
|
};
|
|
52
91
|
},
|
|
@@ -55,7 +94,8 @@ export const datasetRecordIndex = {
|
|
|
55
94
|
const snapshot = await datasets(orgId).doc(id).get();
|
|
56
95
|
const record = snapshot.exists ? datasetIndexedRecord(snapshot.id, snapshot.data()) : null;
|
|
57
96
|
if (!record || !hostId) return record;
|
|
58
|
-
|
|
97
|
+
if (!visibleToHost(record.facts['visibleTo'], hostId)) return null;
|
|
98
|
+
return withRecordPage(record, await siteRecordPages(hostId));
|
|
59
99
|
}
|
|
60
100
|
};
|
|
61
101
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/server/dataset-record-index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport type {\n PluginIndexedRecord,\n PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { scopedToHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { datasetIndexedRecord } from '../model/dataset-record'\n\n/**\n * The data plugin's `dataset` index (AGL-3080): the workspace's datasets, as\n * another surface reads them — an AI job drafting an automation that names a\n * dataset, a planner listing what a site already has — without knowing where\n * they are stored or what a deleted one looks like.\n *\n * Datasets belong to the ORGANIZATION (`orgId` required; a scope with none\n * answers nothing). Narrowed to a site (`hostId`), only the datasets shared\n * with it are answered — the site Data page's rule. A deleted dataset is left\n * out of every answer.\n *\n * Each record is `datasetIndexedRecord`'s (`model/dataset-record.ts`): the\n * name, the fields and the scope tokens, never a record of the dataset.\n */\n\nfunction datasets(orgId: string) {\n return firebaseAdmin.app().firestore().collection('orgs').doc(orgId).collection('datasets')\n}\n\nexport const datasetRecordIndex: PluginRecordIndex = {\n async list({ orgId, hostId, limit }) {\n if (!orgId || limit <= 0) return { records: [], truncated: false }\n const collection = datasets(orgId)\n const query = hostId ? scopedToHost(collection, hostId) : collection\n // One more than asked, so `truncated` is a fact rather than a guess;\n // deleted datasets are filtered after the read.\n const snapshot = await query.limit(limit + 1).get()\n const records = snapshot.docs\n .map((doc) => datasetIndexedRecord(doc.id, doc.data()))\n .filter((record): record is PluginIndexedRecord => record !== null)\n return { records: records.
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/data/src/lib/server/dataset-record-index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport type {\n PluginIndexedRecord,\n PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { scopedToHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { datasetIndexedRecord } from '../model/dataset-record'\nimport {\n DATASET_RECORD_PAGES_COLLECTION,\n type DatasetRecordPageBinding,\n RECORD_PAGES_MAX_PER_SITE,\n parseRecordPageBinding,\n} from '../record-pages/record-pages'\n\n/**\n * The data plugin's `dataset` index (AGL-3080): the workspace's datasets, as\n * another surface reads them — an AI job drafting an automation that names a\n * dataset, a planner listing what a site already has — without knowing where\n * they are stored or what a deleted one looks like.\n *\n * Datasets belong to the ORGANIZATION (`orgId` required; a scope with none\n * answers nothing). Narrowed to a site (`hostId`), only the datasets shared\n * with it are answered — the site Data page's rule. A deleted dataset is left\n * out of every answer.\n *\n * Each record is `datasetIndexedRecord`'s (`model/dataset-record.ts`): the\n * name, the fields and the scope tokens, never a record of the dataset.\n *\n * Narrowed to a site, each also says where its records have pages of their\n * own on THAT site (AGL-3747), as `facts.recordPage`: the template screen,\n * the base and the address field of the site's record template for it\n * (AGL-3475), `null` where the site keeps none. A planner reads it to link a list's cards to\n * record pages made before its plan; one small read of the site's record\n * templates, bounded by `RECORD_PAGES_MAX_PER_SITE`, per answer.\n */\n\nfunction datasets(orgId: string) {\n return firebaseAdmin.app().firestore().collection('orgs').doc(orgId).collection('datasets')\n}\n\n/** What `facts.recordPage` says of one dataset's record pages on a site. */\nexport interface DatasetIndexedRecordPage {\n screenId: string\n base: string\n slugField: string\n}\n\n/**\n * The site's record templates by the dataset they render, the first in the\n * site's order where a dataset has more than one.\n */\nasync function siteRecordPages(hostId: string): Promise<Map<string, DatasetIndexedRecordPage>> {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection(DATASET_RECORD_PAGES_COLLECTION)\n .limit(RECORD_PAGES_MAX_PER_SITE)\n .get()\n const pages = new Map<string, DatasetIndexedRecordPage>()\n for (const doc of snapshot.docs) {\n const binding: DatasetRecordPageBinding | null = parseRecordPageBinding(doc.id, doc.data())\n if (!binding || pages.has(binding.datasetId)) continue\n pages.set(binding.datasetId, { screenId: binding.screenId, base: binding.base, slugField: binding.slugField })\n }\n return pages\n}\n\n/** A record with its record page on the site, where one is kept. */\nfunction withRecordPage(\n record: PluginIndexedRecord,\n pages: ReadonlyMap<string, DatasetIndexedRecordPage>,\n): PluginIndexedRecord {\n return { ...record, facts: { ...record.facts, recordPage: pages.get(record.id) ?? null } }\n}\n\nexport const datasetRecordIndex: PluginRecordIndex = {\n async list({ orgId, hostId, limit }) {\n if (!orgId || limit <= 0) return { records: [], truncated: false }\n const collection = datasets(orgId)\n const query = hostId ? scopedToHost(collection, hostId) : collection\n // One more than asked, so `truncated` is a fact rather than a guess;\n // deleted datasets are filtered after the read.\n const snapshot = await query.limit(limit + 1).get()\n const records = snapshot.docs\n .map((doc) => datasetIndexedRecord(doc.id, doc.data()))\n .filter((record): record is PluginIndexedRecord => record !== null)\n .slice(0, limit)\n if (!hostId || !records.length) return { records, truncated: snapshot.size > limit }\n const pages = await siteRecordPages(hostId)\n return { records: records.map((record) => withRecordPage(record, pages)), truncated: snapshot.size > limit }\n },\n async get({ orgId, hostId, id }) {\n if (!orgId || !id) return null\n const snapshot = await datasets(orgId).doc(id).get()\n const record = snapshot.exists ? datasetIndexedRecord(snapshot.id, snapshot.data()) : null\n if (!record || !hostId) return record\n if (!visibleToHost(record.facts['visibleTo'] as string[], hostId)) return null\n return withRecordPage(record, await siteRecordPages(hostId))\n },\n}\n"],"names":["visibleToHost","firebaseAdmin","scopedToHost","datasetIndexedRecord","DATASET_RECORD_PAGES_COLLECTION","RECORD_PAGES_MAX_PER_SITE","parseRecordPageBinding","datasets","orgId","app","firestore","collection","doc","siteRecordPages","hostId","snapshot","limit","get","pages","Map","docs","binding","id","data","has","datasetId","set","screenId","base","slugField","withRecordPage","record","facts","recordPage","datasetRecordIndex","list","records","truncated","query","map","filter","slice","length","size","exists"],"mappings":"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"}
|