@beezping/adapter-memory 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ export { EMAIL_PATTERN, isValidEmail } from "./email.cjs";
2
+ export type { SitepingErrorCode } from "./errors.cjs";
3
+ export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.cjs";
4
+ export type { FilterResult, Pagination } from "./filters.cjs";
5
+ export { applyFeedbackFilters, clampPagination, DEFAULT_PAGE_LIMIT, isUnreachableOffset, MAX_PAGE_LIMIT, } from "./filters.cjs";
6
+ export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.cjs";
7
+ export { createI18n, interpolate, tWithParams } from "./i18n.cjs";
8
+ export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.cjs";
9
+ export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.cjs";
10
+ export type { ScreenshotStorage } from "./screenshot-storage.cjs";
11
+ export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.cjs";
12
+ export { buildAnnotationRecord, buildFeedbackRecord, createCollectionStore } from "./store-helpers.cjs";
13
+ export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.cjs";
14
+ export { hasOwn, isRecord } from "./type-utils.cjs";
15
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackPage, FeedbackPayload, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingRequestCredentials, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.cjs";
16
+ export { BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, CONSOLE_DIAGNOSTIC_LEVELS, DEFAULT_REQUEST_CREDENTIALS, describeInvalidRequestCredentials, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isClosedStatus, isRequestCredentials, isStoreDuplicate, isStoreNotFound, isStorePersistence, OPEN_FEEDBACK_STATUSES, REQUEST_CREDENTIALS_MODES, StoreDuplicateError, StoreNotFoundError, StorePersistenceError, toFeedbackUpdate, } from "./types.cjs";
17
+ export { errorFromResponse, feedbackQueryToSearchParams, networkErrorFromException } from "./wire.cjs";
@@ -0,0 +1,17 @@
1
+ export { EMAIL_PATTERN, isValidEmail } from "./email.js";
2
+ export type { SitepingErrorCode } from "./errors.js";
3
+ export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.js";
4
+ export type { FilterResult, Pagination } from "./filters.js";
5
+ export { applyFeedbackFilters, clampPagination, DEFAULT_PAGE_LIMIT, isUnreachableOffset, MAX_PAGE_LIMIT, } from "./filters.js";
6
+ export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.js";
7
+ export { createI18n, interpolate, tWithParams } from "./i18n.js";
8
+ export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.js";
9
+ export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.js";
10
+ export type { ScreenshotStorage } from "./screenshot-storage.js";
11
+ export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.js";
12
+ export { buildAnnotationRecord, buildFeedbackRecord, createCollectionStore } from "./store-helpers.js";
13
+ export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.js";
14
+ export { hasOwn, isRecord } from "./type-utils.js";
15
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackPage, FeedbackPayload, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingRequestCredentials, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.js";
16
+ export { BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, CONSOLE_DIAGNOSTIC_LEVELS, DEFAULT_REQUEST_CREDENTIALS, describeInvalidRequestCredentials, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isClosedStatus, isRequestCredentials, isStoreDuplicate, isStoreNotFound, isStorePersistence, OPEN_FEEDBACK_STATUSES, REQUEST_CREDENTIALS_MODES, StoreDuplicateError, StoreNotFoundError, StorePersistenceError, toFeedbackUpdate, } from "./types.js";
17
+ export { errorFromResponse, feedbackQueryToSearchParams, networkErrorFromException } from "./wire.js";
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Record-construction helpers and the collection-store engine.
3
+ *
4
+ * Every snapshot-style adapter (memory, localStorage, flat file, KV, …)
5
+ * needs the same three ingredients: turn a `FeedbackCreateInput` into a
6
+ * `FeedbackRecord` (null-normalizing optional fields, stamping ids and
7
+ * timestamps), filter/paginate with `applyFeedbackFilters`, and implement
8
+ * the dedup/update/delete choreography of the `SitepingStore` contract.
9
+ *
10
+ * `buildFeedbackRecord` / `buildAnnotationRecord` cover the first part for
11
+ * any adapter. `createCollectionStore` covers all of it: give it `load`,
12
+ * `persist`, and `generateId`, and it returns a fully conformant
13
+ * `SitepingStore` — writing a new snapshot adapter is ~20 lines plus its
14
+ * storage specifics.
15
+ */
16
+ import type { AnnotationCreateInput, AnnotationRecord, FeedbackCreateInput, FeedbackRecord, SitepingStore } from "./types.cjs";
17
+ /**
18
+ * Build a persisted `AnnotationRecord` from its create input — normalizes
19
+ * the optional anchor fields to `null` and stamps identity/timestamp.
20
+ */
21
+ export declare function buildAnnotationRecord(input: AnnotationCreateInput, ctx: {
22
+ id: string;
23
+ feedbackId: string;
24
+ now: Date;
25
+ }): AnnotationRecord;
26
+ /**
27
+ * Build a persisted `FeedbackRecord` (with its annotations) from a create
28
+ * input — normalizes every optional field to `null` and stamps ids and
29
+ * timestamps. Adapters without external screenshot storage keep the data
30
+ * URL inline on `screenshotUrl`, which is what this helper does; adapters
31
+ * with a `ScreenshotStorage` upload first and override `screenshotUrl`.
32
+ */
33
+ export declare function buildFeedbackRecord(input: FeedbackCreateInput, ctx: {
34
+ id: string;
35
+ annotationId: () => string;
36
+ now?: Date;
37
+ }): FeedbackRecord;
38
+ /**
39
+ * Storage primitives behind a collection store. `load`/`persist` may be
40
+ * sync or async — the engine awaits both, so in-memory arrays, localStorage
41
+ * and async KV stores all fit the same three functions.
42
+ */
43
+ export interface CollectionStoreBackend {
44
+ /**
45
+ * Return the current full snapshot of feedback records. The engine never
46
+ * mutates this array — handing out a live cache is safe.
47
+ */
48
+ load(): FeedbackRecord[] | Promise<FeedbackRecord[]>;
49
+ /**
50
+ * Persist the full snapshot — always a new array, never the one `load()`
51
+ * returned. Throw `StorePersistenceError` when the write is lost (quota,
52
+ * storage disabled, …) — never swallow the failure. Because the loaded
53
+ * snapshot is left untouched, a throw here leaves a cached `load()` result
54
+ * consistent with durable storage: no phantom record, no half-applied
55
+ * update.
56
+ */
57
+ persist(feedbacks: FeedbackRecord[]): void | Promise<void>;
58
+ /** Generate a unique id for a new feedback or annotation record. */
59
+ generateId(): string;
60
+ }
61
+ /**
62
+ * A `SitepingStore` with the optional `verifyProjectOwnership` and
63
+ * `createFeedbackIfAbsent` guaranteed — what `createCollectionStore` returns.
64
+ */
65
+ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "verifyProjectOwnership" | "createFeedbackIfAbsent">>;
66
+ /**
67
+ * Build a fully conformant `SitepingStore` on top of a snapshot backend.
68
+ *
69
+ * The engine implements the whole store contract: clientId dedup (idempotent
70
+ * create, with `createFeedbackIfAbsent` reporting inserts), newest-first
71
+ * ordering, the standard filter/pagination pipeline,
72
+ * `StoreNotFoundError` on missing update/delete, project-scoped bulk delete,
73
+ * and `verifyProjectOwnership`. The snapshot returned by `load` is never
74
+ * mutated: every write hands `persist` a new array, so a failed write leaves
75
+ * a cached snapshot exactly as it was. When `persist` fails during `createFeedback`
76
+ * and the record carries an inline screenshot, the engine retries once
77
+ * without the screenshot (by far the heaviest field) so the text feedback
78
+ * survives a storage-quota hit; if that also fails, the error propagates —
79
+ * returning the record would claim a success that was never persisted.
80
+ *
81
+ * Every read-modify-write mutation (`createFeedbackIfAbsent`,
82
+ * `createFeedback`, `updateFeedback`, `deleteFeedback`,
83
+ * `deleteAllFeedbacks`) runs through one promise queue per returned store,
84
+ * so its `load` → check → `persist` sequence never interleaves with another
85
+ * mutation of the same instance. That makes `createFeedbackIfAbsent` report
86
+ * `created: true` exactly once per `clientId` and prevents concurrent writes
87
+ * from overwriting each other's snapshot. The guarantee is scoped to one
88
+ * store instance in one JS process: two instances over the same storage (two
89
+ * server processes on a shared file, two browser tabs on the same
90
+ * localStorage key) are not coordinated — backends that need that must
91
+ * provide their own atomic primitive (a unique constraint, a transaction, a
92
+ * compare-and-set). A failed mutation rejects with its original error and
93
+ * does not block the ones queued after it. Reads are not queued: they see the
94
+ * last persisted snapshot.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * export class MemoryStore implements SitepingStore {
99
+ * private feedbacks: FeedbackRecord[] = [];
100
+ * private readonly store = createCollectionStore({
101
+ * load: () => this.feedbacks,
102
+ * persist: (next) => {
103
+ * this.feedbacks = next;
104
+ * },
105
+ * generateId: () => crypto.randomUUID(),
106
+ * });
107
+ * createFeedback = this.store.createFeedback;
108
+ * // …delegate the remaining methods the same way
109
+ * }
110
+ * ```
111
+ */
112
+ export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Record-construction helpers and the collection-store engine.
3
+ *
4
+ * Every snapshot-style adapter (memory, localStorage, flat file, KV, …)
5
+ * needs the same three ingredients: turn a `FeedbackCreateInput` into a
6
+ * `FeedbackRecord` (null-normalizing optional fields, stamping ids and
7
+ * timestamps), filter/paginate with `applyFeedbackFilters`, and implement
8
+ * the dedup/update/delete choreography of the `SitepingStore` contract.
9
+ *
10
+ * `buildFeedbackRecord` / `buildAnnotationRecord` cover the first part for
11
+ * any adapter. `createCollectionStore` covers all of it: give it `load`,
12
+ * `persist`, and `generateId`, and it returns a fully conformant
13
+ * `SitepingStore` — writing a new snapshot adapter is ~20 lines plus its
14
+ * storage specifics.
15
+ */
16
+ import type { AnnotationCreateInput, AnnotationRecord, FeedbackCreateInput, FeedbackRecord, SitepingStore } from "./types.js";
17
+ /**
18
+ * Build a persisted `AnnotationRecord` from its create input — normalizes
19
+ * the optional anchor fields to `null` and stamps identity/timestamp.
20
+ */
21
+ export declare function buildAnnotationRecord(input: AnnotationCreateInput, ctx: {
22
+ id: string;
23
+ feedbackId: string;
24
+ now: Date;
25
+ }): AnnotationRecord;
26
+ /**
27
+ * Build a persisted `FeedbackRecord` (with its annotations) from a create
28
+ * input — normalizes every optional field to `null` and stamps ids and
29
+ * timestamps. Adapters without external screenshot storage keep the data
30
+ * URL inline on `screenshotUrl`, which is what this helper does; adapters
31
+ * with a `ScreenshotStorage` upload first and override `screenshotUrl`.
32
+ */
33
+ export declare function buildFeedbackRecord(input: FeedbackCreateInput, ctx: {
34
+ id: string;
35
+ annotationId: () => string;
36
+ now?: Date;
37
+ }): FeedbackRecord;
38
+ /**
39
+ * Storage primitives behind a collection store. `load`/`persist` may be
40
+ * sync or async — the engine awaits both, so in-memory arrays, localStorage
41
+ * and async KV stores all fit the same three functions.
42
+ */
43
+ export interface CollectionStoreBackend {
44
+ /**
45
+ * Return the current full snapshot of feedback records. The engine never
46
+ * mutates this array — handing out a live cache is safe.
47
+ */
48
+ load(): FeedbackRecord[] | Promise<FeedbackRecord[]>;
49
+ /**
50
+ * Persist the full snapshot — always a new array, never the one `load()`
51
+ * returned. Throw `StorePersistenceError` when the write is lost (quota,
52
+ * storage disabled, …) — never swallow the failure. Because the loaded
53
+ * snapshot is left untouched, a throw here leaves a cached `load()` result
54
+ * consistent with durable storage: no phantom record, no half-applied
55
+ * update.
56
+ */
57
+ persist(feedbacks: FeedbackRecord[]): void | Promise<void>;
58
+ /** Generate a unique id for a new feedback or annotation record. */
59
+ generateId(): string;
60
+ }
61
+ /**
62
+ * A `SitepingStore` with the optional `verifyProjectOwnership` and
63
+ * `createFeedbackIfAbsent` guaranteed — what `createCollectionStore` returns.
64
+ */
65
+ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "verifyProjectOwnership" | "createFeedbackIfAbsent">>;
66
+ /**
67
+ * Build a fully conformant `SitepingStore` on top of a snapshot backend.
68
+ *
69
+ * The engine implements the whole store contract: clientId dedup (idempotent
70
+ * create, with `createFeedbackIfAbsent` reporting inserts), newest-first
71
+ * ordering, the standard filter/pagination pipeline,
72
+ * `StoreNotFoundError` on missing update/delete, project-scoped bulk delete,
73
+ * and `verifyProjectOwnership`. The snapshot returned by `load` is never
74
+ * mutated: every write hands `persist` a new array, so a failed write leaves
75
+ * a cached snapshot exactly as it was. When `persist` fails during `createFeedback`
76
+ * and the record carries an inline screenshot, the engine retries once
77
+ * without the screenshot (by far the heaviest field) so the text feedback
78
+ * survives a storage-quota hit; if that also fails, the error propagates —
79
+ * returning the record would claim a success that was never persisted.
80
+ *
81
+ * Every read-modify-write mutation (`createFeedbackIfAbsent`,
82
+ * `createFeedback`, `updateFeedback`, `deleteFeedback`,
83
+ * `deleteAllFeedbacks`) runs through one promise queue per returned store,
84
+ * so its `load` → check → `persist` sequence never interleaves with another
85
+ * mutation of the same instance. That makes `createFeedbackIfAbsent` report
86
+ * `created: true` exactly once per `clientId` and prevents concurrent writes
87
+ * from overwriting each other's snapshot. The guarantee is scoped to one
88
+ * store instance in one JS process: two instances over the same storage (two
89
+ * server processes on a shared file, two browser tabs on the same
90
+ * localStorage key) are not coordinated — backends that need that must
91
+ * provide their own atomic primitive (a unique constraint, a transaction, a
92
+ * compare-and-set). A failed mutation rejects with its original error and
93
+ * does not block the ones queued after it. Reads are not queued: they see the
94
+ * last persisted snapshot.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * export class MemoryStore implements SitepingStore {
99
+ * private feedbacks: FeedbackRecord[] = [];
100
+ * private readonly store = createCollectionStore({
101
+ * load: () => this.feedbacks,
102
+ * persist: (next) => {
103
+ * this.feedbacks = next;
104
+ * },
105
+ * generateId: () => crypto.randomUUID(),
106
+ * });
107
+ * createFeedback = this.store.createFeedback;
108
+ * // …delegate the remaining methods the same way
109
+ * }
110
+ * ```
111
+ */
112
+ export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * General-purpose TypeScript utility types used across `@beezping/*`.
3
+ *
4
+ * These are kept dependency-free and re-exported from the package entry
5
+ * so adapters and integrators can rely on the same primitives the core
6
+ * uses internally.
7
+ */
8
+ /**
9
+ * Force TypeScript to expand a computed type into a flat object literal in
10
+ * tooltips and error messages. Purely cosmetic — same structural type, just
11
+ * easier to read.
12
+ *
13
+ * @example
14
+ * type Raw = Omit<FeedbackRecord, "annotations"> & { annotations: number };
15
+ * type Pretty = Prettify<Raw>; // displayed as a flat object
16
+ */
17
+ export type Prettify<T> = {
18
+ [K in keyof T]: T[K];
19
+ } & {};
20
+ /**
21
+ * Returns `Y` when `A` is exactly assignable to `B` and vice-versa,
22
+ * otherwise `N`. Powers compile-time equality assertions.
23
+ */
24
+ export type IfEquals<A, B, Y = true, N = false> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
25
+ /**
26
+ * Compile-time exact-type guard — resolves to `true` when `Actual` and
27
+ * `Expected` are identical, `never` otherwise. Assign the result to a
28
+ * `const _lock: AssertEqual<A, B> = true;` so any drift becomes a compile
29
+ * error at the declaration site.
30
+ */
31
+ export type AssertEqual<Actual, Expected> = IfEquals<Actual, Expected, true, never>;
32
+ /**
33
+ * JSON-serialized shape of `T` — the wire form produced by `Response.json()`
34
+ * / `JSON.stringify`: `Date` becomes ISO `string` (nullability preserved),
35
+ * arrays are serialized element-wise, everything else is untouched.
36
+ *
37
+ * Used to derive the `*Response` API types from the `*Record` store types so
38
+ * the two can never drift: add a field to `FeedbackRecord` and
39
+ * `FeedbackResponse` follows automatically.
40
+ */
41
+ export type Serialized<T> = {
42
+ [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K] extends (infer U)[] ? Serialized<U>[] : T[K];
43
+ };
44
+ /**
45
+ * Type guard that narrows `value` to a non-null `Record<PropertyKey, unknown>`.
46
+ * Useful when validating arbitrary inputs before reading fields.
47
+ */
48
+ export declare function isRecord(value: unknown): value is Record<PropertyKey, unknown>;
49
+ /**
50
+ * Returns true when `value` is an object that exposes the requested key.
51
+ * Type-narrows `value` so the property can be accessed without further
52
+ * casting — a strictly typed replacement for `"k" in obj`.
53
+ *
54
+ * Named after the standardised `Object.hasOwn` helper rather than the
55
+ * legacy `Object.prototype.hasOwnProperty`, which the linter forbids
56
+ * shadowing.
57
+ */
58
+ export declare function hasOwn<K extends PropertyKey>(value: unknown, key: K): value is Record<K, unknown>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * General-purpose TypeScript utility types used across `@beezping/*`.
3
+ *
4
+ * These are kept dependency-free and re-exported from the package entry
5
+ * so adapters and integrators can rely on the same primitives the core
6
+ * uses internally.
7
+ */
8
+ /**
9
+ * Force TypeScript to expand a computed type into a flat object literal in
10
+ * tooltips and error messages. Purely cosmetic — same structural type, just
11
+ * easier to read.
12
+ *
13
+ * @example
14
+ * type Raw = Omit<FeedbackRecord, "annotations"> & { annotations: number };
15
+ * type Pretty = Prettify<Raw>; // displayed as a flat object
16
+ */
17
+ export type Prettify<T> = {
18
+ [K in keyof T]: T[K];
19
+ } & {};
20
+ /**
21
+ * Returns `Y` when `A` is exactly assignable to `B` and vice-versa,
22
+ * otherwise `N`. Powers compile-time equality assertions.
23
+ */
24
+ export type IfEquals<A, B, Y = true, N = false> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
25
+ /**
26
+ * Compile-time exact-type guard — resolves to `true` when `Actual` and
27
+ * `Expected` are identical, `never` otherwise. Assign the result to a
28
+ * `const _lock: AssertEqual<A, B> = true;` so any drift becomes a compile
29
+ * error at the declaration site.
30
+ */
31
+ export type AssertEqual<Actual, Expected> = IfEquals<Actual, Expected, true, never>;
32
+ /**
33
+ * JSON-serialized shape of `T` — the wire form produced by `Response.json()`
34
+ * / `JSON.stringify`: `Date` becomes ISO `string` (nullability preserved),
35
+ * arrays are serialized element-wise, everything else is untouched.
36
+ *
37
+ * Used to derive the `*Response` API types from the `*Record` store types so
38
+ * the two can never drift: add a field to `FeedbackRecord` and
39
+ * `FeedbackResponse` follows automatically.
40
+ */
41
+ export type Serialized<T> = {
42
+ [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K] extends (infer U)[] ? Serialized<U>[] : T[K];
43
+ };
44
+ /**
45
+ * Type guard that narrows `value` to a non-null `Record<PropertyKey, unknown>`.
46
+ * Useful when validating arbitrary inputs before reading fields.
47
+ */
48
+ export declare function isRecord(value: unknown): value is Record<PropertyKey, unknown>;
49
+ /**
50
+ * Returns true when `value` is an object that exposes the requested key.
51
+ * Type-narrows `value` so the property can be accessed without further
52
+ * casting — a strictly typed replacement for `"k" in obj`.
53
+ *
54
+ * Named after the standardised `Object.hasOwn` helper rather than the
55
+ * legacy `Object.prototype.hasOwnProperty`, which the linter forbids
56
+ * shadowing.
57
+ */
58
+ export declare function hasOwn<K extends PropertyKey>(value: unknown, key: K): value is Record<K, unknown>;