@happyvertical/smrt-fields 0.40.61 → 0.40.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +125 -112
  2. package/dist/chunks/FieldPolicyCollection--RxukfCX.js +1218 -0
  3. package/dist/chunks/FieldPolicyCollection--RxukfCX.js.map +1 -0
  4. package/dist/collections/FieldPolicySuggestionCollection.d.ts +160 -0
  5. package/dist/collections/FieldPolicySuggestionCollection.d.ts.map +1 -0
  6. package/dist/collections/FieldUsageCounterCollection.d.ts +143 -0
  7. package/dist/collections/FieldUsageCounterCollection.d.ts.map +1 -0
  8. package/dist/deterministic-id.d.ts +18 -0
  9. package/dist/deterministic-id.d.ts.map +1 -0
  10. package/dist/field-definitions.d.ts +8 -0
  11. package/dist/field-definitions.d.ts.map +1 -1
  12. package/dist/index.d.ts +8 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1565 -1077
  15. package/dist/index.js.map +1 -1
  16. package/dist/manifest.json +1442 -188
  17. package/dist/models/FieldPolicySuggestion.d.ts +132 -0
  18. package/dist/models/FieldPolicySuggestion.d.ts.map +1 -0
  19. package/dist/models/FieldUsageCounter.d.ts +178 -0
  20. package/dist/models/FieldUsageCounter.d.ts.map +1 -0
  21. package/dist/models/FieldUsageReportReceipt.d.ts +25 -0
  22. package/dist/models/FieldUsageReportReceipt.d.ts.map +1 -0
  23. package/dist/smrt-knowledge.json +402 -5
  24. package/dist/svelte/__tests__/FieldPolicyControlPanel.test.js +54 -0
  25. package/dist/svelte/__tests__/ObjectForm.test.js +131 -0
  26. package/dist/svelte/__tests__/UsageLearning.test.js +140 -0
  27. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte +21 -4
  28. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts +5 -1
  29. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts.map +1 -1
  30. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte +23 -0
  31. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte.d.ts +10 -0
  32. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte.d.ts.map +1 -0
  33. package/dist/svelte/components/FieldPolicyControlPanel.svelte +28 -1
  34. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts +3 -0
  35. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts.map +1 -1
  36. package/dist/svelte/components/FieldPolicyGearButton.svelte +8 -1
  37. package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts.map +1 -1
  38. package/dist/svelte/components/FieldPolicyGearProvider.svelte +34 -0
  39. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts +3 -0
  40. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts.map +1 -1
  41. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte +135 -0
  42. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte.d.ts +11 -0
  43. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte.d.ts.map +1 -0
  44. package/dist/svelte/components/ObjectForm.svelte +91 -2
  45. package/dist/svelte/components/ObjectForm.svelte.d.ts +11 -1
  46. package/dist/svelte/components/ObjectForm.svelte.d.ts.map +1 -1
  47. package/dist/svelte/gear-context.svelte.d.ts +2 -0
  48. package/dist/svelte/gear-context.svelte.d.ts.map +1 -1
  49. package/dist/svelte/index.d.ts +6 -1
  50. package/dist/svelte/index.d.ts.map +1 -1
  51. package/dist/svelte/index.js +4 -1
  52. package/dist/svelte/suggestions.d.ts +38 -0
  53. package/dist/svelte/suggestions.d.ts.map +1 -0
  54. package/dist/svelte/suggestions.js +68 -0
  55. package/dist/svelte/usage-capture.d.ts +63 -0
  56. package/dist/svelte/usage-capture.d.ts.map +1 -0
  57. package/dist/svelte/usage-capture.js +61 -0
  58. package/dist/types.d.ts +40 -0
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/types.js +7 -1
  61. package/dist/types.js.map +1 -1
  62. package/dist/usage-learning.d.ts +201 -0
  63. package/dist/usage-learning.d.ts.map +1 -0
  64. package/dist/usage-schedules.d.ts +110 -0
  65. package/dist/usage-schedules.d.ts.map +1 -0
  66. package/dist/users-module.d.ts +30 -0
  67. package/dist/users-module.d.ts.map +1 -0
  68. package/package.json +8 -8
