@aglyn/plugins-ai 1.0.0-beta.219 → 1.0.0-beta.220
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 +18 -18
- package/src/lib/billing/ai-month-meter.d.ts +51 -0
- package/src/lib/billing/ai-month-meter.js +89 -0
- package/src/lib/billing/ai-month-meter.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +11 -33
- package/src/lib/billing/ai-overage-close.js +4 -3
- package/src/lib/billing/ai-overage-close.js.map +1 -1
- package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
- package/src/lib/billing/ai-overage-cutover.js +1 -28
- package/src/lib/billing/ai-overage-cutover.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +1 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/billing/ai-overage-standing.js +1 -1
- package/src/lib/billing/ai-overage-standing.js.map +1 -1
- package/src/lib/components/ai-allotment-editor.component.js +1 -1
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
- package/src/lib/components/ai-assist-provider.component.js +3 -2
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +63 -0
- package/src/lib/components/ai-automation-zones.js +31 -0
- package/src/lib/components/ai-automation-zones.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +6 -3
- package/src/lib/components/ai-describe-button.component.js +4 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +1 -1
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/constants.d.ts +5 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/constants.js.map +1 -1
- package/src/lib/declarations.js +2 -0
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +24 -10
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
- package/src/lib/insights/ai-dataset-catalog.js +57 -0
- package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +6 -39
- package/src/lib/insights/ai-figure-readers.js +2 -391
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +10 -1
- package/src/lib/insights/ai-insight-digest.js +16 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +1 -1
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
- package/src/lib/jobs/ai-job-component-step.js +23 -6
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +11 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-email-step.d.ts +5 -1
- package/src/lib/jobs/ai-job-email-step.js +10 -1
- package/src/lib/jobs/ai-job-email-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
- package/src/lib/jobs/ai-job-form-step.js +60 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +21 -0
- package/src/lib/jobs/ai-job-generation.js +53 -1
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
- package/src/lib/jobs/ai-job-layout-step.js +45 -12
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +7 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
- package/src/lib/jobs/ai-job-plan-creations.js +199 -0
- package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +8 -2
- package/src/lib/jobs/ai-job-plan-step.js +20 -6
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
- package/src/lib/jobs/ai-job-site-step.js +6 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
- package/src/lib/jobs/ai-job-template-step.js +72 -15
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +1 -1
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +5 -4
- package/src/lib/jobs/ai-workflow-records.js +26 -22
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-allotments.d.ts +16 -3
- package/src/lib/model/ai-allotments.js +34 -11
- package/src/lib/model/ai-allotments.js.map +1 -1
- package/src/lib/model/ai-job-creations.d.ts +55 -0
- package/src/lib/model/ai-job-creations.js +85 -0
- package/src/lib/model/ai-job-creations.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +3 -2
- package/src/lib/model/ai-page-job.js +4 -3
- package/src/lib/model/ai-page-job.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +4 -2
- package/src/lib/model/ai-site-job.js +8 -4
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +10 -0
- package/src/lib/model/ai-template-subjects.js +14 -0
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/model/ai-tokens.js +1 -1
- package/src/lib/model/ai-tokens.js.map +1 -1
- package/src/lib/model/ai-usage-by-user.js +1 -1
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/providers/catalog.js +1 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.js +1 -1
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/runtime/ai-device-audit.d.ts +1 -1
- package/src/lib/runtime/ai-device-audit.js +1 -1
- package/src/lib/runtime/ai-device-audit.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +1 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-gate.js +1 -1
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.js +1 -1
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +28 -22
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-org.js +1 -1
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +1 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-allotments.js +1 -1
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-assist.js +2 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +1 -1
- package/src/lib/server/assist-chat.js +2 -2
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/billing-credits.d.ts +5 -0
- package/src/lib/server/billing-credits.js +8 -2
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +2 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
- package/src/lib/testing/stand-in-dataset-index.js +80 -0
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
- package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
- package/src/lib/usage/ai-allotment-alerts.js +31 -14
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
- package/src/lib/usage/ai-quota-checks.d.ts +37 -0
- package/src/lib/usage/ai-quota-checks.js +69 -0
- package/src/lib/usage/ai-quota-checks.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.js +22 -10
- package/src/lib/usage/ai-usage-alerts.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.js +1 -1
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
- package/src/lib/usage/assist-ceiling.d.ts +2 -2
- package/src/lib/usage/assist-ceiling.js +4 -4
- package/src/lib/usage/assist-ceiling.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +494 -0
- package/src/lib/usage/assist-credits.js +524 -0
- package/src/lib/usage/assist-credits.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +1 -1
- package/src/lib/usage/assist-free-taste.js +1 -1
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-signal-mining.js +1 -1
- package/src/lib/usage/assist-signal-mining.js.map +1 -1
- package/src/lib/usage/assist-spend-guards.d.ts +153 -0
- package/src/lib/usage/assist-spend-guards.js +182 -0
- package/src/lib/usage/assist-spend-guards.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +1 -1
- package/src/lib/usage/assist-usage.js +1 -1
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.js +1 -1
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage-axes.d.ts +10 -0
- package/src/lib/usage-axes.js +40 -2
- package/src/lib/usage-axes.js.map +1 -1
|
@@ -14,10 +14,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import {
|
|
18
|
-
import { formPeriodKey, formStatsTotals } from "@aglyn/aglyn/app-utils/forms";
|
|
19
|
-
import { visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
|
|
20
|
-
import { hostRoleFor, isOrgWideMember, memberCanSee } from "@aglyn/aglyn/app-utils/organizations";
|
|
17
|
+
*/ import { formPeriodKey, formStatsTotals } from "@aglyn/aglyn/app-utils/forms";
|
|
21
18
|
import { PLUGIN_FIGURE_MAX_ROWS, pluginFigureChange, pluginFigureDay, pluginFigureWindows, registerPluginFigureReader } from "@aglyn/aglyn/plugin-manager/plugin-figures";
|
|
22
19
|
import { AI_PLUGIN_ID } from "../constants.js";
|
|
23
20
|
/** The traffic windows: the Traffic card's own ranges. */ export const AI_TRAFFIC_WINDOWS = [
|
|
@@ -34,21 +31,6 @@ import { AI_PLUGIN_ID } from "../constants.js";
|
|
|
34
31
|
/** Referring sites, and campaign sources and campaigns, a sources table lists. */ export const AI_TRAFFIC_TOP_REFERRERS = 8;
|
|
35
32
|
export const AI_TRAFFIC_TOP_CAMPAIGNS = 5;
|
|
36
33
|
/** Forms a forms table reads. */ export const AI_FORMS_READ_LIMIT = 100;
|
|
37
|
-
/** Datasets a dataset summary reads. */ export const AI_DATASETS_READ_LIMIT = 50;
|
|
38
|
-
/** Records a dataset breakdown or field summary reads, in document order; the rest are named in a note. */ export const AI_DATASET_RECORDS_READ_LIMIT = 2000;
|
|
39
|
-
/** The fewest records a breakdown shows a group of by its own label. */ export const AI_DATASET_MIN_GROUP = 3;
|
|
40
|
-
/**
|
|
41
|
-
* The most different values a breakdown groups by: every state and territory,
|
|
42
|
-
* a status, a category. A field with more tells records apart rather than
|
|
43
|
-
* sorting them, so it is refused.
|
|
44
|
-
*/ export const AI_DATASET_MAX_GROUP_VALUES = 60;
|
|
45
|
-
/** The aggregates a breakdown computes. */ export const AI_DATASET_OPERATIONS = [
|
|
46
|
-
'count',
|
|
47
|
-
'sum',
|
|
48
|
-
'average',
|
|
49
|
-
'min',
|
|
50
|
-
'max'
|
|
51
|
-
];
|
|
52
34
|
const str = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
53
35
|
const num = (value)=>{
|
|
54
36
|
const parsed = typeof value === 'number' ? value : Number(value);
|
|
@@ -472,381 +454,10 @@ export function formsFigureReader(firestore) {
|
|
|
472
454
|
};
|
|
473
455
|
}, firestore, 'forms');
|
|
474
456
|
}
|
|
475
|
-
/**
|
|
476
|
-
* The datasets a request may read: those the member who asked may see — and,
|
|
477
|
-
* narrowed to a site, only those shared with it, which is the site Data page's
|
|
478
|
-
* rule (AGL-2891). With no member, only datasets shared with every site.
|
|
479
|
-
*/ async function visibleDatasets(firestore, request) {
|
|
480
|
-
const orgRef = firestore.collection('orgs').doc(request.orgId);
|
|
481
|
-
const [snapshot, memberSnapshot] = await Promise.all([
|
|
482
|
-
orgRef.collection('datasets').limit(AI_DATASETS_READ_LIMIT).get(),
|
|
483
|
-
request.uid ? orgRef.collection('members').doc(request.uid).get() : Promise.resolve(null)
|
|
484
|
-
]);
|
|
485
|
-
const member = (memberSnapshot == null ? void 0 : memberSnapshot.exists) ? memberSnapshot.data() : null;
|
|
486
|
-
if (request.uid && !member) return [];
|
|
487
|
-
if (request.hostId && member && !isOrgWideMember(member) && hostRoleFor(member, request.hostId) === null) {
|
|
488
|
-
return [];
|
|
489
|
-
}
|
|
490
|
-
return snapshot.docs.map((doc)=>{
|
|
491
|
-
var _doc_data;
|
|
492
|
-
return {
|
|
493
|
-
id: doc.id,
|
|
494
|
-
data: (_doc_data = doc.data()) != null ? _doc_data : {}
|
|
495
|
-
};
|
|
496
|
-
}).filter(({ data })=>{
|
|
497
|
-
const visibleTo = data['visibleTo'];
|
|
498
|
-
if (data['deletedAt']) return false;
|
|
499
|
-
if (request.hostId && !visibleToHost(visibleTo, request.hostId)) return false;
|
|
500
|
-
return member ? memberCanSee(member, visibleTo) : Boolean(visibleTo == null ? void 0 : visibleTo.includes('org'));
|
|
501
|
-
}).map(({ id, data })=>({
|
|
502
|
-
id,
|
|
503
|
-
name: str(data['displayName']) || id,
|
|
504
|
-
model: effectiveDatasetModel(data)
|
|
505
|
-
})).sort((a, b)=>a.name.localeCompare(b.name));
|
|
506
|
-
}
|
|
507
|
-
/** The datasets a request may read, with their fields: what the model picks a breakdown from. */ export async function aiDatasetCatalog(firestore, request) {
|
|
508
|
-
const datasets = await visibleDatasets(firestore, _extends({}, request, {
|
|
509
|
-
days: 0,
|
|
510
|
-
now: new Date(0),
|
|
511
|
-
params: {}
|
|
512
|
-
}));
|
|
513
|
-
return datasets.map((dataset)=>({
|
|
514
|
-
id: dataset.id,
|
|
515
|
-
name: dataset.name,
|
|
516
|
-
fields: dataset.model.order.map((fieldId)=>{
|
|
517
|
-
var _ref;
|
|
518
|
-
var _dataset_model_fields_fieldId, _dataset_model_fields_fieldId1;
|
|
519
|
-
return {
|
|
520
|
-
id: fieldId,
|
|
521
|
-
name: str((_dataset_model_fields_fieldId = dataset.model.fields[fieldId]) == null ? void 0 : _dataset_model_fields_fieldId.name) || fieldId,
|
|
522
|
-
type: String((_ref = (_dataset_model_fields_fieldId1 = dataset.model.fields[fieldId]) == null ? void 0 : _dataset_model_fields_fieldId1.type) != null ? _ref : 'text')
|
|
523
|
-
};
|
|
524
|
-
})
|
|
525
|
-
}));
|
|
526
|
-
}
|
|
527
|
-
async function readRecords(firestore, orgId, datasetId) {
|
|
528
|
-
const recordsRef = firestore.collection('orgs').doc(orgId).collection('datasets').doc(datasetId).collection('records');
|
|
529
|
-
const [page, count] = await Promise.all([
|
|
530
|
-
recordsRef.limit(AI_DATASET_RECORDS_READ_LIMIT).get(),
|
|
531
|
-
recordsRef.count().get()
|
|
532
|
-
]);
|
|
533
|
-
return {
|
|
534
|
-
rows: page.docs.map((doc)=>{
|
|
535
|
-
var _values, _doc_data;
|
|
536
|
-
return (_values = ((_doc_data = doc.data()) != null ? _doc_data : {})['values']) != null ? _values : {};
|
|
537
|
-
}),
|
|
538
|
-
total: num(count.data().count)
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
const NUMERIC_TYPES = new Set([
|
|
542
|
-
'float',
|
|
543
|
-
'int32',
|
|
544
|
-
'int64'
|
|
545
|
-
]);
|
|
546
|
-
function readNote(read, total) {
|
|
547
|
-
return total > read ? [
|
|
548
|
-
`Read the first ${read.toLocaleString('en-US')} of ${total.toLocaleString('en-US')} records, so every figure covers only those.`
|
|
549
|
-
] : [];
|
|
550
|
-
}
|
|
551
|
-
/** A field a dataset table may be read by: the model's own id, or its name as the dataset page shows it. */ function fieldOf(model, raw) {
|
|
552
|
-
const wanted = raw.trim().toLowerCase();
|
|
553
|
-
for (const id of model.order){
|
|
554
|
-
var _model_fields_id;
|
|
555
|
-
if (id.toLowerCase() === wanted || str((_model_fields_id = model.fields[id]) == null ? void 0 : _model_fields_id.name).toLowerCase() === wanted) return id;
|
|
556
|
-
}
|
|
557
|
-
return null;
|
|
558
|
-
}
|
|
559
|
-
/** A value as a breakdown groups it: text as written, a number or a yes/no as text; empty is its own group. */ function groupLabel(value) {
|
|
560
|
-
if (value === null || value === undefined || value === '') return '(empty)';
|
|
561
|
-
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
|
|
562
|
-
if (typeof value === 'number' || typeof value === 'string') return String(value).trim() || '(empty)';
|
|
563
|
-
return '(not a single value)';
|
|
564
|
-
}
|
|
565
|
-
export function datasetFigureReaders(firestore) {
|
|
566
|
-
const base = {
|
|
567
|
-
scope: 'org',
|
|
568
|
-
plugin: 'data',
|
|
569
|
-
feature: 'dataStore',
|
|
570
|
-
windows: []
|
|
571
|
-
};
|
|
572
|
-
// Datasets belong to the workspace, so a table links its Data page.
|
|
573
|
-
const source = {
|
|
574
|
-
label: 'Data',
|
|
575
|
-
path: 'data'
|
|
576
|
-
};
|
|
577
|
-
return [
|
|
578
|
-
_extends({}, base, {
|
|
579
|
-
id: 'datasets.summary',
|
|
580
|
-
label: 'Datasets',
|
|
581
|
-
description: 'With no dataset named: each dataset’s records and fields. With one: each of its fields, how many records fill it, how many distinct values it holds and, for a number, its lowest, average and highest.',
|
|
582
|
-
params: [
|
|
583
|
-
{
|
|
584
|
-
name: 'dataset',
|
|
585
|
-
description: 'A dataset’s id or name, to summarize its fields.',
|
|
586
|
-
required: false
|
|
587
|
-
}
|
|
588
|
-
],
|
|
589
|
-
read: async (request)=>{
|
|
590
|
-
const db = firestore();
|
|
591
|
-
const datasets = await visibleDatasets(db, request);
|
|
592
|
-
const named = request.params['dataset'];
|
|
593
|
-
if (!named) {
|
|
594
|
-
const counted = await Promise.all(datasets.slice(0, PLUGIN_FIGURE_MAX_ROWS).map(async (dataset)=>({
|
|
595
|
-
dataset: dataset.name,
|
|
596
|
-
records: num((await db.collection('orgs').doc(request.orgId).collection('datasets').doc(dataset.id).collection('records').count().get()).data().count),
|
|
597
|
-
fields: dataset.model.order.length
|
|
598
|
-
})));
|
|
599
|
-
return {
|
|
600
|
-
ok: true,
|
|
601
|
-
table: {
|
|
602
|
-
title: 'Datasets',
|
|
603
|
-
source,
|
|
604
|
-
period: null,
|
|
605
|
-
columns: [
|
|
606
|
-
{
|
|
607
|
-
key: 'dataset',
|
|
608
|
-
label: 'Dataset',
|
|
609
|
-
kind: 'text'
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
key: 'records',
|
|
613
|
-
label: 'Records',
|
|
614
|
-
kind: 'count'
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
key: 'fields',
|
|
618
|
-
label: 'Fields',
|
|
619
|
-
kind: 'count'
|
|
620
|
-
}
|
|
621
|
-
],
|
|
622
|
-
rows: counted,
|
|
623
|
-
omitted: Math.max(0, datasets.length - counted.length),
|
|
624
|
-
notes: []
|
|
625
|
-
}
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
const dataset = datasets.find((entry)=>entry.id === named || entry.name.toLowerCase() === named.toLowerCase());
|
|
629
|
-
if (!dataset) return refused(404, 'That dataset is not one you can read here');
|
|
630
|
-
const { rows: values, total } = await readRecords(db, request.orgId, dataset.id);
|
|
631
|
-
const rows = dataset.model.order.slice(0, PLUGIN_FIGURE_MAX_ROWS).map((fieldId)=>{
|
|
632
|
-
var _ref;
|
|
633
|
-
const field = dataset.model.fields[fieldId];
|
|
634
|
-
const filled = values.filter((entry)=>entry[fieldId] !== null && entry[fieldId] !== undefined && entry[fieldId] !== '');
|
|
635
|
-
const numbers = NUMERIC_TYPES.has(String(field == null ? void 0 : field.type)) ? filled.map((entry)=>entry[fieldId]).filter((value)=>typeof value === 'number' && Number.isFinite(value)) : [];
|
|
636
|
-
return {
|
|
637
|
-
field: str(field == null ? void 0 : field.name) || fieldId,
|
|
638
|
-
type: String((_ref = field == null ? void 0 : field.type) != null ? _ref : 'text'),
|
|
639
|
-
filled: share(filled.length, values.length),
|
|
640
|
-
distinct: new Set(filled.map((entry)=>groupLabel(entry[fieldId]))).size,
|
|
641
|
-
lowest: numbers.length ? Math.min(...numbers) : null,
|
|
642
|
-
average: numbers.length ? Math.round(numbers.reduce((sum, value)=>sum + value, 0) / numbers.length * 100) / 100 : null,
|
|
643
|
-
highest: numbers.length ? Math.max(...numbers) : null
|
|
644
|
-
};
|
|
645
|
-
});
|
|
646
|
-
return {
|
|
647
|
-
ok: true,
|
|
648
|
-
table: {
|
|
649
|
-
title: `${dataset.name}: fields`,
|
|
650
|
-
source,
|
|
651
|
-
period: null,
|
|
652
|
-
columns: [
|
|
653
|
-
{
|
|
654
|
-
key: 'field',
|
|
655
|
-
label: 'Field',
|
|
656
|
-
kind: 'text'
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
key: 'type',
|
|
660
|
-
label: 'Type',
|
|
661
|
-
kind: 'text'
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
key: 'filled',
|
|
665
|
-
label: 'Records that fill it',
|
|
666
|
-
kind: 'percent'
|
|
667
|
-
},
|
|
668
|
-
{
|
|
669
|
-
key: 'distinct',
|
|
670
|
-
label: 'Distinct values',
|
|
671
|
-
kind: 'count'
|
|
672
|
-
},
|
|
673
|
-
{
|
|
674
|
-
key: 'lowest',
|
|
675
|
-
label: 'Lowest',
|
|
676
|
-
kind: 'number'
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
key: 'average',
|
|
680
|
-
label: 'Average',
|
|
681
|
-
kind: 'number'
|
|
682
|
-
},
|
|
683
|
-
{
|
|
684
|
-
key: 'highest',
|
|
685
|
-
label: 'Highest',
|
|
686
|
-
kind: 'number'
|
|
687
|
-
}
|
|
688
|
-
],
|
|
689
|
-
rows,
|
|
690
|
-
omitted: Math.max(0, dataset.model.order.length - rows.length),
|
|
691
|
-
notes: readNote(values.length, total)
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
}),
|
|
696
|
-
_extends({}, base, {
|
|
697
|
-
id: 'datasets.breakdown',
|
|
698
|
-
label: 'Dataset breakdown',
|
|
699
|
-
description: 'One dataset’s records grouped by the values of one field, with how many records each group holds and, for a number field, its sum, average, lowest or highest in each group.',
|
|
700
|
-
params: [
|
|
701
|
-
{
|
|
702
|
-
name: 'dataset',
|
|
703
|
-
description: 'The dataset’s id or name.',
|
|
704
|
-
required: true
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
name: 'group',
|
|
708
|
-
description: 'The field to group records by, by id or name.',
|
|
709
|
-
required: true
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
name: 'measure',
|
|
713
|
-
description: 'A number field to aggregate, by id or name; empty to count records.',
|
|
714
|
-
required: false
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
name: 'operation',
|
|
718
|
-
description: `One of ${AI_DATASET_OPERATIONS.join(', ')}; count when empty.`,
|
|
719
|
-
required: false
|
|
720
|
-
}
|
|
721
|
-
],
|
|
722
|
-
read: async (request)=>{
|
|
723
|
-
var _request_params_dataset, _request_params_group, _request_params_operation;
|
|
724
|
-
var _dataset_model_fields_measure, _dataset_model_fields_group, _dataset_model_fields_measure1;
|
|
725
|
-
const db = firestore();
|
|
726
|
-
const datasets = await visibleDatasets(db, request);
|
|
727
|
-
const named = (_request_params_dataset = request.params['dataset']) != null ? _request_params_dataset : '';
|
|
728
|
-
const dataset = datasets.find((entry)=>entry.id === named || entry.name.toLowerCase() === named.toLowerCase());
|
|
729
|
-
if (!dataset) return refused(404, 'That dataset is not one you can read here');
|
|
730
|
-
const group = fieldOf(dataset.model, (_request_params_group = request.params['group']) != null ? _request_params_group : '');
|
|
731
|
-
if (!group) return refused(400, 'That dataset has no field by that name to group by');
|
|
732
|
-
const operation = (_request_params_operation = request.params['operation']) != null ? _request_params_operation : 'count';
|
|
733
|
-
if (!AI_DATASET_OPERATIONS.includes(operation)) {
|
|
734
|
-
return refused(400, `The operation is one of ${AI_DATASET_OPERATIONS.join(', ')}`);
|
|
735
|
-
}
|
|
736
|
-
const measure = request.params['measure'] ? fieldOf(dataset.model, request.params['measure']) : null;
|
|
737
|
-
if (operation !== 'count' && (!measure || !NUMERIC_TYPES.has(String((_dataset_model_fields_measure = dataset.model.fields[measure]) == null ? void 0 : _dataset_model_fields_measure.type)))) {
|
|
738
|
-
return refused(400, 'Only a number field can be summed, averaged or ranged');
|
|
739
|
-
}
|
|
740
|
-
const { rows: values, total } = await readRecords(db, request.orgId, dataset.id);
|
|
741
|
-
const groups = new Map();
|
|
742
|
-
const sizes = new Map();
|
|
743
|
-
for (const entry of values){
|
|
744
|
-
var _sizes_get;
|
|
745
|
-
const label = groupLabel(entry[group]);
|
|
746
|
-
sizes.set(label, ((_sizes_get = sizes.get(label)) != null ? _sizes_get : 0) + 1);
|
|
747
|
-
const value = measure ? entry[measure] : null;
|
|
748
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
749
|
-
var _groups_get;
|
|
750
|
-
groups.set(label, [
|
|
751
|
-
...(_groups_get = groups.get(label)) != null ? _groups_get : [],
|
|
752
|
-
value
|
|
753
|
-
]);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
if (sizes.size > AI_DATASET_MAX_GROUP_VALUES) {
|
|
757
|
-
return refused(400, `That field has more than ${AI_DATASET_MAX_GROUP_VALUES} different values, too many to group by. Group by a field many records share, such as a state, a status or a category`);
|
|
758
|
-
}
|
|
759
|
-
// A group of fewer than AI_DATASET_MIN_GROUP records is folded into
|
|
760
|
-
// one row: a group that small is a record, and its label could be a
|
|
761
|
-
// person's name.
|
|
762
|
-
const small = [
|
|
763
|
-
...sizes.entries()
|
|
764
|
-
].filter(([, size])=>size < AI_DATASET_MIN_GROUP);
|
|
765
|
-
const aggregate = (numbers)=>{
|
|
766
|
-
if (operation === 'count') return null;
|
|
767
|
-
if (!numbers.length) return null;
|
|
768
|
-
const sum = numbers.reduce((acc, value)=>acc + value, 0);
|
|
769
|
-
switch(operation){
|
|
770
|
-
case 'sum':
|
|
771
|
-
return Math.round(sum * 100) / 100;
|
|
772
|
-
case 'average':
|
|
773
|
-
return Math.round(sum / numbers.length * 100) / 100;
|
|
774
|
-
case 'min':
|
|
775
|
-
return Math.min(...numbers);
|
|
776
|
-
default:
|
|
777
|
-
return Math.max(...numbers);
|
|
778
|
-
}
|
|
779
|
-
};
|
|
780
|
-
const row = (group, records, numbers)=>operation === 'count' ? {
|
|
781
|
-
group,
|
|
782
|
-
records
|
|
783
|
-
} : {
|
|
784
|
-
group,
|
|
785
|
-
records,
|
|
786
|
-
value: aggregate(numbers)
|
|
787
|
-
};
|
|
788
|
-
const rows = [
|
|
789
|
-
...sizes.entries()
|
|
790
|
-
].filter(([, size])=>size >= AI_DATASET_MIN_GROUP).map(([label, size])=>{
|
|
791
|
-
var _groups_get;
|
|
792
|
-
return row(label, size, (_groups_get = groups.get(label)) != null ? _groups_get : []);
|
|
793
|
-
});
|
|
794
|
-
if (small.length) {
|
|
795
|
-
rows.push(row(`Other (${small.length} groups under ${AI_DATASET_MIN_GROUP} records)`, small.reduce((sum, [, size])=>sum + size, 0), small.flatMap(([label])=>{
|
|
796
|
-
var _groups_get;
|
|
797
|
-
return (_groups_get = groups.get(label)) != null ? _groups_get : [];
|
|
798
|
-
})));
|
|
799
|
-
}
|
|
800
|
-
rows.sort((a, b)=>{
|
|
801
|
-
var _b_value, _a_value;
|
|
802
|
-
return num((_b_value = b['value']) != null ? _b_value : b['records']) - num((_a_value = a['value']) != null ? _a_value : a['records']);
|
|
803
|
-
});
|
|
804
|
-
const groupName = str((_dataset_model_fields_group = dataset.model.fields[group]) == null ? void 0 : _dataset_model_fields_group.name) || group;
|
|
805
|
-
const measureName = measure ? str((_dataset_model_fields_measure1 = dataset.model.fields[measure]) == null ? void 0 : _dataset_model_fields_measure1.name) || measure : '';
|
|
806
|
-
return {
|
|
807
|
-
ok: true,
|
|
808
|
-
table: {
|
|
809
|
-
title: `${dataset.name} by ${groupName}`,
|
|
810
|
-
source,
|
|
811
|
-
period: null,
|
|
812
|
-
columns: [
|
|
813
|
-
{
|
|
814
|
-
key: 'group',
|
|
815
|
-
label: groupName,
|
|
816
|
-
kind: 'text'
|
|
817
|
-
},
|
|
818
|
-
{
|
|
819
|
-
key: 'records',
|
|
820
|
-
label: 'Records',
|
|
821
|
-
kind: 'count'
|
|
822
|
-
},
|
|
823
|
-
...operation === 'count' ? [] : [
|
|
824
|
-
{
|
|
825
|
-
key: 'value',
|
|
826
|
-
label: `${operation[0].toUpperCase()}${operation.slice(1)} of ${measureName}`,
|
|
827
|
-
kind: 'number'
|
|
828
|
-
}
|
|
829
|
-
]
|
|
830
|
-
],
|
|
831
|
-
rows: rows.slice(0, PLUGIN_FIGURE_MAX_ROWS),
|
|
832
|
-
omitted: Math.max(0, rows.length - PLUGIN_FIGURE_MAX_ROWS),
|
|
833
|
-
notes: [
|
|
834
|
-
...readNote(values.length, total),
|
|
835
|
-
...small.length ? [
|
|
836
|
-
`Groups of fewer than ${AI_DATASET_MIN_GROUP} records are counted together as Other.`
|
|
837
|
-
] : []
|
|
838
|
-
]
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
})
|
|
843
|
-
];
|
|
844
|
-
}
|
|
845
457
|
/** Every reader this plugin registers, for the platform's own records. */ export function aiFigureReaders(firestore) {
|
|
846
458
|
return [
|
|
847
459
|
...trafficFigureReaders(firestore),
|
|
848
|
-
formsFigureReader(firestore)
|
|
849
|
-
...datasetFigureReaders(firestore)
|
|
460
|
+
formsFigureReader(firestore)
|
|
850
461
|
];
|
|
851
462
|
}
|
|
852
463
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-figure-readers.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 { effectiveDatasetModel, type DatasetModel } from '@aglyn/aglyn/app-utils/dataset-models'\nimport { formPeriodKey, formStatsTotals, type FormStats } from '@aglyn/aglyn/app-utils/forms'\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { hostRoleFor, isOrgWideMember, memberCanSee } from '@aglyn/aglyn/app-utils/organizations'\nimport type { AglynOrgMember } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport {\n PLUGIN_FIGURE_MAX_ROWS,\n pluginFigureChange,\n pluginFigureDay,\n pluginFigureWindows,\n registerPluginFigureReader,\n type PluginFigureReader,\n type PluginFigureRead,\n type PluginFigureRequest,\n type PluginFigureRow,\n type PluginFigureTable,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport { AI_PLUGIN_ID } from '../constants'\n\n/**\n * The figure readers this plugin registers for records the PLATFORM keeps\n * (AGL-2915): a site's page-view counters, its forms' counters and the\n * workspace's datasets. Every other reader an insight reads is registered by\n * the plugin that owns its records — commerce, bookings, marketing — on the\n * `plugin-figures` seam, and nothing here reads their documents.\n *\n * Each reader answers with one compact table of aggregates, read-only and\n * about nobody: counts, sums and rates, labeled by a page path, a form's name,\n * a referring site or a dataset's own field — never a visitor, a submission or\n * a record. A dataset breakdown refuses a field with more than\n * `AI_DATASET_MAX_GROUP_VALUES` different values — on a dataset of any size, a\n * name, an address or an order number — and folds every group of fewer than\n * `AI_DATASET_MIN_GROUP` records into one row, so each label it shows is a\n * value at least that many records share.\n *\n * The readers use the arithmetic their console cards use: the Traffic card's\n * windows and change (`pluginFigureWindows`, `pluginFigureChange`, which are\n * that card's split and delta), and the forms plugin's month counters through\n * `formStatsTotals`, the one function every forms surface windows by.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\n/** How a reader reaches Firestore: resolved when it reads, never when it registers. */\nexport type AiFigureFirestore = () => Firestore\n\n/** The traffic windows: the Traffic card's own ranges. */\nexport const AI_TRAFFIC_WINDOWS: readonly number[] = [7, 14, 30, 90]\n\n/** A day-by-day table fits a table's row bound only over a short window. */\nexport const AI_TRAFFIC_DAILY_WINDOWS: readonly number[] = [7, 14]\n\n/** Pages a pages table lists; the rest are counted in `omitted`. */\nexport const AI_TRAFFIC_TOP_PAGES = 10\n\n/** Referring sites, and campaign sources and campaigns, a sources table lists. */\nexport const AI_TRAFFIC_TOP_REFERRERS = 8\nexport const AI_TRAFFIC_TOP_CAMPAIGNS = 5\n\n/** Forms a forms table reads. */\nexport const AI_FORMS_READ_LIMIT = 100\n\n/** Datasets a dataset summary reads. */\nexport const AI_DATASETS_READ_LIMIT = 50\n\n/** Records a dataset breakdown or field summary reads, in document order; the rest are named in a note. */\nexport const AI_DATASET_RECORDS_READ_LIMIT = 2_000\n\n/** The fewest records a breakdown shows a group of by its own label. */\nexport const AI_DATASET_MIN_GROUP = 3\n\n/**\n * The most different values a breakdown groups by: every state and territory,\n * a status, a category. A field with more tells records apart rather than\n * sorting them, so it is refused.\n */\nexport const AI_DATASET_MAX_GROUP_VALUES = 60\n\n/** The aggregates a breakdown computes. */\nexport const AI_DATASET_OPERATIONS = ['count', 'sum', 'average', 'min', 'max'] as const\nexport type AiDatasetOperation = (typeof AI_DATASET_OPERATIONS)[number]\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst num = (value: unknown): number => {\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nconst round1 = (value: number): number => Math.round(value * 10) / 10\n\nconst share = (part: number, whole: number): number | null => (whole > 0 ? round1((part / whole) * 100) : null)\n\nfunction refused(status: 400 | 403 | 404, error: string): PluginFigureRead {\n return { ok: false, status, error }\n}\n\n/** A window's days as the table's period, oldest to newest. */\nfunction periodOf(request: PluginFigureRequest): PluginFigureTable['period'] {\n const { current } = pluginFigureWindows(request.now, request.days)\n return { from: current.from, to: current.to, days: request.days }\n}\n\n/** Every day id of the window and the one before it, newest first. */\nfunction dayIds(now: Date, days: number): { current: string[]; previous: string[] } {\n const { current, previous } = pluginFigureWindows(now, days)\n const walk = (startMs: number, endMs: number): string[] => {\n const ids: string[] = []\n for (let at = endMs - 86_400_000; at >= startMs; at -= 86_400_000) ids.push(pluginFigureDay(new Date(at)))\n return ids\n }\n return { current: walk(current.startMs, current.endMs), previous: walk(previous.startMs, previous.endMs) }\n}\n\n// ── Traffic ───────────────────────────────────────────────────────────────\n\ninterface TrafficDay {\n total: number\n visitors: number\n paths: Record<string, number>\n referrers: Record<string, number>\n utm: Record<string, Record<string, number>>\n}\n\nconst counts = (value: unknown): Record<string, number> => {\n const out: Record<string, number> = {}\n if (!value || typeof value !== 'object') return out\n for (const [key, count] of Object.entries(value as Record<string, unknown>)) {\n const n = num(count)\n if (n > 0) out[key] = n\n }\n return out\n}\n\nasync function readTrafficDays(\n firestore: Firestore,\n hostId: string,\n ids: readonly string[],\n): Promise<TrafficDay[]> {\n if (!ids.length) return []\n const collection = firestore.collection('hosts').doc(hostId).collection('analytics')\n const snapshots = await firestore.getAll(...ids.map((id) => collection.doc(id)))\n return snapshots.map((snapshot) => {\n const data = (snapshot.exists ? snapshot.data() : null) ?? {}\n const utm: Record<string, Record<string, number>> = {}\n for (const [param, values] of Object.entries((data['utm'] ?? {}) as Record<string, unknown>)) {\n utm[param] = counts(values)\n }\n return {\n total: num(data['total']),\n visitors: num(data['visitors']),\n paths: counts(data['paths']),\n referrers: counts(data['referrers']),\n utm,\n }\n })\n}\n\nfunction sumMaps(days: readonly TrafficDay[], pick: (day: TrafficDay) => Record<string, number>): Map<string, number> {\n const totals = new Map<string, number>()\n for (const day of days) {\n for (const [key, count] of Object.entries(pick(day))) totals.set(key, (totals.get(key) ?? 0) + count)\n }\n return totals\n}\n\nconst ranked = (totals: Map<string, number>): Array<[string, number]> =>\n [...totals.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\nfunction siteReader(\n base: Omit<PluginFigureReader, 'read' | 'scope' | 'plugin'>,\n read: (firestore: Firestore, request: PluginFigureRequest & { hostId: string }) => Promise<PluginFigureRead>,\n firestore: AiFigureFirestore,\n plugin: string | null,\n): PluginFigureReader {\n return {\n ...base,\n scope: 'site',\n plugin,\n read: async (request) => {\n if (!request.hostId) return refused(400, 'Open a site to read its figures')\n if (!base.windows.includes(request.days)) return refused(400, 'That window is not one this reader covers')\n return read(firestore(), { ...request, hostId: request.hostId })\n },\n }\n}\n\nexport function trafficFigureReaders(firestore: AiFigureFirestore): PluginFigureReader[] {\n const source = { label: 'Analytics', path: 'analytics' }\n const visitorsNote =\n 'Visitors are counted once per browser tab per day, so one person on two days counts twice.'\n return [\n siteReader(\n {\n id: 'traffic.summary',\n label: 'Traffic',\n description:\n 'Page views, visitors and views a day on the site over the window, each beside the window before it and the change.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const ids = dayIds(request.now, request.days)\n const [current, previous] = await Promise.all([\n readTrafficDays(db, request.hostId, ids.current),\n readTrafficDays(db, request.hostId, ids.previous),\n ])\n const total = (days: TrafficDay[]) => days.reduce((sum, day) => sum + day.total, 0)\n const visitors = (days: TrafficDay[]) => days.reduce((sum, day) => sum + day.visitors, 0)\n const views = total(current)\n const viewsBefore = total(previous)\n const perDay = Math.round(views / request.days)\n const perDayBefore = Math.round(viewsBefore / request.days)\n const table: PluginFigureTable = {\n title: 'Traffic',\n source,\n period: periodOf(request),\n columns: [\n { key: 'figure', label: 'Figure', kind: 'text' },\n { key: 'current', label: 'This window', kind: 'count' },\n { key: 'previous', label: 'Window before', kind: 'count' },\n { key: 'change', label: 'Change', kind: 'change' },\n ],\n rows: [\n { figure: 'Page views', current: views, previous: viewsBefore, change: pluginFigureChange(views, viewsBefore) },\n {\n figure: 'Visitors',\n current: visitors(current),\n previous: visitors(previous),\n change: pluginFigureChange(visitors(current), visitors(previous)),\n },\n {\n figure: 'Page views a day',\n current: perDay,\n previous: perDayBefore,\n change: pluginFigureChange(perDay, perDayBefore),\n },\n ],\n omitted: 0,\n notes: [\n visitorsNote,\n ...(viewsBefore ? [] : ['The window before this one recorded no page views, so there is no change to compare.']),\n ],\n }\n return { ok: true, table }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.pages',\n label: 'Top pages',\n description: `The site's ${AI_TRAFFIC_TOP_PAGES} most viewed pages over the window, with each page's share of all views and its change from the window before.`,\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const ids = dayIds(request.now, request.days)\n const [current, previous] = await Promise.all([\n readTrafficDays(db, request.hostId, ids.current),\n readTrafficDays(db, request.hostId, ids.previous),\n ])\n const now = ranked(sumMaps(current, (day) => day.paths))\n const before = sumMaps(previous, (day) => day.paths)\n const all = now.reduce((sum, [, count]) => sum + count, 0)\n const rows: PluginFigureRow[] = now.slice(0, AI_TRAFFIC_TOP_PAGES).map(([path, views]) => ({\n page: path,\n views,\n share: share(views, all),\n change: pluginFigureChange(views, before.get(path) ?? null),\n }))\n return {\n ok: true,\n table: {\n title: 'Top pages',\n source,\n period: periodOf(request),\n columns: [\n { key: 'page', label: 'Page', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'share', label: 'Share of views', kind: 'percent' },\n { key: 'change', label: 'Change', kind: 'change' },\n ],\n rows,\n omitted: Math.max(0, now.length - rows.length),\n notes: [],\n },\n }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.sources',\n label: 'Traffic sources',\n description:\n 'Where visits came from over the window: the referring sites, and the campaign sources and campaigns links were tagged with, with each one’s views.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const days = await readTrafficDays(db, request.hostId, dayIds(request.now, request.days).current)\n const all = days.reduce((sum, day) => sum + day.total, 0)\n const referrers = ranked(sumMaps(days, (day) => day.referrers))\n const sources = ranked(sumMaps(days, (day) => day.utm['source'] ?? {}))\n const campaigns = ranked(sumMaps(days, (day) => day.utm['campaign'] ?? {}))\n const rowsOf = (entries: Array<[string, number]>, limit: number, type: string) =>\n entries.slice(0, limit).map(([name, views]) => ({ source: name, type, views, share: share(views, all) }))\n const rows = [\n ...rowsOf(referrers, AI_TRAFFIC_TOP_REFERRERS, 'Referring site'),\n ...rowsOf(sources, AI_TRAFFIC_TOP_CAMPAIGNS, 'Campaign source'),\n ...rowsOf(campaigns, AI_TRAFFIC_TOP_CAMPAIGNS, 'Campaign'),\n ]\n return {\n ok: true,\n table: {\n title: 'Traffic sources',\n source,\n period: periodOf(request),\n columns: [\n { key: 'source', label: 'Source', kind: 'text' },\n { key: 'type', label: 'Kind', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'share', label: 'Share of all views', kind: 'percent' },\n ],\n rows,\n omitted:\n Math.max(0, referrers.length - AI_TRAFFIC_TOP_REFERRERS) +\n Math.max(0, sources.length - AI_TRAFFIC_TOP_CAMPAIGNS) +\n Math.max(0, campaigns.length - AI_TRAFFIC_TOP_CAMPAIGNS),\n notes: ['A visit with no referring site, such as a typed address, is in no row.'],\n },\n }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.daily',\n label: 'Traffic by day',\n description: 'Page views and visitors on each day of the window, oldest first, for spotting a day that stood out.',\n windows: AI_TRAFFIC_DAILY_WINDOWS,\n },\n async (db, request) => {\n const ids = [...dayIds(request.now, request.days).current].reverse()\n const days = await readTrafficDays(db, request.hostId, ids)\n return {\n ok: true,\n table: {\n title: 'Traffic by day',\n source,\n period: periodOf(request),\n columns: [\n { key: 'day', label: 'Day', kind: 'date' },\n { key: 'views', label: 'Page views', kind: 'count' },\n { key: 'visitors', label: 'Visitors', kind: 'count' },\n ],\n rows: days.map((day, index) => ({ day: ids[index], views: day.total, visitors: day.visitors })),\n omitted: 0,\n notes: [visitorsNote],\n },\n }\n },\n firestore,\n null,\n ),\n ]\n}\n\n// ── Forms ─────────────────────────────────────────────────────────────────\n\nexport function formsFigureReader(firestore: AiFigureFirestore): PluginFigureReader {\n return siteReader(\n {\n id: 'forms.performance',\n label: 'Forms',\n description:\n 'Each form’s views, submissions, the share of views that became a submission, and leads, over the calendar months the window falls in.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const { current } = pluginFigureWindows(request.now, request.days)\n const from = formPeriodKey(current.startMs)\n const to = formPeriodKey(current.endMs - 1)\n const snapshot = await db\n .collection('hosts')\n .doc(request.hostId)\n .collection('forms')\n .limit(AI_FORMS_READ_LIMIT)\n .get()\n const forms = snapshot.docs\n .map((doc) => ({ doc, data: (doc.data() ?? {}) as Record<string, unknown> }))\n .filter(({ data }) => !data['deletedAt'])\n .map(({ doc, data }) => {\n const totals = formStatsTotals(data['stats'] as FormStats | undefined, { from, to })\n return {\n form: str(data['displayName']) || doc.id,\n views: totals.views,\n submissions: totals.submissions,\n completion:\n totals.views && totals.submissions !== null ? share(totals.submissions, totals.views) : null,\n leads: totals.leads,\n }\n })\n .sort((a, b) => (b.submissions ?? -1) - (a.submissions ?? -1) || a.form.localeCompare(b.form))\n const monthStart = from ? `${from}-01` : current.from\n return {\n ok: true,\n table: {\n title: 'Forms',\n source: { label: 'Forms', path: 'forms' },\n period: {\n from: monthStart,\n to: current.to,\n days: Math.round((Date.parse(current.to) - Date.parse(monthStart)) / 86_400_000) + 1,\n },\n columns: [\n { key: 'form', label: 'Form', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'submissions', label: 'Submissions', kind: 'count' },\n { key: 'completion', label: 'Views that became a submission', kind: 'percent' },\n { key: 'leads', label: 'Leads', kind: 'count' },\n ],\n rows: forms.slice(0, PLUGIN_FIGURE_MAX_ROWS),\n omitted: Math.max(0, forms.length - PLUGIN_FIGURE_MAX_ROWS),\n notes: [\n 'Form figures are kept by calendar month, so they cover whole months from the first day of the month the window starts in.',\n 'Views are counted in the visitor’s browser and submissions on the server, so a rate can pass 100%.',\n ],\n },\n }\n },\n firestore,\n 'forms',\n )\n}\n\n// ── Datasets ──────────────────────────────────────────────────────────────\n\ninterface VisibleDataset {\n id: string\n name: string\n model: DatasetModel\n}\n\n/**\n * The datasets a request may read: those the member who asked may see — and,\n * narrowed to a site, only those shared with it, which is the site Data page's\n * rule (AGL-2891). With no member, only datasets shared with every site.\n */\nasync function visibleDatasets(firestore: Firestore, request: PluginFigureRequest): Promise<VisibleDataset[]> {\n const orgRef = firestore.collection('orgs').doc(request.orgId)\n const [snapshot, memberSnapshot] = await Promise.all([\n orgRef.collection('datasets').limit(AI_DATASETS_READ_LIMIT).get(),\n request.uid ? orgRef.collection('members').doc(request.uid).get() : Promise.resolve(null),\n ])\n const member = memberSnapshot?.exists ? (memberSnapshot.data() as Partial<AglynOrgMember>) : null\n if (request.uid && !member) return []\n if (request.hostId && member && !isOrgWideMember(member) && hostRoleFor(member, request.hostId) === null) {\n return []\n }\n return snapshot.docs\n .map((doc) => ({ id: doc.id, data: (doc.data() ?? {}) as Record<string, unknown> }))\n .filter(({ data }) => {\n const visibleTo = data['visibleTo'] as string[] | undefined\n if (data['deletedAt']) return false\n if (request.hostId && !visibleToHost(visibleTo, request.hostId)) return false\n return member ? memberCanSee(member, visibleTo) : Boolean(visibleTo?.includes('org'))\n })\n .map(({ id, data }) => ({\n id,\n name: str(data['displayName']) || id,\n model: effectiveDatasetModel(data as { model?: DatasetModel; fields?: string[] }),\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/** A dataset a question may name, with its fields, as the read prompt lists it. */\nexport interface AiDatasetCatalogEntry {\n id: string\n name: string\n fields: Array<{ id: string; name: string; type: string }>\n}\n\n/** The datasets a request may read, with their fields: what the model picks a breakdown from. */\nexport async function aiDatasetCatalog(\n firestore: Firestore,\n request: Pick<PluginFigureRequest, 'orgId' | 'hostId' | 'uid'>,\n): Promise<AiDatasetCatalogEntry[]> {\n const datasets = await visibleDatasets(firestore, { ...request, days: 0, now: new Date(0), params: {} })\n return datasets.map((dataset) => ({\n id: dataset.id,\n name: dataset.name,\n fields: dataset.model.order.map((fieldId) => ({\n id: fieldId,\n name: str(dataset.model.fields[fieldId]?.name) || fieldId,\n type: String(dataset.model.fields[fieldId]?.type ?? 'text'),\n })),\n }))\n}\n\nasync function readRecords(\n firestore: Firestore,\n orgId: string,\n datasetId: string,\n): Promise<{ rows: Array<Record<string, unknown>>; total: number }> {\n const recordsRef = firestore.collection('orgs').doc(orgId).collection('datasets').doc(datasetId).collection('records')\n const [page, count] = await Promise.all([\n recordsRef.limit(AI_DATASET_RECORDS_READ_LIMIT).get(),\n recordsRef.count().get(),\n ])\n return {\n rows: page.docs.map((doc) => ((doc.data() ?? {})['values'] ?? {}) as Record<string, unknown>),\n total: num(count.data().count),\n }\n}\n\nconst NUMERIC_TYPES = new Set(['float', 'int32', 'int64'])\n\nfunction readNote(read: number, total: number): string[] {\n return total > read\n ? [`Read the first ${read.toLocaleString('en-US')} of ${total.toLocaleString('en-US')} records, so every figure covers only those.`]\n : []\n}\n\n/** A field a dataset table may be read by: the model's own id, or its name as the dataset page shows it. */\nfunction fieldOf(model: DatasetModel, raw: string): string | null {\n const wanted = raw.trim().toLowerCase()\n for (const id of model.order) {\n if (id.toLowerCase() === wanted || str(model.fields[id]?.name).toLowerCase() === wanted) return id\n }\n return null\n}\n\n/** A value as a breakdown groups it: text as written, a number or a yes/no as text; empty is its own group. */\nfunction groupLabel(value: unknown): string {\n if (value === null || value === undefined || value === '') return '(empty)'\n if (typeof value === 'boolean') return value ? 'Yes' : 'No'\n if (typeof value === 'number' || typeof value === 'string') return String(value).trim() || '(empty)'\n return '(not a single value)'\n}\n\nexport function datasetFigureReaders(firestore: AiFigureFirestore): PluginFigureReader[] {\n const base = { scope: 'org' as const, plugin: 'data', feature: 'dataStore', windows: [] as number[] }\n // Datasets belong to the workspace, so a table links its Data page.\n const source = { label: 'Data', path: 'data' }\n return [\n {\n ...base,\n id: 'datasets.summary',\n label: 'Datasets',\n description:\n 'With no dataset named: each dataset’s records and fields. With one: each of its fields, how many records fill it, how many distinct values it holds and, for a number, its lowest, average and highest.',\n params: [{ name: 'dataset', description: 'A dataset’s id or name, to summarize its fields.', required: false }],\n read: async (request) => {\n const db = firestore()\n const datasets = await visibleDatasets(db, request)\n const named = request.params['dataset']\n if (!named) {\n const counted = await Promise.all(\n datasets.slice(0, PLUGIN_FIGURE_MAX_ROWS).map(async (dataset) => ({\n dataset: dataset.name,\n records: num(\n (\n await db\n .collection('orgs')\n .doc(request.orgId)\n .collection('datasets')\n .doc(dataset.id)\n .collection('records')\n .count()\n .get()\n ).data().count,\n ),\n fields: dataset.model.order.length,\n })),\n )\n return {\n ok: true,\n table: {\n title: 'Datasets',\n source,\n period: null,\n columns: [\n { key: 'dataset', label: 'Dataset', kind: 'text' },\n { key: 'records', label: 'Records', kind: 'count' },\n { key: 'fields', label: 'Fields', kind: 'count' },\n ],\n rows: counted,\n omitted: Math.max(0, datasets.length - counted.length),\n notes: [],\n },\n }\n }\n const dataset = datasets.find((entry) => entry.id === named || entry.name.toLowerCase() === named.toLowerCase())\n if (!dataset) return refused(404, 'That dataset is not one you can read here')\n const { rows: values, total } = await readRecords(db, request.orgId, dataset.id)\n const rows = dataset.model.order.slice(0, PLUGIN_FIGURE_MAX_ROWS).map((fieldId) => {\n const field = dataset.model.fields[fieldId]\n const filled = values.filter((entry) => entry[fieldId] !== null && entry[fieldId] !== undefined && entry[fieldId] !== '')\n const numbers = NUMERIC_TYPES.has(String(field?.type))\n ? filled.map((entry) => entry[fieldId]).filter((value): value is number => typeof value === 'number' && Number.isFinite(value))\n : []\n return {\n field: str(field?.name) || fieldId,\n type: String(field?.type ?? 'text'),\n filled: share(filled.length, values.length),\n distinct: new Set(filled.map((entry) => groupLabel(entry[fieldId]))).size,\n lowest: numbers.length ? Math.min(...numbers) : null,\n average: numbers.length ? Math.round((numbers.reduce((sum, value) => sum + value, 0) / numbers.length) * 100) / 100 : null,\n highest: numbers.length ? Math.max(...numbers) : null,\n }\n })\n return {\n ok: true,\n table: {\n title: `${dataset.name}: fields`,\n source,\n period: null,\n columns: [\n { key: 'field', label: 'Field', kind: 'text' },\n { key: 'type', label: 'Type', kind: 'text' },\n { key: 'filled', label: 'Records that fill it', kind: 'percent' },\n { key: 'distinct', label: 'Distinct values', kind: 'count' },\n { key: 'lowest', label: 'Lowest', kind: 'number' },\n { key: 'average', label: 'Average', kind: 'number' },\n { key: 'highest', label: 'Highest', kind: 'number' },\n ],\n rows,\n omitted: Math.max(0, dataset.model.order.length - rows.length),\n notes: readNote(values.length, total),\n },\n }\n },\n },\n {\n ...base,\n id: 'datasets.breakdown',\n label: 'Dataset breakdown',\n description:\n 'One dataset’s records grouped by the values of one field, with how many records each group holds and, for a number field, its sum, average, lowest or highest in each group.',\n params: [\n { name: 'dataset', description: 'The dataset’s id or name.', required: true },\n { name: 'group', description: 'The field to group records by, by id or name.', required: true },\n { name: 'measure', description: 'A number field to aggregate, by id or name; empty to count records.', required: false },\n { name: 'operation', description: `One of ${AI_DATASET_OPERATIONS.join(', ')}; count when empty.`, required: false },\n ],\n read: async (request) => {\n const db = firestore()\n const datasets = await visibleDatasets(db, request)\n const named = request.params['dataset'] ?? ''\n const dataset = datasets.find((entry) => entry.id === named || entry.name.toLowerCase() === named.toLowerCase())\n if (!dataset) return refused(404, 'That dataset is not one you can read here')\n const group = fieldOf(dataset.model, request.params['group'] ?? '')\n if (!group) return refused(400, 'That dataset has no field by that name to group by')\n const operation = (request.params['operation'] ?? 'count') as AiDatasetOperation\n if (!(AI_DATASET_OPERATIONS as readonly string[]).includes(operation)) {\n return refused(400, `The operation is one of ${AI_DATASET_OPERATIONS.join(', ')}`)\n }\n const measure = request.params['measure'] ? fieldOf(dataset.model, request.params['measure']) : null\n if (operation !== 'count' && (!measure || !NUMERIC_TYPES.has(String(dataset.model.fields[measure]?.type)))) {\n return refused(400, 'Only a number field can be summed, averaged or ranged')\n }\n const { rows: values, total } = await readRecords(db, request.orgId, dataset.id)\n const groups = new Map<string, number[]>()\n const sizes = new Map<string, number>()\n for (const entry of values) {\n const label = groupLabel(entry[group])\n sizes.set(label, (sizes.get(label) ?? 0) + 1)\n const value = measure ? entry[measure] : null\n if (typeof value === 'number' && Number.isFinite(value)) {\n groups.set(label, [...(groups.get(label) ?? []), value])\n }\n }\n if (sizes.size > AI_DATASET_MAX_GROUP_VALUES) {\n return refused(\n 400,\n `That field has more than ${AI_DATASET_MAX_GROUP_VALUES} different values, too many to group by. Group by a field many records share, such as a state, a status or a category`,\n )\n }\n // A group of fewer than AI_DATASET_MIN_GROUP records is folded into\n // one row: a group that small is a record, and its label could be a\n // person's name.\n const small = [...sizes.entries()].filter(([, size]) => size < AI_DATASET_MIN_GROUP)\n const aggregate = (numbers: number[]): number | null => {\n if (operation === 'count') return null\n if (!numbers.length) return null\n const sum = numbers.reduce((acc, value) => acc + value, 0)\n switch (operation) {\n case 'sum':\n return Math.round(sum * 100) / 100\n case 'average':\n return Math.round((sum / numbers.length) * 100) / 100\n case 'min':\n return Math.min(...numbers)\n default:\n return Math.max(...numbers)\n }\n }\n const row = (group: string, records: number, numbers: number[]): PluginFigureRow =>\n operation === 'count' ? { group, records } : { group, records, value: aggregate(numbers) }\n const rows: PluginFigureRow[] = [...sizes.entries()]\n .filter(([, size]) => size >= AI_DATASET_MIN_GROUP)\n .map(([label, size]) => row(label, size, groups.get(label) ?? []))\n if (small.length) {\n rows.push(\n row(\n `Other (${small.length} groups under ${AI_DATASET_MIN_GROUP} records)`,\n small.reduce((sum, [, size]) => sum + size, 0),\n small.flatMap(([label]) => groups.get(label) ?? []),\n ),\n )\n }\n rows.sort((a, b) => (num(b['value'] ?? b['records']) - num(a['value'] ?? a['records'])))\n const groupName = str(dataset.model.fields[group]?.name) || group\n const measureName = measure ? str(dataset.model.fields[measure]?.name) || measure : ''\n return {\n ok: true,\n table: {\n title: `${dataset.name} by ${groupName}`,\n source,\n period: null,\n columns: [\n { key: 'group', label: groupName, kind: 'text' },\n { key: 'records', label: 'Records', kind: 'count' },\n ...(operation === 'count'\n ? []\n : [{ key: 'value', label: `${operation[0].toUpperCase()}${operation.slice(1)} of ${measureName}`, kind: 'number' as const }]),\n ],\n rows: rows.slice(0, PLUGIN_FIGURE_MAX_ROWS),\n omitted: Math.max(0, rows.length - PLUGIN_FIGURE_MAX_ROWS),\n notes: [\n ...readNote(values.length, total),\n ...(small.length ? [`Groups of fewer than ${AI_DATASET_MIN_GROUP} records are counted together as Other.`] : []),\n ],\n },\n }\n },\n },\n ]\n}\n\n/** Every reader this plugin registers, for the platform's own records. */\nexport function aiFigureReaders(firestore: AiFigureFirestore): PluginFigureReader[] {\n return [...trafficFigureReaders(firestore), formsFigureReader(firestore), ...datasetFigureReaders(firestore)]\n}\n\n/**\n * Registers them from the console surface, the one surface that runs insight\n * jobs. By a call, never by an import (AGL-3025).\n */\nexport function registerAiFigureReaders(firestore: AiFigureFirestore): void {\n for (const reader of aiFigureReaders(firestore)) {\n registerPluginFigureReader(reader, { pluginId: AI_PLUGIN_ID })\n }\n}\n"],"names":["effectiveDatasetModel","formPeriodKey","formStatsTotals","visibleToHost","hostRoleFor","isOrgWideMember","memberCanSee","PLUGIN_FIGURE_MAX_ROWS","pluginFigureChange","pluginFigureDay","pluginFigureWindows","registerPluginFigureReader","AI_PLUGIN_ID","AI_TRAFFIC_WINDOWS","AI_TRAFFIC_DAILY_WINDOWS","AI_TRAFFIC_TOP_PAGES","AI_TRAFFIC_TOP_REFERRERS","AI_TRAFFIC_TOP_CAMPAIGNS","AI_FORMS_READ_LIMIT","AI_DATASETS_READ_LIMIT","AI_DATASET_RECORDS_READ_LIMIT","AI_DATASET_MIN_GROUP","AI_DATASET_MAX_GROUP_VALUES","AI_DATASET_OPERATIONS","str","value","trim","num","parsed","Number","isFinite","round1","Math","round","share","part","whole","refused","status","error","ok","periodOf","request","current","now","days","from","to","dayIds","previous","walk","startMs","endMs","ids","at","push","Date","counts","out","key","count","Object","entries","n","readTrafficDays","firestore","hostId","length","collection","doc","snapshots","getAll","map","id","snapshot","data","exists","utm","param","values","total","visitors","paths","referrers","sumMaps","pick","totals","Map","day","set","get","ranked","sort","a","b","localeCompare","siteReader","base","read","plugin","scope","windows","includes","trafficFigureReaders","source","label","path","visitorsNote","description","db","Promise","all","reduce","sum","views","viewsBefore","perDay","perDayBefore","table","title","period","columns","kind","rows","figure","change","omitted","notes","before","slice","page","max","sources","campaigns","rowsOf","limit","type","name","reverse","index","formsFigureReader","forms","docs","filter","form","submissions","completion","leads","monthStart","parse","visibleDatasets","orgRef","orgId","memberSnapshot","uid","resolve","member","visibleTo","Boolean","model","aiDatasetCatalog","datasets","params","dataset","fields","order","fieldId","String","readRecords","datasetId","recordsRef","NUMERIC_TYPES","Set","readNote","toLocaleString","fieldOf","raw","wanted","toLowerCase","groupLabel","undefined","datasetFigureReaders","feature","required","named","counted","records","find","entry","field","filled","numbers","has","distinct","size","lowest","min","average","highest","join","group","operation","measure","groups","sizes","small","aggregate","acc","row","flatMap","groupName","measureName","toUpperCase","aiFigureReaders","registerAiFigureReaders","reader","pluginId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/insights/ai-figure-readers.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 { formPeriodKey, formStatsTotals, type FormStats } from '@aglyn/aglyn/app-utils/forms'\nimport {\n PLUGIN_FIGURE_MAX_ROWS,\n pluginFigureChange,\n pluginFigureDay,\n pluginFigureWindows,\n registerPluginFigureReader,\n type PluginFigureReader,\n type PluginFigureRead,\n type PluginFigureRequest,\n type PluginFigureRow,\n type PluginFigureTable,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport { AI_PLUGIN_ID } from '../constants'\n\n/**\n * The figure readers this plugin registers for records the PLATFORM keeps\n * (AGL-2915): a site's page-view counters and its forms' counters. Every\n * other reader an insight reads is registered by the plugin that owns its\n * records — commerce, bookings, marketing, the workspace's datasets — on the\n * `plugin-figures` seam, and nothing here reads their documents.\n *\n * Each reader answers with one compact table of aggregates, read-only and\n * about nobody: counts, sums and rates, labeled by a page path, a form's name\n * or a referring site — never a visitor or a submission.\n *\n * The readers use the arithmetic their console cards use: the Traffic card's\n * windows and change (`pluginFigureWindows`, `pluginFigureChange`, which are\n * that card's split and delta), and the forms plugin's month counters through\n * `formStatsTotals`, the one function every forms surface windows by.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\n/** How a reader reaches Firestore: resolved when it reads, never when it registers. */\nexport type AiFigureFirestore = () => Firestore\n\n/** The traffic windows: the Traffic card's own ranges. */\nexport const AI_TRAFFIC_WINDOWS: readonly number[] = [7, 14, 30, 90]\n\n/** A day-by-day table fits a table's row bound only over a short window. */\nexport const AI_TRAFFIC_DAILY_WINDOWS: readonly number[] = [7, 14]\n\n/** Pages a pages table lists; the rest are counted in `omitted`. */\nexport const AI_TRAFFIC_TOP_PAGES = 10\n\n/** Referring sites, and campaign sources and campaigns, a sources table lists. */\nexport const AI_TRAFFIC_TOP_REFERRERS = 8\nexport const AI_TRAFFIC_TOP_CAMPAIGNS = 5\n\n/** Forms a forms table reads. */\nexport const AI_FORMS_READ_LIMIT = 100\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst num = (value: unknown): number => {\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nconst round1 = (value: number): number => Math.round(value * 10) / 10\n\nconst share = (part: number, whole: number): number | null => (whole > 0 ? round1((part / whole) * 100) : null)\n\nfunction refused(status: 400 | 403 | 404, error: string): PluginFigureRead {\n return { ok: false, status, error }\n}\n\n/** A window's days as the table's period, oldest to newest. */\nfunction periodOf(request: PluginFigureRequest): PluginFigureTable['period'] {\n const { current } = pluginFigureWindows(request.now, request.days)\n return { from: current.from, to: current.to, days: request.days }\n}\n\n/** Every day id of the window and the one before it, newest first. */\nfunction dayIds(now: Date, days: number): { current: string[]; previous: string[] } {\n const { current, previous } = pluginFigureWindows(now, days)\n const walk = (startMs: number, endMs: number): string[] => {\n const ids: string[] = []\n for (let at = endMs - 86_400_000; at >= startMs; at -= 86_400_000) ids.push(pluginFigureDay(new Date(at)))\n return ids\n }\n return { current: walk(current.startMs, current.endMs), previous: walk(previous.startMs, previous.endMs) }\n}\n\n// ── Traffic ───────────────────────────────────────────────────────────────\n\ninterface TrafficDay {\n total: number\n visitors: number\n paths: Record<string, number>\n referrers: Record<string, number>\n utm: Record<string, Record<string, number>>\n}\n\nconst counts = (value: unknown): Record<string, number> => {\n const out: Record<string, number> = {}\n if (!value || typeof value !== 'object') return out\n for (const [key, count] of Object.entries(value as Record<string, unknown>)) {\n const n = num(count)\n if (n > 0) out[key] = n\n }\n return out\n}\n\nasync function readTrafficDays(\n firestore: Firestore,\n hostId: string,\n ids: readonly string[],\n): Promise<TrafficDay[]> {\n if (!ids.length) return []\n const collection = firestore.collection('hosts').doc(hostId).collection('analytics')\n const snapshots = await firestore.getAll(...ids.map((id) => collection.doc(id)))\n return snapshots.map((snapshot) => {\n const data = (snapshot.exists ? snapshot.data() : null) ?? {}\n const utm: Record<string, Record<string, number>> = {}\n for (const [param, values] of Object.entries((data['utm'] ?? {}) as Record<string, unknown>)) {\n utm[param] = counts(values)\n }\n return {\n total: num(data['total']),\n visitors: num(data['visitors']),\n paths: counts(data['paths']),\n referrers: counts(data['referrers']),\n utm,\n }\n })\n}\n\nfunction sumMaps(days: readonly TrafficDay[], pick: (day: TrafficDay) => Record<string, number>): Map<string, number> {\n const totals = new Map<string, number>()\n for (const day of days) {\n for (const [key, count] of Object.entries(pick(day))) totals.set(key, (totals.get(key) ?? 0) + count)\n }\n return totals\n}\n\nconst ranked = (totals: Map<string, number>): Array<[string, number]> =>\n [...totals.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\nfunction siteReader(\n base: Omit<PluginFigureReader, 'read' | 'scope' | 'plugin'>,\n read: (firestore: Firestore, request: PluginFigureRequest & { hostId: string }) => Promise<PluginFigureRead>,\n firestore: AiFigureFirestore,\n plugin: string | null,\n): PluginFigureReader {\n return {\n ...base,\n scope: 'site',\n plugin,\n read: async (request) => {\n if (!request.hostId) return refused(400, 'Open a site to read its figures')\n if (!base.windows.includes(request.days)) return refused(400, 'That window is not one this reader covers')\n return read(firestore(), { ...request, hostId: request.hostId })\n },\n }\n}\n\nexport function trafficFigureReaders(firestore: AiFigureFirestore): PluginFigureReader[] {\n const source = { label: 'Analytics', path: 'analytics' }\n const visitorsNote =\n 'Visitors are counted once per browser tab per day, so one person on two days counts twice.'\n return [\n siteReader(\n {\n id: 'traffic.summary',\n label: 'Traffic',\n description:\n 'Page views, visitors and views a day on the site over the window, each beside the window before it and the change.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const ids = dayIds(request.now, request.days)\n const [current, previous] = await Promise.all([\n readTrafficDays(db, request.hostId, ids.current),\n readTrafficDays(db, request.hostId, ids.previous),\n ])\n const total = (days: TrafficDay[]) => days.reduce((sum, day) => sum + day.total, 0)\n const visitors = (days: TrafficDay[]) => days.reduce((sum, day) => sum + day.visitors, 0)\n const views = total(current)\n const viewsBefore = total(previous)\n const perDay = Math.round(views / request.days)\n const perDayBefore = Math.round(viewsBefore / request.days)\n const table: PluginFigureTable = {\n title: 'Traffic',\n source,\n period: periodOf(request),\n columns: [\n { key: 'figure', label: 'Figure', kind: 'text' },\n { key: 'current', label: 'This window', kind: 'count' },\n { key: 'previous', label: 'Window before', kind: 'count' },\n { key: 'change', label: 'Change', kind: 'change' },\n ],\n rows: [\n { figure: 'Page views', current: views, previous: viewsBefore, change: pluginFigureChange(views, viewsBefore) },\n {\n figure: 'Visitors',\n current: visitors(current),\n previous: visitors(previous),\n change: pluginFigureChange(visitors(current), visitors(previous)),\n },\n {\n figure: 'Page views a day',\n current: perDay,\n previous: perDayBefore,\n change: pluginFigureChange(perDay, perDayBefore),\n },\n ],\n omitted: 0,\n notes: [\n visitorsNote,\n ...(viewsBefore ? [] : ['The window before this one recorded no page views, so there is no change to compare.']),\n ],\n }\n return { ok: true, table }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.pages',\n label: 'Top pages',\n description: `The site's ${AI_TRAFFIC_TOP_PAGES} most viewed pages over the window, with each page's share of all views and its change from the window before.`,\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const ids = dayIds(request.now, request.days)\n const [current, previous] = await Promise.all([\n readTrafficDays(db, request.hostId, ids.current),\n readTrafficDays(db, request.hostId, ids.previous),\n ])\n const now = ranked(sumMaps(current, (day) => day.paths))\n const before = sumMaps(previous, (day) => day.paths)\n const all = now.reduce((sum, [, count]) => sum + count, 0)\n const rows: PluginFigureRow[] = now.slice(0, AI_TRAFFIC_TOP_PAGES).map(([path, views]) => ({\n page: path,\n views,\n share: share(views, all),\n change: pluginFigureChange(views, before.get(path) ?? null),\n }))\n return {\n ok: true,\n table: {\n title: 'Top pages',\n source,\n period: periodOf(request),\n columns: [\n { key: 'page', label: 'Page', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'share', label: 'Share of views', kind: 'percent' },\n { key: 'change', label: 'Change', kind: 'change' },\n ],\n rows,\n omitted: Math.max(0, now.length - rows.length),\n notes: [],\n },\n }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.sources',\n label: 'Traffic sources',\n description:\n 'Where visits came from over the window: the referring sites, and the campaign sources and campaigns links were tagged with, with each one’s views.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const days = await readTrafficDays(db, request.hostId, dayIds(request.now, request.days).current)\n const all = days.reduce((sum, day) => sum + day.total, 0)\n const referrers = ranked(sumMaps(days, (day) => day.referrers))\n const sources = ranked(sumMaps(days, (day) => day.utm['source'] ?? {}))\n const campaigns = ranked(sumMaps(days, (day) => day.utm['campaign'] ?? {}))\n const rowsOf = (entries: Array<[string, number]>, limit: number, type: string) =>\n entries.slice(0, limit).map(([name, views]) => ({ source: name, type, views, share: share(views, all) }))\n const rows = [\n ...rowsOf(referrers, AI_TRAFFIC_TOP_REFERRERS, 'Referring site'),\n ...rowsOf(sources, AI_TRAFFIC_TOP_CAMPAIGNS, 'Campaign source'),\n ...rowsOf(campaigns, AI_TRAFFIC_TOP_CAMPAIGNS, 'Campaign'),\n ]\n return {\n ok: true,\n table: {\n title: 'Traffic sources',\n source,\n period: periodOf(request),\n columns: [\n { key: 'source', label: 'Source', kind: 'text' },\n { key: 'type', label: 'Kind', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'share', label: 'Share of all views', kind: 'percent' },\n ],\n rows,\n omitted:\n Math.max(0, referrers.length - AI_TRAFFIC_TOP_REFERRERS) +\n Math.max(0, sources.length - AI_TRAFFIC_TOP_CAMPAIGNS) +\n Math.max(0, campaigns.length - AI_TRAFFIC_TOP_CAMPAIGNS),\n notes: ['A visit with no referring site, such as a typed address, is in no row.'],\n },\n }\n },\n firestore,\n null,\n ),\n siteReader(\n {\n id: 'traffic.daily',\n label: 'Traffic by day',\n description: 'Page views and visitors on each day of the window, oldest first, for spotting a day that stood out.',\n windows: AI_TRAFFIC_DAILY_WINDOWS,\n },\n async (db, request) => {\n const ids = [...dayIds(request.now, request.days).current].reverse()\n const days = await readTrafficDays(db, request.hostId, ids)\n return {\n ok: true,\n table: {\n title: 'Traffic by day',\n source,\n period: periodOf(request),\n columns: [\n { key: 'day', label: 'Day', kind: 'date' },\n { key: 'views', label: 'Page views', kind: 'count' },\n { key: 'visitors', label: 'Visitors', kind: 'count' },\n ],\n rows: days.map((day, index) => ({ day: ids[index], views: day.total, visitors: day.visitors })),\n omitted: 0,\n notes: [visitorsNote],\n },\n }\n },\n firestore,\n null,\n ),\n ]\n}\n\n// ── Forms ─────────────────────────────────────────────────────────────────\n\nexport function formsFigureReader(firestore: AiFigureFirestore): PluginFigureReader {\n return siteReader(\n {\n id: 'forms.performance',\n label: 'Forms',\n description:\n 'Each form’s views, submissions, the share of views that became a submission, and leads, over the calendar months the window falls in.',\n windows: AI_TRAFFIC_WINDOWS,\n },\n async (db, request) => {\n const { current } = pluginFigureWindows(request.now, request.days)\n const from = formPeriodKey(current.startMs)\n const to = formPeriodKey(current.endMs - 1)\n const snapshot = await db\n .collection('hosts')\n .doc(request.hostId)\n .collection('forms')\n .limit(AI_FORMS_READ_LIMIT)\n .get()\n const forms = snapshot.docs\n .map((doc) => ({ doc, data: (doc.data() ?? {}) as Record<string, unknown> }))\n .filter(({ data }) => !data['deletedAt'])\n .map(({ doc, data }) => {\n const totals = formStatsTotals(data['stats'] as FormStats | undefined, { from, to })\n return {\n form: str(data['displayName']) || doc.id,\n views: totals.views,\n submissions: totals.submissions,\n completion:\n totals.views && totals.submissions !== null ? share(totals.submissions, totals.views) : null,\n leads: totals.leads,\n }\n })\n .sort((a, b) => (b.submissions ?? -1) - (a.submissions ?? -1) || a.form.localeCompare(b.form))\n const monthStart = from ? `${from}-01` : current.from\n return {\n ok: true,\n table: {\n title: 'Forms',\n source: { label: 'Forms', path: 'forms' },\n period: {\n from: monthStart,\n to: current.to,\n days: Math.round((Date.parse(current.to) - Date.parse(monthStart)) / 86_400_000) + 1,\n },\n columns: [\n { key: 'form', label: 'Form', kind: 'text' },\n { key: 'views', label: 'Views', kind: 'count' },\n { key: 'submissions', label: 'Submissions', kind: 'count' },\n { key: 'completion', label: 'Views that became a submission', kind: 'percent' },\n { key: 'leads', label: 'Leads', kind: 'count' },\n ],\n rows: forms.slice(0, PLUGIN_FIGURE_MAX_ROWS),\n omitted: Math.max(0, forms.length - PLUGIN_FIGURE_MAX_ROWS),\n notes: [\n 'Form figures are kept by calendar month, so they cover whole months from the first day of the month the window starts in.',\n 'Views are counted in the visitor’s browser and submissions on the server, so a rate can pass 100%.',\n ],\n },\n }\n },\n firestore,\n 'forms',\n )\n}\n\n/** Every reader this plugin registers, for the platform's own records. */\nexport function aiFigureReaders(firestore: AiFigureFirestore): PluginFigureReader[] {\n return [...trafficFigureReaders(firestore), formsFigureReader(firestore)]\n}\n\n/**\n * Registers them from the console surface, the one surface that runs insight\n * jobs. By a call, never by an import (AGL-3025).\n */\nexport function registerAiFigureReaders(firestore: AiFigureFirestore): void {\n for (const reader of aiFigureReaders(firestore)) {\n registerPluginFigureReader(reader, { pluginId: AI_PLUGIN_ID })\n }\n}\n"],"names":["formPeriodKey","formStatsTotals","PLUGIN_FIGURE_MAX_ROWS","pluginFigureChange","pluginFigureDay","pluginFigureWindows","registerPluginFigureReader","AI_PLUGIN_ID","AI_TRAFFIC_WINDOWS","AI_TRAFFIC_DAILY_WINDOWS","AI_TRAFFIC_TOP_PAGES","AI_TRAFFIC_TOP_REFERRERS","AI_TRAFFIC_TOP_CAMPAIGNS","AI_FORMS_READ_LIMIT","str","value","trim","num","parsed","Number","isFinite","round1","Math","round","share","part","whole","refused","status","error","ok","periodOf","request","current","now","days","from","to","dayIds","previous","walk","startMs","endMs","ids","at","push","Date","counts","out","key","count","Object","entries","n","readTrafficDays","firestore","hostId","length","collection","doc","snapshots","getAll","map","id","snapshot","data","exists","utm","param","values","total","visitors","paths","referrers","sumMaps","pick","totals","Map","day","set","get","ranked","sort","a","b","localeCompare","siteReader","base","read","plugin","scope","windows","includes","trafficFigureReaders","source","label","path","visitorsNote","description","db","Promise","all","reduce","sum","views","viewsBefore","perDay","perDayBefore","table","title","period","columns","kind","rows","figure","change","omitted","notes","before","slice","page","max","sources","campaigns","rowsOf","limit","type","name","reverse","index","formsFigureReader","forms","docs","filter","form","submissions","completion","leads","monthStart","parse","aiFigureReaders","registerAiFigureReaders","reader","pluginId"],"mappings":"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"}
|