@@ -0,0 +1,132 @@
1
+ import { SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';
2
+ import { FieldPolicySuggestionData, FieldPolicySuggestionKind, FieldPolicySuggestionStatus } from '../types.js';
3
+ /**
4
+ * `activeKey` sentinel for the single ACTIVE (pending) suggestion per
5
+ * `(objectRef, fieldName, tenantId, kind)`. Settled rows key themselves by id,
6
+ * so history never competes for the slot.
7
+ */
8
+ export declare const ACTIVE_SUGGESTION_KEY = "active";
9
+ export interface FieldPolicySuggestionOptions extends SmrtObjectOptions {
10
+ objectRef?: string;
11
+ fieldName?: string;
12
+ tenantId?: string;
13
+ kind?: FieldPolicySuggestionKind;
14
+ proposedValue?: string | null;
15
+ evidence?: string;
16
+ status?: FieldPolicySuggestionStatus;
17
+ cooldownUntil?: Date | null;
18
+ decidedBy?: string | null;
19
+ decidedAt?: Date | null;
20
+ }
21
+ /**
22
+ * A pending, human-reviewable field-policy improvement proposed from real
23
+ * usage (epic #2045, issue #2051): promote a field to the `basic` tier, or
24
+ * seed an org default with the dominant observed value.
25
+ *
26
+ * Suggestion-first by design — a row DOES NOTHING until a
27
+ * `fields.policy.manage` holder accepts it, and acceptance writes the
28
+ * org-scope {@link ../models/FieldPolicy.FieldPolicy} row through NORMAL
29
+ * validation (registry check, type check, security rail, required-field
30
+ * invariant, ownership + permission split). Dismissing sets a cool-down that
31
+ * suppresses regeneration of the same suggestion.
32
+ *
33
+ * **One ACTIVE suggestion per identity, structurally** (not merely by a
34
+ * check-then-insert): {@link activeKey} is a computed column holding the
35
+ * sentinel {@link ACTIVE_SUGGESTION_KEY} while the row is `pending` and the
36
+ * row's own id once it settles, and it participates in `conflictColumns`. The
37
+ * unique index therefore admits at most ONE pending row per
38
+ * `(objectRef, fieldName, tenantId, kind)` while every settled row keys
39
+ * itself — so two overlapping generation runs (e.g. a global and a
40
+ * tenant-specific schedule) UPSERT onto the same row instead of duplicating,
41
+ * with no transaction spanning their reads. It is the `FieldPolicy.scopeKey`
42
+ * trick applied to a lifecycle slot. On settle the column flips to the row's
43
+ * id, which frees the slot for a post-cool-down regeneration while keeping the
44
+ * dismissed/accepted history (and its id) intact — core conflicts a persisted
45
+ * row on its primary key (#1472), so the flip is a plain UPDATE.
46
+ *
47
+ * #1885 seam: this substrate is fully independent of personas'
48
+ * `DirectiveProposal` review queue (no shared producer discriminator —
49
+ * deliberately out of scope). Tenant learning agents MAY create
50
+ * FieldPolicySuggestion rows through this model's normal validation; the
51
+ * reviewed `fields.policy.manage` acceptance gate is unchanged by who
52
+ * proposed.
53
+ */
54
+ export declare class FieldPolicySuggestion extends SmrtObject {
55
+ /** Qualified class name of the target object (`@package/name:ClassName`). */
56
+ objectRef: string;
57
+ /** Field name on the target object (validated against the registry). */
58
+ fieldName: string;
59
+ /** Owning tenant (required — suggestions always target one org). */
60
+ tenantId?: string;
61
+ /** What the suggestion proposes ('promote' | 'default'). */
62
+ kind: FieldPolicySuggestionKind;
63
+ /**
64
+ * JSON-encoded proposed default (`kind: 'default'` only) — the exact
65
+ * encoding `FieldPolicy.defaultValue` stores, so acceptance passes it
66
+ * through unchanged. NULL for `promote`.
67
+ */
68
+ proposedValue: string | null;
69
+ /**
70
+ * Human-readable evidence as a JSON string: a `summary` sentence plus the
71
+ * structured window/threshold numbers behind it (see
72
+ * `buildFieldUsageEvidence`).
73
+ */
74
+ evidence: string;
75
+ /** Lifecycle status ('pending' | 'accepted' | 'dismissed'). */
76
+ status: FieldPolicySuggestionStatus;
77
+ /**
78
+ * Computed lifecycle-slot key, set in `save()`: {@link ACTIVE_SUGGESTION_KEY}
79
+ * while `pending`, else the row's own id. It exists ONLY to make the
80
+ * `conflictColumns` unique index express "at most one ACTIVE suggestion per
81
+ * identity, unlimited settled history" (the `FieldPolicy.scopeKey`
82
+ * precedent) — never read it for logic; `status` owns that.
83
+ */
84
+ activeKey: string;
85
+ /**
86
+ * Until this instant, a dismissed suggestion suppresses regeneration of the
87
+ * same `(objectRef, fieldName, tenantId, kind)` suggestion. NULL until
88
+ * dismissed.
89
+ */
90
+ cooldownUntil: Date | null;
91
+ /** Who accepted/dismissed (audit attribution, #2050); not validated. */
92
+ decidedBy: string | null;
93
+ /** When the suggestion was accepted/dismissed. */
94
+ decidedAt: Date | null;
95
+ constructor(options?: FieldPolicySuggestionOptions);
96
+ /** Parse the stored evidence object (guarded; junk parses as empty). */
97
+ getEvidence(): Record<string, unknown>;
98
+ /** Serialize an evidence object into the stored JSON string. */
99
+ setEvidence(evidence: Record<string, unknown>): void;
100
+ /** Parse the proposed value; `undefined` when none is stored. */
101
+ getProposedValue(): unknown;
102
+ /** Serialized row shape for the collection actions. */
103
+ toSuggestionData(): FieldPolicySuggestionData;
104
+ save(): Promise<this>;
105
+ /**
106
+ * Recompute the lifecycle-slot key: the shared sentinel while pending (so
107
+ * the unique index admits exactly one), the row's own id once settled (so
108
+ * history never competes for the slot and the freed slot allows a
109
+ * post-cool-down regeneration). A settled row that has not been persisted
110
+ * yet is assigned its id here — the key must be unique from the first write.
111
+ */
112
+ private applyActiveKey;
113
+ delete(): Promise<void>;
114
+ /**
115
+ * The "normal validation" the #1885 seam promises producers: registry-known
116
+ * field, policy-addressable, never sensitive/read-permission-gated/transient
117
+ * (those fields are count-only in usage data and get no suggestions), valid
118
+ * kind/status, and for `default` suggestions a JSON proposed value that
119
+ * type-checks against the manifest field type.
120
+ */
121
+ private validateFieldPolicySuggestion;
122
+ /**
123
+ * Tenant write boundary (the FieldPolicy posture): inside a non-bypass
124
+ * tenant context a caller may only touch its own tenant's suggestions —
125
+ * checked against BOTH the in-memory scope and, for persisted rows, the
126
+ * PERSISTED tenant (a foreign row cannot be re-scoped into the caller's
127
+ * tenant). Trusted execution (no context / bypass) is exempt — that is what
128
+ * lets the scheduled generation job and platform flows operate.
129
+ */
130
+ private assertRowOwnedByAmbientContext;
131
+ }
132
+ //# sourceMappingURL=FieldPolicySuggestion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldPolicySuggestion.d.ts","sourceRoot":"","sources":["../../src/models/FieldPolicySuggestion.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAclC,OAAO,EAGL,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAE9C,MAAM,WAAW,4BAA6B,SAAQ,iBAAiB;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,yBAAyB,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAOH,qBAaa,qBAAsB,SAAQ,UAAU;IACnD,6EAA6E;IAE7E,SAAS,EAAE,MAAM,CAAM;IAEvB,wEAAwE;IAExE,SAAS,EAAE,MAAM,CAAM;IAEvB,oEAAoE;IAEpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,4DAA4D;IAE5D,IAAI,EAAE,yBAAyB,CAAa;IAE5C;;;;OAIG;IAEH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEpC;;;;OAIG;IAEH,QAAQ,EAAE,MAAM,CAAQ;IAExB,+DAA+D;IAE/D,MAAM,EAAE,2BAA2B,CAAa;IAEhD;;;;;;OAMG;IAEH,SAAS,EAAE,MAAM,CAAyB;IAE1C;;;;OAIG;IAEH,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,wEAAwE;IAExE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhC,kDAAkD;IAElD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;gBAElB,OAAO,GAAE,4BAAiC;IAkBtD,wEAAwE;IACxE,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAWtC,gEAAgE;IAChE,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIpD,iEAAiE;IACjE,gBAAgB,IAAI,OAAO;IAW3B,uDAAuD;IACvD,gBAAgB,IAAI,yBAAyB;IAgB9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAWP,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtC;;;;;;OAMG;YACW,6BAA6B;IAiF3C;;;;;;;OAOG;YACW,8BAA8B;CAyC7C"}
@@ -0,0 +1,178 @@
1
+ import { SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';
2
+ /**
3
+ * Cap on distinct-user ids stored per bucket. For threshold questions
4
+ * ("did at least N distinct users set this field?") the capped set is EXACT up
5
+ * to the cap; once overflowed, {@link FieldUsageCounter.distinctUserCount} is
6
+ * an honest LOWER BOUND that trivially satisfies any threshold ≤ the cap.
7
+ */
8
+ export declare const MAX_DISTINCT_USERS_PER_BUCKET = 100;
9
+ /** Cap on histogram buckets per counter row (bounded storage). */
10
+ export declare const MAX_VALUE_HISTOGRAM_BUCKETS = 25;
11
+ /** Longest histogram key recorded; longer samples are skipped (count-only). */
12
+ export declare const MAX_VALUE_HISTOGRAM_KEY_LENGTH = 64;
13
+ /** `period` bucket format: UTC calendar day. */
14
+ export declare const FIELD_USAGE_PERIOD_PATTERN: RegExp;
15
+ /** The UTC day bucket for a timestamp (`YYYY-MM-DD`). */
16
+ export declare function fieldUsagePeriodForDate(date: Date): string;
17
+ /**
18
+ * A prototype-free histogram map.
19
+ *
20
+ * Histogram keys are user-supplied values (an `idType: 'text'` reference id may
21
+ * legitimately be `constructor`, `toString`, or `__proto__`). On a plain object
22
+ * those either resolve to inherited members — making an absent bucket look
23
+ * present and corrupting its count — or, for `__proto__`, invoke the prototype
24
+ * setter instead of creating an own key. A null-prototype object has no such
25
+ * members, so every key behaves like data. Use this everywhere histogram counts
26
+ * are accumulated (storage AND merge paths).
27
+ */
28
+ export declare function emptyHistogram(): Record<string, number>;
29
+ export interface FieldUsageCounterOptions extends SmrtObjectOptions {
30
+ objectRef?: string;
31
+ fieldName?: string;
32
+ tenantId?: string;
33
+ period?: string;
34
+ submissionCount?: number;
35
+ setCount?: number;
36
+ distinctUserCount?: number;
37
+ distinctUserIds?: string;
38
+ distinctUsersOverflowed?: boolean;
39
+ valueHistogram?: string | null;
40
+ valueHistogramOverflowed?: boolean;
41
+ }
42
+ /**
43
+ * Period-bucketed field usage counter (epic #2045, issue #2051).
44
+ *
45
+ * One row aggregates field submissions for a single
46
+ * `(objectRef, fieldName, tenantId, period)` — the substrate the
47
+ * suggestion-generation job reads. Counters are deliberately APPROXIMATE:
48
+ * ingestion is fire-and-forget and concurrent bucket merges may lose an
49
+ * increment (read-modify-write), which is acceptable for usage statistics and
50
+ * documented here rather than papered over.
51
+ *
52
+ * TWO counters, because they answer different questions:
53
+ * - {@link submissionCount} — EVERY observed submission of the field
54
+ * (default-matching or not). It is the denominator for value dominance:
55
+ * "N% of submissions used value V". Without it, a value seen only in
56
+ * deviations would look 100% dominant even against thousands of
57
+ * default-valued submissions.
58
+ * - {@link setCount} — submissions whose value DIFFERED from the resolved
59
+ * default (server-derived). It plus {@link distinctUserIds} is the
60
+ * promote signal ("real users are actively filling this in").
61
+ *
62
+ * Content rails (enforced by the ingestion action, which derives everything
63
+ * from the live registry and never trusts the client):
64
+ * - Sensitive and read-permission-gated fields are COUNT-ONLY: their raw
65
+ * values are never recorded anywhere in usage data — not even for
66
+ * default-matching submissions.
67
+ * - Value histograms exist only for low-cardinality field types (`boolean`,
68
+ * `foreignKey`, `crossPackageRef`) — never free text, even non-sensitive
69
+ * text (PII risk) — with a bounded bucket count and key length. They cover
70
+ * ALL submissions (not just deviations) so the dominance ratio is a true
71
+ * fraction of {@link submissionCount}.
72
+ * - `distinctUserIds` is a capped set with an overflow marker (see
73
+ * {@link MAX_DISTINCT_USERS_PER_BUCKET} for the honesty contract).
74
+ */
75
+ export declare class FieldUsageCounter extends SmrtObject {
76
+ /** Qualified class name of the target object (`@package/name:ClassName`). */
77
+ objectRef: string;
78
+ /** Field name on the target object. */
79
+ fieldName: string;
80
+ /**
81
+ * Owning tenant. REQUIRED: ingestion fails closed without an ambient tenant
82
+ * context, so every row is attributable (and the conflict-column tuple
83
+ * stays total). Native UUID on PostgreSQL/DuckDB.
84
+ */
85
+ tenantId?: string;
86
+ /** UTC day bucket (`YYYY-MM-DD`); lexicographic order is time order. */
87
+ period: string;
88
+ /**
89
+ * EVERY observed submission of this field in the bucket, whether or not the
90
+ * value matched the resolved default — the dominance denominator.
91
+ *
92
+ * Rows written before this column existed carry `0` while `setCount > 0`;
93
+ * {@link isLegacyBucket} detects that shape and the generation job then
94
+ * treats the total as UNKNOWN and skips `default` suggestions for the group
95
+ * (promote, which needs no denominator, still works).
96
+ */
97
+ submissionCount: number;
98
+ /**
99
+ * Submissions whose value DIFFERED from the server-resolved default (the
100
+ * promote signal). Always `<= submissionCount` on rows written by the
101
+ * current ingestion path.
102
+ */
103
+ setCount: number;
104
+ /**
105
+ * Size of the stored distinct-user set. When
106
+ * {@link distinctUsersOverflowed} is true this is a LOWER BOUND (the set is
107
+ * capped), never an estimate.
108
+ */
109
+ distinctUserCount: number;
110
+ /** JSON array of distinct user ids, capped (see the class doc). */
111
+ distinctUserIds: string;
112
+ /** True once a distinct user was NOT added because the set is at its cap. */
113
+ distinctUsersOverflowed: boolean;
114
+ /**
115
+ * JSON object `serializedValue -> count` for histogram-eligible fields;
116
+ * NULL when the field is count-only. Keys are bounded in number and length.
117
+ */
118
+ valueHistogram: string | null;
119
+ /** True once a sample was dropped because the bucket cap was reached. */
120
+ valueHistogramOverflowed: boolean;
121
+ constructor(options?: FieldUsageCounterOptions);
122
+ /** Parse the stored distinct-user set (guarded; junk parses as empty). */
123
+ getDistinctUserIds(): string[];
124
+ /**
125
+ * Add a user to the distinct set, honoring the cap. At the cap the id is
126
+ * NOT added and the overflow marker is set instead, keeping
127
+ * {@link distinctUserCount} an honest lower bound.
128
+ */
129
+ addDistinctUser(userId: string): void;
130
+ /**
131
+ * Parse the stored histogram (guarded; junk parses as empty).
132
+ *
133
+ * Returns a NULL-PROTOTYPE object. Histogram keys are user-supplied ids —
134
+ * an `idType: 'text'` reference may legitimately be `constructor`,
135
+ * `toString`, or `__proto__` — and on a plain object those inherit truthy
136
+ * prototype values (so a missing bucket reads as present) or, for
137
+ * `__proto__`, hit the prototype setter instead of creating an own key.
138
+ * Both would silently corrupt counts. See {@link emptyHistogram}.
139
+ */
140
+ getValueHistogram(): Record<string, number>;
141
+ /**
142
+ * Record one histogram sample under an already-serialized key, honoring the
143
+ * bucket cap (a NEW key past the cap is dropped and the overflow marker
144
+ * set; existing keys keep counting).
145
+ *
146
+ * Bucket presence is an OWN-key test, never a truthiness/`undefined` read,
147
+ * so prototype-shaped ids behave like any other key.
148
+ */
149
+ recordHistogramSample(key: string): void;
150
+ /**
151
+ * Whether this bucket predates the {@link submissionCount} column (or was
152
+ * corrupted): it records deviations without a total, so no honest dominance
153
+ * ratio can be computed from it. The generation job skips `default`
154
+ * suggestions for any group containing such a bucket.
155
+ */
156
+ isLegacyBucket(): boolean;
157
+ save(): Promise<this>;
158
+ delete(): Promise<void>;
159
+ private validateFieldUsageCounter;
160
+ /**
161
+ * Tenant write boundary (the FieldPolicy posture — no class-level
162
+ * `@TenantScoped`, because the learning jobs legitimately operate
163
+ * cross-tenant in trusted execution): inside a non-bypass tenant context a
164
+ * caller may only touch rows of its own tenant; without a context (system/
165
+ * job execution) writes are trusted.
166
+ *
167
+ * Checked against BOTH the in-memory tenant and — for a row that already
168
+ * exists — the PERSISTED one. The persisted check is what makes the boundary
169
+ * real: bucket ids are deterministic and the deriving helper is exported, so
170
+ * a foreign row is trivially addressable, and an in-memory-only check would
171
+ * let a caller load it, re-stamp `tenantId` with its own, and adopt or delete
172
+ * another tenant's counters (the #2047 FieldPolicy pattern).
173
+ */
174
+ private assertRowOwnedByAmbientContext;
175
+ /** The stored tenant for this row's id; `null` when it is not persisted. */
176
+ private getPersistedTenantId;
177
+ }
178
+ //# sourceMappingURL=FieldUsageCounter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldUsageCounter.d.ts","sourceRoot":"","sources":["../../src/models/FieldUsageCounter.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAQlC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,kEAAkE;AAClE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,+EAA+E;AAC/E,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,QAAwB,CAAC;AAEhE,yDAAyD;AACzD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE1D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEvD;AAED,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAMH,qBAOa,iBAAkB,SAAQ,UAAU;IAC/C,6EAA6E;IAE7E,SAAS,EAAE,MAAM,CAAM;IAEvB,uCAAuC;IAEvC,SAAS,EAAE,MAAM,CAAM;IAEvB;;;;OAIG;IAEH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,wEAAwE;IAExE,MAAM,EAAE,MAAM,CAAM;IAEpB;;;;;;;;OAQG;IAEH,eAAe,EAAE,MAAM,CAAK;IAE5B;;;;OAIG;IAEH,QAAQ,EAAE,MAAM,CAAK;IAErB;;;;OAIG;IAEH,iBAAiB,EAAE,MAAM,CAAK;IAE9B,mEAAmE;IAEnE,eAAe,EAAE,MAAM,CAAQ;IAE/B,6EAA6E;IAE7E,uBAAuB,EAAE,OAAO,CAAS;IAEzC;;;OAGG;IAEH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC,yEAAyE;IAEzE,wBAAwB,EAAE,OAAO,CAAS;gBAE9B,OAAO,GAAE,wBAA6B;IA2BlD,0EAA0E;IAC1E,kBAAkB,IAAI,MAAM,EAAE;IAW9B;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAcrC;;;;;;;;;OASG;IACH,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAwB3C;;;;;;;OAOG;IACH,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAiBxC;;;;;OAKG;IACH,cAAc,IAAI,OAAO;IAIV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtC,OAAO,CAAC,yBAAyB;IAoCjC;;;;;;;;;;;;;OAaG;YACW,8BAA8B;IAgC5C,4EAA4E;YAC9D,oBAAoB;CAYnC"}
@@ -0,0 +1,25 @@
1
+ import { SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';
2
+ /**
3
+ * Durable daily receipt for one member's contribution to one field.
4
+ *
5
+ * The counter action creates this before incrementing its aggregate. Its
6
+ * natural key makes the anti-inflation rule durable across requests and
7
+ * replicas: one `(tenant, user, object, field, UTC day)` sample may affect
8
+ * usage evidence. Receipts intentionally retain no submitted value.
9
+ */
10
+ export declare class FieldUsageReportReceipt extends SmrtObject {
11
+ tenantId?: string;
12
+ userId: string;
13
+ objectRef: string;
14
+ fieldName: string;
15
+ period: string;
16
+ constructor(options?: FieldUsageReportReceiptOptions);
17
+ }
18
+ export interface FieldUsageReportReceiptOptions extends SmrtObjectOptions {
19
+ tenantId?: string;
20
+ userId?: string;
21
+ objectRef?: string;
22
+ fieldName?: string;
23
+ period?: string;
24
+ }
25
+ //# sourceMappingURL=FieldUsageReportReceipt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldUsageReportReceipt.d.ts","sourceRoot":"","sources":["../../src/models/FieldUsageReportReceipt.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAGlC;;;;;;;GAOG;AACH,qBAaa,uBAAwB,SAAQ,UAAU;IAErD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,MAAM,EAAE,MAAM,CAAM;IAGpB,SAAS,EAAE,MAAM,CAAM;IAGvB,SAAS,EAAE,MAAM,CAAM;IAGvB,MAAM,EAAE,MAAM,CAAM;gBAER,OAAO,GAAE,8BAAmC;CAQzD;AAED,MAAM,WAAW,8BAA+B,SAAQ,iBAAiB;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}