import { Coordinate, Floor, MapView, Model } from "@mappedin/mappedin-js"; //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/primitive.d.ts /** Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). @category Type */ type Primitive = null | undefined | string | number | boolean | symbol | bigint; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/is-any.d.ts /** Returns a boolean for whether the given type is `any`. @link https://stackoverflow.com/a/49928360/1490091 Useful in type utilities, such as disallowing `any`s to be passed to a function. @example ``` import type {IsAny} from 'type-fest'; const typedObject = {a: 1, b: 2} as const; const anyObject: any = {a: 1, b: 2}; function get extends true ? {} : Record), K extends keyof O = keyof O>(object: O, key: K) { return object[key]; } const typedA = get(typedObject, 'a'); //=> 1 const anyA = get(anyObject, 'a'); //=> any ``` @category Type Guard @category Utilities */ type IsAny = 0 extends 1 & NoInfer ? true : false; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/is-optional-key-of.d.ts /** Returns a boolean for whether the given key is an optional key of type. This is useful when writing utility types or schema validators that need to differentiate `optional` keys. @example ``` import type {IsOptionalKeyOf} from 'type-fest'; type User = { name: string; surname: string; luckyNumber?: number; }; type Admin = { name: string; surname?: string; }; type T1 = IsOptionalKeyOf; //=> true type T2 = IsOptionalKeyOf; //=> false type T3 = IsOptionalKeyOf; //=> boolean type T4 = IsOptionalKeyOf; //=> false type T5 = IsOptionalKeyOf; //=> boolean ``` @category Type Guard @category Utilities */ type IsOptionalKeyOf = IsAny extends true ? never : Key$1 extends keyof Type ? Type extends Record ? false : true : false; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/optional-keys-of.d.ts /** Extract all optional keys from the given type. This is useful when you want to create a new type that contains different type values for the optional keys only. @example ``` import type {OptionalKeysOf, Except} from 'type-fest'; type User = { name: string; surname: string; luckyNumber?: number; }; const REMOVE_FIELD = Symbol('remove field symbol'); type UpdateOperation = Except, OptionalKeysOf> & { [Key in OptionalKeysOf]?: Entity[Key] | typeof REMOVE_FIELD; }; const update1: UpdateOperation = { name: 'Alice', }; const update2: UpdateOperation = { name: 'Bob', luckyNumber: REMOVE_FIELD, }; ``` @category Utilities */ type OptionalKeysOf = Type extends unknown // For distributing `Type` ? (keyof { [Key in keyof Type as IsOptionalKeyOf extends false ? never : Key]: never }) & keyof Type // Intersect with `keyof Type` to ensure result of `OptionalKeysOf` is always assignable to `keyof Type` : never; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/required-keys-of.d.ts /** Extract all required keys from the given type. This is useful when you want to create a new type that contains different type values for the required keys only or use the list of keys for validation purposes, etc... @example ``` import type {RequiredKeysOf} from 'type-fest'; declare function createValidation< Entity extends object, Key extends RequiredKeysOf = RequiredKeysOf, >(field: Key, validator: (value: Entity[Key]) => boolean): (entity: Entity) => boolean; type User = { name: string; surname: string; luckyNumber?: number; }; const validator1 = createValidation('name', value => value.length < 25); const validator2 = createValidation('surname', value => value.length < 25); // @ts-expect-error const validator3 = createValidation('luckyNumber', value => value > 0); // Error: Argument of type '"luckyNumber"' is not assignable to parameter of type '"name" | "surname"'. ``` @category Utilities */ type RequiredKeysOf = Type extends unknown // For distributing `Type` ? Exclude> : never; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/is-never.d.ts /** Returns a boolean for whether the given type is `never`. @link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 @link https://stackoverflow.com/a/53984913/10292952 @link https://www.zhenghao.io/posts/ts-never Useful in type utilities, such as checking if something does not occur. @example ``` import type {IsNever, And} from 'type-fest'; type A = IsNever; //=> true type B = IsNever; //=> false type C = IsNever; //=> false type D = IsNever; //=> false type E = IsNever; //=> false type F = IsNever; //=> false ``` @example ``` import type {IsNever} from 'type-fest'; type IsTrue = T extends true ? true : false; // When a distributive conditional is instantiated with `never`, the entire conditional results in `never`. type A = IsTrue; //=> never // If you don't want that behaviour, you can explicitly add an `IsNever` check before the distributive conditional. type IsTrueFixed = IsNever extends true ? false : T extends true ? true : false; type B = IsTrueFixed; //=> false ``` @category Type Guard @category Utilities */ type IsNever = [T] extends [never] ? true : false; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/if.d.ts /** An if-else-like type that resolves depending on whether the given `boolean` type is `true` or `false`. Use-cases: - You can use this in combination with `Is*` types to create an if-else-like experience. For example, `If, 'is any', 'not any'>`. Note: - Returns a union of if branch and else branch if the given type is `boolean` or `any`. For example, `If` will return `'Y' | 'N'`. - Returns the else branch if the given type is `never`. For example, `If` will return `'N'`. @example ``` import type {If} from 'type-fest'; type A = If; //=> 'yes' type B = If; //=> 'no' type C = If; //=> 'yes' | 'no' type D = If; //=> 'yes' | 'no' type E = If; //=> 'no' ``` @example ``` import type {If, IsAny, IsNever} from 'type-fest'; type A = If, 'is any', 'not any'>; //=> 'not any' type B = If, 'is never', 'not never'>; //=> 'is never' ``` @example ``` import type {If, IsEqual} from 'type-fest'; type IfEqual = If, IfBranch, ElseBranch>; type A = IfEqual; //=> 'equal' type B = IfEqual; //=> 'not equal' ``` Note: Sometimes using the `If` type can make an implementation non–tail-recursive, which can impact performance. In such cases, it’s better to use a conditional directly. Refer to the following example: @example ``` import type {If, IsEqual, StringRepeat} from 'type-fest'; type HundredZeroes = StringRepeat<'0', 100>; // The following implementation is not tail recursive type Includes = S extends `${infer First}${infer Rest}` ? If, 'found', Includes> : 'not found'; // Hence, instantiations with long strings will fail // @ts-expect-error type Fails = Includes; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Error: Type instantiation is excessively deep and possibly infinite. // However, if we use a simple conditional instead of `If`, the implementation becomes tail-recursive type IncludesWithoutIf = S extends `${infer First}${infer Rest}` ? IsEqual extends true ? 'found' : IncludesWithoutIf : 'not found'; // Now, instantiations with long strings will work type Works = IncludesWithoutIf; //=> 'not found' ``` @category Type Guard @category Utilities */ type If = IsNever extends true ? ElseBranch : Type extends true ? IfBranch : ElseBranch; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/internal/type.d.ts /** Matches any primitive, `void`, `Date`, or `RegExp` value. */ type BuiltIns = Primitive | void | Date | RegExp; /** Test if the given function has multiple call signatures. Needed to handle the case of a single call signature with properties. Multiple call signatures cannot currently be supported due to a TypeScript limitation. @see https://github.com/microsoft/TypeScript/issues/29732 */ type HasMultipleCallSignatures unknown> = T extends { (...arguments_: infer A): unknown; (...arguments_: infer B): unknown; } ? B extends A ? A extends B ? false : true : true : false; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/simplify.d.ts /** Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability. @example ``` import type {Simplify} from 'type-fest'; type PositionProps = { top: number; left: number; }; type SizeProps = { width: number; height: number; }; // In your editor, hovering over `Props` will show a flattened object with all the properties. type Props = Simplify; ``` Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable. But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface. If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify = ...`. Then `value` will be assignable to the `fn` argument. Or the `value` can be cast as `Simplify` if you can't re-declare the `value`. @example ``` import type {Simplify} from 'type-fest'; interface SomeInterface { foo: number; bar?: string; baz: number | undefined; } type SomeType = { foo: number; bar?: string; baz: number | undefined; }; const literal = {foo: 123, bar: 'hello', baz: 456}; const someType: SomeType = literal; const someInterface: SomeInterface = literal; declare function fn(object: Record): void; fn(literal); // Good: literal object type is sealed fn(someType); // Good: type is sealed // @ts-expect-error fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened fn(someInterface as Simplify); // Good: transform an `interface` into a `type` ``` @link https://github.com/microsoft/TypeScript/issues/15300 @see {@link SimplifyDeep} @category Object */ type Simplify = { [KeyType in keyof T]: T[KeyType] } & {}; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/omit-index-signature.d.ts /** Omit any index signatures from the given object type, leaving only explicitly defined properties. This is the counterpart of `PickIndexSignature`. Use-cases: - Remove overly permissive signatures from third-party types. This type was taken from this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747). It relies on the fact that an empty object (`{}`) is assignable to an object with just an index signature, like `Record`, but not to an object with explicitly defined keys, like `Record<'foo' | 'bar', unknown>`. (The actual value type, `unknown`, is irrelevant and could be any type. Only the key type matters.) ``` const indexed: Record = {}; // Allowed // @ts-expect-error const keyed: Record<'foo', unknown> = {}; // Error // TS2739: Type '{}' is missing the following properties from type 'Record<"foo" | "bar", unknown>': foo, bar ``` Instead of causing a type error like the above, you can also use a [conditional type](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) to test whether a type is assignable to another: ``` type Indexed = {} extends Record ? '✅ `{}` is assignable to `Record`' : '❌ `{}` is NOT assignable to `Record`'; type IndexedResult = Indexed; //=> '✅ `{}` is assignable to `Record`' type Keyed = {} extends Record<'foo' | 'bar', unknown> ? '✅ `{}` is assignable to `Record<\'foo\' | \'bar\', unknown>`' : '❌ `{}` is NOT assignable to `Record<\'foo\' | \'bar\', unknown>`'; type KeyedResult = Keyed; //=> '❌ `{}` is NOT assignable to `Record<\'foo\' | \'bar\', unknown>`' ``` Using a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#further-exploration), you can then check for each `KeyType` of `ObjectType`... ``` type OmitIndexSignature = { [KeyType in keyof ObjectType // Map each key of `ObjectType`... ]: ObjectType[KeyType]; // ...to its original value, i.e. `OmitIndexSignature == Foo`. }; ``` ...whether an empty object (`{}`) would be assignable to an object with that `KeyType` (`Record`)... ``` type OmitIndexSignature = { [KeyType in keyof ObjectType // Is `{}` assignable to `Record`? as {} extends Record ? never // ✅ `{}` is assignable to `Record` : KeyType // ❌ `{}` is NOT assignable to `Record` ]: ObjectType[KeyType]; }; ``` If `{}` is assignable, it means that `KeyType` is an index signature and we want to remove it. If it is not assignable, `KeyType` is a "real" key and we want to keep it. @example ``` import type {OmitIndexSignature} from 'type-fest'; type Example = { // These index signatures will be removed. [x: string]: any; [x: number]: any; [x: symbol]: any; [x: `head-${string}`]: string; [x: `${string}-tail`]: string; [x: `head-${string}-tail`]: string; [x: `${bigint}`]: string; [x: `embedded-${number}`]: string; // These explicitly defined keys will remain. foo: 'bar'; qux?: 'baz'; }; type ExampleWithoutIndexSignatures = OmitIndexSignature; //=> {foo: 'bar'; qux?: 'baz'} ``` @see {@link PickIndexSignature} @category Object */ type OmitIndexSignature = { [KeyType in keyof ObjectType as {} extends Record ? never : KeyType]: ObjectType[KeyType] }; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/pick-index-signature.d.ts /** Pick only index signatures from the given object type, leaving out all explicitly defined properties. This is the counterpart of `OmitIndexSignature`. @example ``` import type {PickIndexSignature} from 'type-fest'; declare const symbolKey: unique symbol; type Example = { // These index signatures will remain. [x: string]: unknown; [x: number]: unknown; [x: symbol]: unknown; [x: `head-${string}`]: string; [x: `${string}-tail`]: string; [x: `head-${string}-tail`]: string; [x: `${bigint}`]: string; [x: `embedded-${number}`]: string; // These explicitly defined keys will be removed. ['kebab-case-key']: string; [symbolKey]: string; foo: 'bar'; qux?: 'baz'; }; type ExampleIndexSignature = PickIndexSignature; // { // [x: string]: unknown; // [x: number]: unknown; // [x: symbol]: unknown; // [x: `head-${string}`]: string; // [x: `${string}-tail`]: string; // [x: `head-${string}-tail`]: string; // [x: `${bigint}`]: string; // [x: `embedded-${number}`]: string; // } ``` @see {@link OmitIndexSignature} @category Object */ type PickIndexSignature = { [KeyType in keyof ObjectType as {} extends Record ? KeyType : never]: ObjectType[KeyType] }; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/merge.d.ts // Merges two objects without worrying about index signatures. type SimpleMerge = Simplify<{ [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key] } & Source>; /** Merge two types into a new type. Keys of the second type overrides keys of the first type. @example ``` import type {Merge} from 'type-fest'; type Foo = { [x: string]: unknown; [x: number]: unknown; foo: string; bar: symbol; }; type Bar = { [x: number]: number; [x: symbol]: unknown; bar: Date; baz: boolean; }; export type FooBar = Merge; //=> { // [x: string]: unknown; // [x: number]: number; // [x: symbol]: unknown; // foo: string; // bar: Date; // baz: boolean; // } ``` Note: If you want a merge type that more accurately reflects the runtime behavior of object spread or `Object.assign`, refer to the {@link ObjectMerge} type. @see {@link ObjectMerge} @category Object */ type Merge = Destination extends unknown // For distributing `Destination` ? Source extends unknown // For distributing `Source` ? Simplify, PickIndexSignature> & SimpleMerge, OmitIndexSignature>> : never // Should never happen : never; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/internal/object.d.ts /** Merges user specified options with default options. @example ``` type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean}; type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: false}; type SpecifiedOptions = {leavesOnly: true}; type Result = ApplyDefaultOptions; //=> {maxRecursionDepth: 10; leavesOnly: true} ``` @example ``` // Complains if default values are not provided for optional options type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean}; type DefaultPathsOptions = {maxRecursionDepth: 10}; type SpecifiedOptions = {}; type Result = ApplyDefaultOptions; // ~~~~~~~~~~~~~~~~~~~ // Property 'leavesOnly' is missing in type 'DefaultPathsOptions' but required in type '{ maxRecursionDepth: number; leavesOnly: boolean; }'. ``` @example ``` // Complains if an option's default type does not conform to the expected type type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean}; type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: 'no'}; type SpecifiedOptions = {}; type Result = ApplyDefaultOptions; // ~~~~~~~~~~~~~~~~~~~ // Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'. ``` @example ``` // Complains if an option's specified type does not conform to the expected type type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean}; type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: false}; type SpecifiedOptions = {leavesOnly: 'yes'}; type Result = ApplyDefaultOptions; // ~~~~~~~~~~~~~~~~ // Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'. ``` */ type ApplyDefaultOptions, RequiredKeysOf> & Partial, never>>>, SpecifiedOptions extends Options> = If, Defaults, If, Defaults, Simplify ? undefined extends SpecifiedOptions[Key] ? never : Key : Key]: SpecifiedOptions[Key] }> & Required>>>; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/partial-deep.d.ts /** @see {@link PartialDeep} */ type PartialDeepOptions = { /** Whether to affect the individual elements of arrays and tuples. @default false */ readonly recurseIntoArrays?: boolean; /** Allows `undefined` values in non-tuple arrays. - When set to `true`, elements of non-tuple arrays can be `undefined`. - When set to `false`, only explicitly defined elements are allowed in non-tuple arrays, ensuring stricter type checking. @default false @example You can allow `undefined` values in non-tuple arrays by passing `{recurseIntoArrays: true; allowUndefinedInNonTupleArrays: true}` as the second type argument: ``` import type {PartialDeep} from 'type-fest'; type Settings = { languages: string[]; }; declare const partialSettings: PartialDeep; partialSettings.languages = [undefined]; // OK ``` */ readonly allowUndefinedInNonTupleArrays?: boolean; }; type DefaultPartialDeepOptions = { recurseIntoArrays: false; allowUndefinedInNonTupleArrays: false; }; /** Create a type from another type with all keys and nested keys set to optional. Use-cases: - Merging a default settings/config object with another object, the second object would be a deep partial of the default object. - Mocking and testing complex entities, where populating an entire object with its keys would be redundant in terms of the mock or test. @example ``` import type {PartialDeep} from 'type-fest'; let settings = { textEditor: { fontSize: 14, fontColor: '#000000', fontWeight: 400, }, autocomplete: false, autosave: true, }; const applySavedSettings = (savedSettings: PartialDeep) => ( {...settings, ...savedSettings, textEditor: {...settings.textEditor, ...savedSettings.textEditor}} ); settings = applySavedSettings({textEditor: {fontWeight: 500}}); ``` By default, this does not affect elements in array and tuple types. You can change this by passing `{recurseIntoArrays: true}` as the second type argument: ``` import type {PartialDeep} from 'type-fest'; type Shape = { dimensions: [number, number]; }; const partialShape: PartialDeep = { dimensions: [], // OK }; partialShape.dimensions = [15]; // OK ``` @see {@link PartialDeepOptions} @category Object @category Array @category Set @category Map */ type PartialDeep = _PartialDeep>; type _PartialDeep> = T extends BuiltIns | ((new (...arguments_: any[]) => unknown)) ? T : T extends Map ? PartialMapDeep : T extends Set ? PartialSetDeep : T extends ReadonlyMap ? PartialReadonlyMapDeep : T extends ReadonlySet ? PartialReadonlySetDeep : T extends ((...arguments_: any[]) => unknown) ? IsNever extends true ? T // For functions with no properties : HasMultipleCallSignatures extends true ? T : ((...arguments_: Parameters) => ReturnType) & PartialObjectDeep : T extends object ? T extends ReadonlyArray // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156 ? Options['recurseIntoArrays'] extends true ? ItemType[] extends T // Test for arrays (non-tuples) specifically ? readonly ItemType[] extends T // Differentiate readonly and mutable arrays ? ReadonlyArray<_PartialDeep> : Array<_PartialDeep> : PartialObjectDeep // Tuples behave properly : T // If they don't opt into array testing, just use the original type : PartialObjectDeep : unknown; /** Same as `PartialDeep`, but accepts only `Map`s and as inputs. Internal helper for `PartialDeep`. */ type PartialMapDeep> = {} & Map<_PartialDeep, _PartialDeep>; /** Same as `PartialDeep`, but accepts only `Set`s as inputs. Internal helper for `PartialDeep`. */ type PartialSetDeep> = {} & Set<_PartialDeep>; /** Same as `PartialDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `PartialDeep`. */ type PartialReadonlyMapDeep> = {} & ReadonlyMap<_PartialDeep, _PartialDeep>; /** Same as `PartialDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `PartialDeep`. */ type PartialReadonlySetDeep> = {} & ReadonlySet<_PartialDeep>; /** Same as `PartialDeep`, but accepts only `object`s as inputs. Internal helper for `PartialDeep`. */ type PartialObjectDeep> = { [KeyType in keyof ObjectType]?: _PartialDeep }; //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/literal-union.d.ts /** Allows creating a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Currently, when a union type of a primitive type is combined with literal types, TypeScript loses all information about the combined literals. Thus, when such type is used in an IDE with autocompletion, no suggestions are made for the declared literals. This type is a workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). It will be removed as soon as it's not needed anymore. @example ``` import type {LiteralUnion} from 'type-fest'; // Before type Pet = 'dog' | 'cat' | string; const petWithoutAutocomplete: Pet = ''; // Start typing in your TypeScript-enabled IDE. // You **will not** get auto-completion for `dog` and `cat` literals. // After type Pet2 = LiteralUnion<'dog' | 'cat', string>; const petWithAutoComplete: Pet2 = ''; // You **will** get auto-completion for `dog` and `cat` literals. ``` @category Type */ type LiteralUnion = LiteralType | (BaseType & Record); //#endregion //#region ../../node_modules/.pnpm/type-fest@5.4.2/node_modules/type-fest/source/readonly-deep.d.ts /** Convert `object`s, `Map`s, `Set`s, and `Array`s and all of their keys/elements into immutable structures recursively. This is useful when a deeply nested structure needs to be exposed as completely immutable, for example, an imported JSON module or when receiving an API response that is passed around. Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/13923) if you want to have this type as a built-in in TypeScript. @example ``` import type {ReadonlyDeep} from 'type-fest'; declare const foo: { a: string; b: {c: number}; d: Array<{e: number}>; }; foo.a = 'bar'; // Allowed foo.b = {c: 3}; // Allowed foo.b.c = 4; // Allowed foo.d = [{e: 5}]; // Allowed foo.d.push({e: 6}); // Allowed const last = foo.d.at(-1); if (last) { last.e = 7; // Allowed } declare const readonlyFoo: ReadonlyDeep; // @ts-expect-error readonlyFoo.a = 'bar'; // Error: Cannot assign to 'a' because it is a read-only property. // @ts-expect-error readonlyFoo.b = {c: 3}; // Error: Cannot assign to 'b' because it is a read-only property. // @ts-expect-error readonlyFoo.b.c = 4; // Error: Cannot assign to 'c' because it is a read-only property. // @ts-expect-error readonlyFoo.d = [{e: 5}]; // Error: Cannot assign to 'd' because it is a read-only property. // @ts-expect-error readonlyFoo.d.push({e: 6}); // Error: Property 'push' does not exist on type 'ReadonlyArray<{readonly e: number}>'. const readonlyLast = readonlyFoo.d.at(-1); if (readonlyLast) { // @ts-expect-error readonlyLast.e = 8; // Error: Cannot assign to 'e' because it is a read-only property. } ``` Note that types containing overloaded functions are not made deeply readonly due to a [TypeScript limitation](https://github.com/microsoft/TypeScript/issues/29732). @category Object @category Array @category Set @category Map */ type ReadonlyDeep = T extends BuiltIns ? T : T extends (new (...arguments_: any[]) => unknown) ? T // Skip class constructors : T extends ((...arguments_: any[]) => unknown) ? {} extends _ReadonlyObjectDeep ? T : HasMultipleCallSignatures extends true ? T : ((...arguments_: Parameters) => ReturnType) & _ReadonlyObjectDeep : T extends Readonly> ? ReadonlyMapDeep : T extends Readonly> ? ReadonlySetDeep : // Identify tuples to avoid converting them to arrays inadvertently; special case `readonly [...never[]]`, as it emerges undesirably from recursive invocations of ReadonlyDeep below. T extends readonly [] | readonly [...never[]] ? readonly [] : T extends readonly [infer U, ...infer V] ? readonly [ReadonlyDeep, ...ReadonlyDeep] : T extends readonly [...infer U, infer V] ? readonly [...ReadonlyDeep, ReadonlyDeep] : T extends ReadonlyArray ? ReadonlyArray> : T extends object ? _ReadonlyObjectDeep : unknown; /** Same as `ReadonlyDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `ReadonlyDeep`. */ type ReadonlyMapDeep = {} & Readonly, ReadonlyDeep>>; /** Same as `ReadonlyDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `ReadonlyDeep`. */ type ReadonlySetDeep = {} & Readonly>>; /** Same as `ReadonlyDeep`, but accepts only `object`s as inputs. Internal helper for `ReadonlyDeep`. */ type _ReadonlyObjectDeep = { readonly [KeyType in keyof ObjectType]: ReadonlyDeep }; //#endregion //#region src/sensors/types.d.ts /** * Built-in sensor IDs */ declare const BUILT_IN_SENSOR_IDS: readonly ["geolocation", "deviceorientation", "devicemotion", "manual", "fusion"]; /** * Built-in sensor IDs. */ type BuiltInSensorId = (typeof BUILT_IN_SENSOR_IDS)[number]; /** * Utility type that rejects built-in IDs when they are known as literals. * * TypeScript cannot represent "any string except these specific literals" as a standalone type * (i.e., `Exclude` evaluates to `string`). This helper is meant for APIs like * `SensorRegistry.register()` where the sensor's `id` is typically a string literal type. */ type CustomSensorId = Id extends BuiltInSensorId ? never : Id; /** * Sensor ID - either a built-in ID or a custom string ID. */ type SensorId = LiteralUnion; /** * Event payloads for internal sensor communication. * * These are INTERNAL events used between sensors and the FusionEngine. * They are NOT the same as the external `position-update` event that * app developers subscribe to on the BlueDot class. * * ## Event Flow * * ``` * Sensors ──► SensorRegistry ──► FusionEngine ──► BlueDot ──► App * │ │ │ │ * │ absolute-update │ │ │ position-update * │ relative-update │ │ │ (external API) * │ (internal) │ │ │ * ``` * * ## Event Types * * - **`absolute-update`**: Sensors providing global coordinates (GPS lat/lon, compass heading) * - **`relative-update`**: Sensors providing displacement from a reference (PDR steps: dx/dy meters) * - **`anchor-set`**: Sensors setting a high-confidence position anchor (VPS, AI Localizer) * - **`anchor-clear`**: Sensors clearing their anchor * - **`error`**: Sensor errors * * The FusionEngine processes these differently: * - Absolute updates → `ekf.updatePosition(lat, lon, noise, timestamp)` * - Relative updates → `ekf.updateOdometry(dx, dy, noise, timestamp)` */ type SensorEventPayloads = { /** * Emitted when a sensor provides an absolute position or heading update. * * "Absolute" means the data represents a global/world coordinate or direction, * not a displacement from a previous position. * * Examples of absolute updates: * - GPS position (latitude, longitude) * - Compass heading (0-360 degrees from north) * - Gyroscope angular velocity (for heading integration) * - VPS/AI Localizer position (high-confidence anchor) * * The FusionEngine routes these to `ekf.updatePosition()` for lat/lon data, * or to the HeadingFusionFilter for heading/angular velocity data. * * @see {@link relative-update} for displacement-based updates */ 'absolute-update': { /** Which sensor produced this update */ sensorId: SensorId; /** The position/heading data (fields are optional - sensors provide what they have) */ update: PartialPositionUpdate; }; /** * Emitted when a sensor provides a relative displacement update. * * "Relative" means the data represents movement FROM a reference position, * not an absolute world coordinate. Used for Pedestrian Dead Reckoning (PDR). * * Examples of relative updates: * - Step detection: "moved 0.7 meters in direction of current heading" * - Inertial navigation: "displaced (dx, dy) meters since last update" * * The FusionEngine routes these to `ekf.updateOdometry()`. * * **Important**: Relative updates are only useful when an anchor is active. * Without an anchor, there's no reference point to apply the displacement to. * The FusionEngine will ignore relative updates when `anchorState === 'none'`. * * @see {@link absolute-update} for global coordinate updates */ 'relative-update': { /** Which sensor produced this update */ sensorId: SensorId; /** The displacement data */ update: RelativePositionUpdate; }; /** * Emitted when a sensor encounters an error. */ error: { /** Which sensor encountered the error */ sensorId: SensorId; /** The error (may be a GeolocationPositionError for geolocation sensor) */ error: Error | GeolocationPositionError; }; /** * Emitted when a sensor sets a position anchor. * * Anchors are high-confidence absolute positions from calibration sources * (VPS, AI Localizer, QR code scan, manual correction). When an anchor is * active, it serves as the reference point for position fusion. * * The FusionEngine resets the EKF state and uses the anchor as ground truth. */ 'anchor-set': { /** Which sensor is setting the anchor */ sensorId: SensorId; /** The anchor position data */ anchor: PositionAnchor; /** Optional config override for this anchor (e.g., extended anchorOnlyPeriod for forcePosition) */ configOverride?: { anchorOnlyPeriodMs: number; }; }; /** * Emitted when a sensor clears its anchor. * * After clearing, the FusionEngine will fall back to other position sources. */ 'anchor-clear': { /** Which sensor is clearing the anchor */ sensorId: SensorId; }; }; //#endregion //#region src/fusion/anchor-state.d.ts /** * Anchor state in the fusion engine's hybrid anchor behavior model. * * - `'none'`: No anchor active; GPS measurements accepted, PDR ignored * - `'anchor-only'`: Anchor active and in exclusive period; GPS rejected, PDR accepted * - `'transition'`: Anchor active but past exclusive period; GPS accepted with increasing weight */ type AnchorState = 'none' | 'anchor-only' | 'transition'; //#endregion //#region src/fusion/types.d.ts type FusionEventPayloads = { 'fused-position': { position: FusedPosition; }; 'heading-update': { heading: number; }; 'anchor-set': { anchor: PositionAnchor; }; 'anchor-expired': { anchor: PositionAnchor; }; error: { error: Error | GeolocationPositionError; }; }; type PositionMetadata = { /** Confidence score 0-1 (0 = no confidence, 1 = 100% certain) */ confidence: number; }; /** * Represents a position update from a position source with an associated confidence score. * This interface is explicitly defined to include core geolocation fields, avoiding TypeScript interface extension errors. */ type PositionUpdate = { /** Latitude in degrees */ latitude: number; /** Longitude in degrees */ longitude: number; /** Accuracy in meters */ accuracy?: number; /** Altitude in meters above the WGS84 ellipsoid (if available) */ altitude?: number | null; /** Altitude accuracy in meters (if available) */ altitudeAccuracy?: number | null; /** Heading in degrees (if available) */ heading?: number | null; /** Angular velocity in degrees per second (gyroscope z-axis rotation rate) */ angularVelocity?: number | null; /** Speed in meters per second (if available) */ speed?: number | null; /** Floor level from device (used to resolve floor) */ floorLevel?: number | null; /** Timestamp in milliseconds */ timestamp?: number; } & PositionMetadata; type PartialPositionUpdate = PartialDeep & PositionMetadata; /** * Contribution from a single sensor to the fused position. */ interface SensorContribution { /** Sensor identifier */ sensorId: SensorId; /** Weight applied to this sensor (normalized 0-1, 0 for heading-only sensors) */ weight: number; /** Original position update from the sensor (may be partial) */ position: PartialPositionUpdate; } /** * A fused position combining multiple sensor updates. */ interface FusedPosition extends PositionUpdate { sensorId: 'fusion'; /** Individual contributions from each sensor */ contributions: SensorContribution[]; /** Whether position is calibrated via an anchor (VPS/AI Localizer) */ isCalibrated: boolean; /** Accumulated displacement from anchor in meters (only present when isCalibrated is true) */ displacement?: DisplacementVector; } /** * A displacement vector in meters. */ interface DisplacementVector { /** Displacement in meters along east-west axis (positive = east) */ dx: number; /** Displacement in meters along north-south axis (positive = north) */ dy: number; } /** * A relative displacement update from a motion sensor (e.g., PDR). * Displacement is in meters, relative to the last known position. * Used for dead reckoning from a calibrated anchor position. */ interface RelativePositionUpdate { /** Displacement in meters along east-west axis (positive = east) */ dx: number; /** Displacement in meters along north-south axis (positive = north) */ dy: number; /** Heading in degrees (0-360) at time of displacement */ heading?: number; /** Timestamp in milliseconds */ timestamp: number; /** Confidence score 0-1 */ confidence: number; } /** * Configuration for the multi-sensor EKF fusion engine. */ interface FusionEngineConfig { /** * Duration in ms where anchor is exclusive (GPS rejected). * @default 5000 (5 seconds) */ anchorOnlyPeriodMs: number; /** * Half-life for state uncertainty growth in milliseconds. * The state uncertainty doubles every halfLifeMs when no measurements arrive. * Lower values = faster decay of old position estimates. * @default 10000 (10 seconds) */ halfLifeMs: number; /** * Maximum age of position updates to consider in milliseconds. * Updates older than this cause filter reset. * @default 5000 (5 seconds) */ maxAgeMs: number; } /** * Position anchor from a calibration source (VPS, AI Localizer). * Anchors represent absolute ground truth positions that other sensors * can reference for relative positioning. */ interface PositionAnchor { /** Absolute latitude in degrees */ latitude: number; /** Absolute longitude in degrees */ longitude: number; /** Heading/bearing from calibration source in degrees */ heading?: number; /** Floor level from calibration source */ floorLevel?: number; /** Sensor that set this anchor */ sensorId: SensorId; /** When anchor was established (timestamp in ms) */ timestamp: number; /** How long anchor remains valid in milliseconds */ ttl: number; /** * Initial confidence of this anchor (0-1). * Used for transition decay: anchor confidence decreases over time. * @default 1.0 */ confidence?: number; } //#endregion //#region ../packages/common/browser.d.ts type BrowserPermissionState = PermissionState | 'unavailable'; //#endregion //#region ../packages/common/extensions.d.ts interface MapViewExtension { enable(options?: PartialDeep): void; disable(): void; get isEnabled(): boolean; destroy(): void; } //#endregion //#region src/status/types.d.ts type BlueDotStatus = 'hidden' | 'active' | 'inactive' | 'disabled'; type BlueDotAction = 'timeout' | 'error' | 'position-update' | 'enable' | 'disable' | 'initialize'; //#endregion //#region src/follow/types.d.ts type FollowMode = /** Camera position follows the Blue Dot's position. */ 'position-only' /** Camera position follows the Blue Dot's position. Camera bearing matches the Blue Dot's heading. */ | 'position-and-heading' /** Camera position follows the Blue Dot's position. Camera bearing is calculated based on the Navigation path. */ | 'position-and-path-direction' /** Disables follow mode */ | false; //#endregion //#region src/types.d.ts type FollowCameraOptions = { /** * @default 21 */ zoomLevel?: number; /** * @default 45 */ pitch?: number; /** * Camera bearing in degrees clockwise from North. 0 is North, 90 is East, 180 is South, 270 is West. * This option is only available in 'position-only' mode. In all other modes, the bearing will be calculated automatically. * @default undefined */ bearing?: number; /** * @default undefined */ elevation?: number; /** * @default 1000 */ duration?: number; /** * @default 'ease-in-out' */ easing?: 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'; }; type BlueDotEventPayloads = { /** * Emitted when the Blue Dot's position is updated either from the device's geolocation API or by calling {@link BlueDot.update}. * see {@link BlueDot.watchDevicePosition} for more details. */ 'position-update': { floor: Floor | undefined; heading: GeolocationPosition['coords']['heading'] | undefined; accuracy: GeolocationPosition['coords']['accuracy'] | undefined; coordinate: Coordinate; }; /** * Emitted when the device's orientation changes and the Blue Dot's heading is updated. * see {@link BlueDot.watchDeviceOrientation} for more details. */ 'device-orientation-update': { heading: GeolocationPosition['coords']['heading'] | undefined; }; /** * Emitted when the Blue Dot's status changes. */ 'status-change': { /** * The new status of the Blue Dot. */ status: BlueDotStatus; /** * The action that caused the status change. */ action: BlueDotAction; }; /** * Emitted when the Blue Dot encounters an error. */ error: GeolocationPositionError; /** * Emitted when the Blue Dot's following state changes. */ 'follow-change': { /** * Whether the Blue Dot is following the user. */ following: boolean; /** * The mode the Blue Dot is following the user in. */ mode?: FollowMode; }; /** * Emitted when the user clicks on the Blue Dot. */ click: { coordinate: Coordinate; }; /** * Emitted when the user hovers over the Blue Dot. */ hover: { coordinate: Coordinate; }; /** * Emitted when a calibration anchor is set (e.g. from VPS or {@link BlueDot.forcePosition}). */ 'anchor-set': { anchor: PositionAnchor; }; /** * Emitted when a calibration anchor's TTL expires. */ 'anchor-expired': { anchor: PositionAnchor; }; }; type BlueDotState = { /** * Whether the BlueDot core element is visible. When false, the dot is hidden but the accuracy ring * and heading cone may still render based on their own visibility settings. * @default true */ visible: boolean; /** * The radius of the BlueDot in pixels. The BlueDot will maintain this size clamped to a minimum of 0.35 metres. * @default 10 */ radius: number; /** * The color of the BlueDot core element. * @default #2266ff */ color: string; /** * The color of the BlueDot when it has timed out and gone inactive. * @default #808080 */ inactiveColor: string; /** * Options for the accuracy ring around the BlueDot. */ accuracyRing: { /** * Whether the accuracy ring is visible. * @default true */ visible: boolean; /** * The color of the accuracy ring. * @default #2266ff */ color: string; /** * The opacity of the accuracy ring. * @default 0.3 */ opacity: number; }; /** * Options for the heading directional indicator. */ heading: { /** * Whether the heading cone is visible. * @default true */ visible: boolean; /** * The color of the heading cone. * @default #2266ff */ color: string; /** * The opacity of the heading cone. * @default 0.7 */ opacity: number; /** * Whether to display the heading cone when the BlueDot is inactive (timed out). * @default false */ displayWhenInactive: boolean; }; /** * The duration of the timeout in milliseconds. * If the BlueDot does not receive a position update within this time, it will grey out until a position is received. * @default 30000 */ timeout: number; /** * Whether to watch the device's position. * @default true */ watchDevicePosition: boolean; /** * Whether to log debug messages. * @default false */ debug: boolean; /** * The maximum acceptable accuracy in meters. Position updates with accuracy exceeding this value will be dropped. * @default 50 */ accuracyThreshold: number; /** * The initial state of the BlueDot when enabled. * @default 'hidden' */ initialState: 'hidden' | 'inactive'; /** * @hidden * Whether the BlueDot must remain within the map bounds. Disabling this will disable analytics as well. * @default true */ preventOutOfBounds: boolean; /** * If true, timestamp will be used to discard updates which are received out of order. * @default true */ discardStaleUpdates: boolean; }; type BlueDotUpdateState = PartialDeep; /** * Position update options for the {@link BlueDot.update} method. */ type BlueDotPositionUpdate = { /** * Latitude to override. * Set to `'device'` to reset to the device's latitude. */ latitude?: GeolocationPosition['coords']['latitude'] | 'device' | undefined; /** * Longitude to override. * Set to `'device'` to reset to the device's longitude. */ longitude?: GeolocationPosition['coords']['longitude'] | 'device' | undefined; /** * Accuracy to override. * Set to `'device'` to reset to the device's accuracy. * Set to `undefined` to disable the accuracy ring. */ accuracy?: GeolocationPosition['coords']['accuracy'] | 'device' | undefined; /** * Heading to override. * Set to `'device'` to reset to the device's heading. * Set to `undefined` to disable the heading indicator. */ heading?: GeolocationPosition['coords']['heading'] | 'device' | undefined; /** * Floor or floorId to override. * Set to `'device'` to reset to the device's floor level. * Set to `undefined` to disable floor level and show the BlueDot on all floors. */ floorOrFloorId?: Floor | string | 'device' | undefined; /** * Timestamp of the position update in milliseconds. */ timestamp?: number; }; type BlueDotPositionUpdateWithFloor = Omit & { floor?: Floor | 'device' | undefined; }; type BlueDotPositionProcessor = (current: BlueDotPositionUpdateWithFloor, incoming: BlueDotPositionUpdateWithFloor) => BlueDotPositionUpdateWithFloor | undefined; /** * Options for the BlueDot update method. */ type BlueDotUpdateOptions = { /** * If true, maintains the current state and skips timers and analytics for this update. * @default false */ silent?: boolean; /** * If true, animates the position change. If false, updates immediately. * @default true */ animate?: boolean; }; type GeolocationPositionExtended = GeolocationPosition & { coords: GeolocationPosition['coords'] & { readonly floorLevel?: number | null; }; }; //#endregion //#region ../packages/common/pubsub.d.ts /** * Generic PubSub class implementing the Publish-Subscribe pattern for event handling. * * @template EVENT_PAYLOAD - The type of the event payload. * @template EVENT - The type of the event. */ declare class PubSub { /** * @private * @internal */ private _subscribers; /** * @private * @internal * AbortController for managing lifecycle and cleanup */ private _abortController; /** * Returns the AbortSignal for this PubSub instance. * Use this signal with APIs that support cancellation (fetch, addEventListener, etc.) * When the PubSub is destroyed, the signal will be aborted and all listeners using it will be automatically removed. * * @example * ```typescript * // Automatically cleaned up when PubSub is destroyed * pubsub.addEventListener(window, 'resize', handler, { signal: pubsub.signal }); * ``` */ get signal(): AbortSignal; /** * @private * @internal */ publish(eventName: EVENT_NAME, data?: EVENT_PAYLOAD[EVENT_NAME]): void; /** * Subscribe a function to be called when the signal is aborted. * @param fn The function to call when the signal is aborted. */ onAbort void>(fn: T): void; /** * Subscribe a function to an event. * * @param eventName An event name which, when fired, will call the provided * function. * @param fn A callback that gets called when the corresponding event is fired. The * callback will get passed an argument with a type that's one of event payloads. * @param options Optional options object. If a signal is provided, the subscription will be * automatically cleaned up when that signal is aborted. * @returns A cleanup function that unsubscribes the event listener when called. * @example * // Subscribe to the 'click' event * const handler = (event) => { * const { coordinate } = event; * const { latitude, longitude } = coordinate; * console.log(`Map was clicked at ${latitude}, ${longitude}`); * }; * map.on('click', handler); */ on(eventName: EVENT_NAME, fn: (payload: EVENT_PAYLOAD[EVENT_NAME] extends { data: null; } ? EVENT_PAYLOAD[EVENT_NAME]['data'] : EVENT_PAYLOAD[EVENT_NAME]) => void, options?: { signal?: AbortSignal; }): () => void; /** * Unsubscribe a function previously subscribed with {@link on} * * @param eventName An event name to which the provided function was previously * subscribed. * @param fn A function that was previously passed to {@link on}. The function must * have the same reference as the function that was subscribed. * @example * // Unsubscribe from the 'click' event * const handler = (event) => { * console.log('Map was clicked', event); * }; * map.off('click', handler); */ off(eventName: EVENT_NAME, fn: (payload: EVENT_PAYLOAD[EVENT_NAME] extends { data: null; } ? EVENT_PAYLOAD[EVENT_NAME]['data'] : EVENT_PAYLOAD[EVENT_NAME]) => void): void; /** * @private * @internal * Destroys the PubSub instance and automatically unsubscribes all listeners registered via on(). */ destroy(): void; } //#endregion //#region src/sensors/base-sensor.d.ts /** * Options for setting an anchor from a sensor. */ interface SetAnchorOptions { /** Latitude in degrees */ latitude: number; /** Longitude in degrees */ longitude: number; /** Heading in degrees (0-360, clockwise from north) */ heading?: number; /** Floor level (elevation) */ floorLevel?: number; /** * How long the anchor remains valid in milliseconds. * @default 30000 (30 seconds) */ ttl?: number; /** * Confidence score (0-1) for this anchor. * @default 1.0 */ confidence?: number; /** * Duration in ms where this anchor is exclusive (GPS rejected). * If not specified, uses the FusionEngine's default anchorOnlyPeriodMs. */ anchorOnlyPeriodMs?: number; } /** * Abstract base class for all position sources. * Extend this class to create custom position sources (e.g., IPS, BLE beacons). */ declare abstract class BaseSensor extends PubSub { /** Unique identifier for this sensor */ abstract readonly id: string; /** Whether this sensor requires browser/device permission to operate */ abstract readonly requiresPermission: boolean; /** Whether this source is currently running */ protected enabled: boolean; constructor(); /** * Whether this source is currently running and producing updates. */ get isEnabled(): boolean; /** * Start receiving position updates from this source. * This is called automatically when the source is enabled. */ protected abstract start(): Promise; /** * Stop receiving position updates from this source. * This is called automatically when the source is disabled. */ protected abstract stop(): void; /** * Check the current permission state for this source. * @returns The current permission state */ abstract checkPermission(): Promise; /** * Request permission to use this source. * Must be called in response to a user gesture (click, tap, etc.). * @returns true if permission was granted, false otherwise */ abstract requestPermission(): Promise; /** * Called when the fusion engine produces a new fused position. * Override this method to receive fused positions for calibration or anchoring. * @param position The fused position from all sources */ onFusedPosition(position: FusedPosition): void; /** * Set a position anchor from this sensor. * * Use this when your sensor has a high-confidence absolute position * (e.g., VPS localization, QR code scan, beacon detection). * The anchor will be used as the reference point for position fusion. * * @param options - Anchor position and configuration * * @example * ```typescript * // In your custom sensor after successful localization: * this.setAnchor({ * latitude: 43.4723, * longitude: -80.5449, * heading: 90, * floorLevel: 2, * ttl: 30000, * }); * ``` */ protected setAnchor(options: SetAnchorOptions): void; /** * Clear the anchor set by this sensor. * * After clearing, the FusionEngine will fall back to other position sources. */ protected clearAnchor(): void; enable(): Promise; disable(): void; } //#endregion //#region src/sensors/deviceorientation/deviceorientation.d.ts /** * Configuration options for the device orientation sensor. */ interface OrientationSensorConfig { /** Minimum heading change (degrees) before emitting update. */ deltaThreshold: number; } declare class DeviceOrientationSensor extends BaseSensor { #private; readonly id = "deviceorientation"; constructor(); get requiresPermission(): boolean; /** * Configure the sensor parameters at runtime. * @param config Partial configuration to apply */ configure(config: Partial): void; /** * Get current configuration values. */ getConfig(): OrientationSensorConfig; protected start: () => Promise; protected stop: () => void; checkPermission: () => Promise; requestPermission: () => Promise; } //#endregion //#region src/sensors/geolocation/outlier-detection.d.ts /** * Configuration for outlier detection. */ interface OutlierDetectionConfig { /** * Maximum plausible speed in m/s for a pedestrian. * Positions implying faster movement are considered outliers. * @default 10 (~36 km/h, faster than sprinting) */ maxPlausibleSpeed: number; /** * Minimum time between positions to perform speed check (ms). * Avoids division by near-zero for rapid updates. * @default 500 */ minTimeForSpeedCheck: number; } //#endregion //#region src/sensors/geolocation/geolocation.d.ts declare class GeolocationSensor extends BaseSensor { #private; readonly id = "geolocation"; readonly requiresPermission = false; /** * Whether we're likely receiving IPS (Indoor Positioning System) data. * True when positions include floor level, indicating Apple IPS or similar. * This is an assumption based on available signals, not a certainty. */ get isLikelyIPS(): boolean; /** * Configure outlier detection parameters. * @param config Partial configuration to apply */ configureOutlierDetection(config: Partial): void; enable(): Promise; start(): Promise; protected stop(): void; checkPermission(): Promise; requestPermission(): Promise; } //#endregion //#region src/sensors/devicemotion/devicemotion.d.ts /** * Configuration options for the device motion sensor. */ interface MotionSensorConfig { /** Step detection threshold (m/s²). Lower = more sensitive. */ stepDetectionThreshold: number; /** Step length in meters. */ stepLength: number; /** Stride adaptation factor (0-1). Higher = vary step length with cadence. */ strideAdaptation: number; /** Motion detection threshold (m/s²). Lower = more responsive. */ motionThreshold: number; /** Enable PDR displacement calculation. */ pdrEnabled: boolean; } /** * Sensor that provides device motion data (acceleration). * Useful for detecting if the user is stationary or moving, * which can affect confidence in position estimates. * * PDR (Pedestrian Dead Reckoning) logic is implemented using pure functions * from `./pdr.ts` for testability. The sensor manages state and coordinates * the pure functions. */ declare class DeviceMotionSensor extends BaseSensor { #private; readonly id = "devicemotion"; get requiresPermission(): boolean; protected start: () => Promise; protected stop: () => void; checkPermission: () => Promise; requestPermission: () => Promise; /** * Whether the device is currently detected as moving. */ get isMoving(): boolean; /** * Set current heading for PDR displacement calculation. * Called by SensorRegistry when heading updates arrive from deviceorientation. * Applies exponential smoothing to prevent zigzag paths from heading jitter. * @param heading Heading in degrees (0-360) */ setHeading(heading: number): void; /** * Configure the sensor parameters at runtime. * @param config Partial configuration to apply */ configure(config: Partial): void; /** * Get current configuration values. */ getConfig(): MotionSensorConfig; } //#endregion //#region src/sensors/manual/manual-sensor.d.ts /** * Options for reporting a manual position update. */ interface ManualPositionOptions { /** Latitude in degrees */ latitude: number; /** Longitude in degrees */ longitude: number; /** Accuracy in meters */ accuracy?: number; /** Heading in degrees (0-360, clockwise from north) */ heading?: number; /** Floor level (elevation) */ floorLevel?: number; /** * Confidence score (0-1) indicating how much to trust this position. * Higher confidence = more influence on the fused position. * @default 0.5 */ confidence?: number; /** Timestamp in milliseconds. Defaults to Date.now() */ timestamp?: number; } /** * Options for reporting a manual relative displacement. */ interface ManualDisplacementOptions { /** Displacement in meters along east-west axis (positive = east) */ dx: number; /** Displacement in meters along north-south axis (positive = north) */ dy: number; /** Heading in degrees (optional) */ heading?: number; /** * Confidence score (0-1) indicating how much to trust this displacement. * @default 0.8 */ confidence?: number; /** Timestamp in milliseconds. Defaults to Date.now() */ timestamp?: number; } /** * A manual sensor that allows programmatic position reporting. * * This sensor enables integrating external positioning systems (IPS, beacons, * custom algorithms) into the BlueDot fusion engine. Positions are weighted * by confidence and combined with other sensor data. * * @example Basic IPS integration * ```typescript * const manual = blueDot.Sensors.get('manual'); * * // Report position from your IPS with confidence * myIPS.onPosition((pos) => { * manual.reportPosition({ * latitude: pos.lat, * longitude: pos.lon, * accuracy: pos.accuracy, * confidence: pos.confidence, * }); * }); * ``` */ declare class ManualSensor extends BaseSensor { #private; readonly id: "manual"; readonly requiresPermission = false; /** * Report an absolute position to the fusion engine. * * The position will be combined with other sensor data based on its * confidence score. Higher confidence = more influence on the result. * * @param options - Position data with confidence * * @example * ```typescript * manualSensor.reportPosition({ * latitude: 43.4723, * longitude: -80.5449, * accuracy: 5, * confidence: 0.9, * }); * ``` */ reportPosition(options: ManualPositionOptions): void; /** * Report a relative displacement to the fusion engine. * * Use this for dead-reckoning style updates where you know the * displacement from the previous position but not the absolute location. * Only meaningful when an anchor is active. * * @param options - Displacement data with confidence * * @example * ```typescript * manualSensor.reportDisplacement({ * dx: 0.5, // 0.5 meters east * dy: 1.0, // 1.0 meters north * confidence: 0.8, * }); * ``` */ reportDisplacement(options: ManualDisplacementOptions): void; /** * Force a position as an anchor, overriding all other sensors for the specified duration. * * This is a convenience method that wraps `setAnchor()` for the manual sensor. * The position will completely dominate the fusion engine until the TTL expires. * * @param options - Position and duration options * * @example * ```typescript * manualSensor.forcePosition({ * latitude: 43.4723, * longitude: -80.5449, * heading: 90, * ttl: 30000, // 30 seconds * }); * ``` */ forcePosition(options: { latitude: number; longitude: number; heading?: number; floorLevel?: number; /** How long the forced position remains active in milliseconds. @default 30000 */ ttl?: number; }): void; /** * Clear the forced position set by this sensor. * After clearing, the fusion engine will fall back to other position sources. */ clearForcedPosition(): void; protected start(): Promise; protected stop(): void; checkPermission(): Promise; requestPermission(): Promise; } //#endregion //#region src/sensors/sensor-registry.d.ts /** * Maps built-in sensor IDs to their concrete sensor types. */ type BuiltInSensorMap = { geolocation: GeolocationSensor; deviceorientation: DeviceOrientationSensor; devicemotion: DeviceMotionSensor; manual: ManualSensor; }; /** * Registry for managing position sensors. * * @example * ```ts * // Enable built-in sensors * blueDot.Sensors.get('geolocation')?.enable(); * blueDot.Sensors.get('deviceorientation')?.enable(); * * // Register and enable a custom sensor * const customSensor = blueDot.Sensors.register(new MyCustomSensor()); * customSensor.enable(); * ``` */ declare class SensorRegistry extends PubSub { #private; constructor(); /** * Register a sensor. * @param sensor The sensor to register * @returns The sensor if registered, undefined if already registered */ register(sensor: T & { id: CustomSensorId; }): T | undefined; /** * Get a registered sensor by ID. * @param sensorId The ID of the sensor to get * @returns The sensor, or undefined if not found */ get(sensorId: K): K extends keyof BuiltInSensorMap ? BuiltInSensorMap[K] | undefined : BaseSensor | undefined; /** * Unregister a sensor. * @param sensor The sensor to unregister */ unregister(sensor: BaseSensor): void; /** * Unregister a sensor. * @param sensorId The ID of the sensor to unregister */ unregister(sensorId: SensorId): void; /** * Get all registered sensors. * @returns An iterator of [sensorId, sensor] pairs */ getAll(): IterableIterator<[SensorId, BaseSensor]>; /** * Destroy the registry and clean up all sensors. */ destroy(): void; } //#endregion //#region src/fusion/algorithms/types.d.ts /** * Interface for heading fusion algorithms. * Combines compass, gyroscope, and GPS heading for stable heading estimation. */ interface HeadingFusionAlgorithm { /** * Update with compass heading (absolute but noisy). * @param heading Compass heading in degrees [0, 360) * @param timestamp Update timestamp in milliseconds */ updateCompass(heading: number, timestamp: number): void; /** * Update with gyroscope angular velocity (smooth relative changes). * @param angularVelocity Angular velocity in deg/s around vertical axis * @param timestamp Update timestamp in milliseconds */ updateGyro(angularVelocity: number, timestamp: number): void; /** * Correct heading with GPS ground truth when moving. * @param gpsHeading GPS heading in degrees [0, 360) * @param speed Current speed in m/s */ correctWithGps(gpsHeading: number, speed: number): void; /** * Get the current fused heading. * @returns Fused heading in degrees [0, 360), or null if not initialized */ getHeading(): number | null; /** * Reset the algorithm state. */ reset(): void; } //#endregion //#region src/fusion/algorithms/heading-filter.d.ts /** * Configuration options for the heading fusion filter. */ interface HeadingFilterConfig { /** Complementary filter alpha (0-1). Higher = more gyro weight. */ complementaryAlpha: number; /** Minimum angular velocity to integrate (deg/s). */ gyroNoiseThreshold: number; /** Maximum heading error before snapping to compass (degrees). */ maxHeadingError: number; /** Enable magnetic disturbance detection. */ magneticGatingEnabled: boolean; /** Minimum heading delta to emit updates (degrees). */ deltaThreshold: number; /** Calibration offset applied to all compass readings (degrees). Set from VPS ground truth. */ calibrationOffset: number; } /** * Complementary filter for heading fusion. * Combines compass (absolute but noisy), gyroscope (smooth relative changes), * and GPS heading (ground truth when moving) to produce stable heading. * * The filter works by: * 1. Short-term: Integrate gyroscope angular velocity for smooth rotations * 2. Long-term: Slowly blend toward compass to prevent gyroscope drift * 3. Correction: When GPS heading is valid (moving fast enough), use as ground truth * * @example * ```ts * const filter = new HeadingFusionFilter(); * * // Update with compass reading * filter.updateCompass(45, Date.now()); * * // Update with gyroscope angular velocity * filter.updateGyro(5.0, Date.now()); // 5 deg/s rotation * * // Correct with GPS when moving * filter.correctWithGps(47, 1.5); // heading 47°, speed 1.5 m/s * * // Get fused heading * const heading = filter.getHeading(); // ~46° * ``` */ declare class HeadingFusionFilter implements HeadingFusionAlgorithm { #private; /** * Update the filter with a compass heading reading. * The compass provides absolute heading but is susceptible to magnetic interference. * @param heading Compass heading in degrees [0, 360) * @param timestamp Update timestamp in milliseconds */ updateCompass(heading: number, _timestamp: number): void; /** * Update the filter with gyroscope angular velocity. * The gyroscope provides smooth relative heading changes immune to magnetic fields. * @param angularVelocity Angular velocity around vertical axis in deg/s * @param timestamp Update timestamp in milliseconds */ updateGyro(angularVelocity: number, timestamp: number): void; /** * Correct the heading with GPS when available. * GPS heading is only considered when moving fast enough and with sufficient confidence. * @param gpsHeading GPS heading in degrees [0, 360) * @param speed Current speed in m/s * @param confidence GPS confidence (0-1), affects how much GPS heading influences result */ correctWithGps(gpsHeading: number, speed: number, confidence?: number): void; /** * Set a calibration offset derived from a known ground-truth heading (e.g. VPS). * The offset is applied to all subsequent compass readings to correct systematic * magnetic bias at the calibration location. * @param offsetDegrees Offset in degrees (groundTruthHeading - deviceCompassHeading) */ setCalibrationOffset(offsetDegrees: number): void; /** * Configure the filter parameters at runtime. * @param config Partial configuration to apply */ configure(config: Partial): void; /** * Get current configuration values. */ getConfig(): HeadingFilterConfig; /** * Check if magnetic disturbance is currently detected. */ isMagneticDisturbanceActive(): boolean; /** * Get the current fused heading. * @returns Fused heading in degrees [0, 360), or null if not initialized */ getHeading(): number | null; /** * Check if the filter has been initialized with at least one compass reading. * @returns True if filter is initialized */ isInitialized(): boolean; /** * Get the last compass heading received. * @returns Last compass heading in degrees, or null if none received */ getLastCompassHeading(): number | null; /** * Reset the filter state. * Call on floor changes or when heading should be re-initialized. */ reset(): void; } //#endregion //#region src/fusion/algorithms/multi-sensor-ekf.d.ts /** * Configuration options for the Multi-Sensor EKF. */ interface MultiSensorEKFOptions { /** * Half-life for uncertainty growth in milliseconds. * The state uncertainty doubles every halfLifeMs when no measurements arrive. * Lower values = faster decay of old estimates. * @default 10000 (10 seconds) */ halfLifeMs: number; } /** * Multi-sensor Extended Kalman Filter for position fusion. * * Combines position measurements from multiple sources (GPS, VPS, anchors) and * displacement measurements from PDR using a constant-velocity motion model. * * Key features: * - All methods take explicit timestamps (no Date.now() inside) * - Confidence-weighted fusion via measurement noise parameter * - Half-life decay of old estimates via process noise scaling * - Supports both absolute position and relative displacement updates * * State vector: [latitude, longitude, velocity_lat, velocity_lon] * * @example * ```ts * const ekf = new MultiSensorEKF({ halfLifeMs: 10000 }); * * // Initialize with first position * ekf.updatePosition(43.123, -79.456, 0.0001, 0); * * // Predict forward * ekf.predict(1000); * * // Update with new GPS measurement (low confidence = high noise) * ekf.updatePosition(43.124, -79.455, 0.001, 1000); * * // Get fused position * const pos = ekf.getPosition(); // { latitude: ~43.1235, longitude: ~-79.4555 } * ``` */ declare class MultiSensorEKF { #private; constructor(options?: Partial); /** * Predict step: project state forward to the given timestamp. * Increases uncertainty based on elapsed time and half-life configuration. * * @param timestampMs - Current timestamp in milliseconds */ predict(timestampMs: number): void; /** * Update state with a position measurement (GPS, VPS, anchor). * * @param latitude - Measured latitude in degrees * @param longitude - Measured longitude in degrees * @param measurementNoise - Measurement noise variance (higher = less trust) * @param timestampMs - Measurement timestamp in milliseconds */ updatePosition(latitude: number, longitude: number, measurementNoise: number, timestampMs: number): void; /** * Update state with an odometry measurement (PDR displacement). * * Unlike position updates, odometry provides relative displacement rather than * absolute position. The displacement is added directly to the state. * * @param dx - Displacement east in meters (positive = east) * @param dy - Displacement north in meters (positive = north) * @param measurementNoise - Measurement noise variance (higher = less trust) * @param timestampMs - Measurement timestamp in milliseconds */ updateOdometry(dx: number, dy: number, measurementNoise: number, timestampMs: number): void; /** * Get the current position estimate. * @returns Position or null if filter not initialized */ getPosition(): { latitude: number; longitude: number; } | null; /** * Get the current velocity estimate. * @returns Velocity in degrees per second, or null if filter not initialized */ getVelocity(): { vLat: number; vLon: number; } | null; /** * Get the current position uncertainty (trace of position covariance). * Higher values indicate less certainty in the position estimate. * @returns Uncertainty value, or Infinity if filter not initialized */ getUncertainty(): number; /** * Get the last update timestamp. * @returns Timestamp in ms, or null if filter not initialized */ getLastTimestamp(): number | null; /** * Check if the filter has been initialized with at least one measurement. */ isInitialized(): boolean; /** * Reset the filter state. * Call on floor changes, large position jumps, or time gaps. */ reset(): void; } //#endregion //#region src/fusion/fusion-engine.d.ts /** * Multi-sensor fusion engine that combines position updates from multiple sources. * * Uses an Extended Kalman Filter (EKF) for position fusion with: * - Confidence-weighted measurements (high confidence = more influence) * - Half-life decay of old estimates (uncertainty grows over time) * - Hybrid anchor behavior (anchor-only period, then transition to GPS) * * Heading fusion uses a separate complementary filter combining compass and gyroscope. */ declare class FusionEngine extends PubSub { #private; constructor(registry: SensorRegistry, config?: Partial); /** * Get the current fusion configuration. */ get config(): Readonly; /** * Get the current anchor state. * @param nowMs - Current timestamp (defaults to Date.now()) */ getAnchorState(nowMs?: number): AnchorState; /** * Get the current position anchor if one is set and not expired. */ get anchor(): Readonly | undefined; /** * Get the current accumulated displacement from the anchor in meters. * Returns (0, 0) if no anchor is set or no movement has occurred. */ get displacement(): Readonly; /** * Set an absolute position anchor from a calibration source (VPS, AI Localizer). * When an anchor is active, it provides the absolute position and other sensors * can provide relative updates from this reference point. * * @param anchor - The position anchor to set * @param configOverride - Optional config overrides for this anchor (e.g., anchorOnlyPeriodMs) */ setAnchor(anchor: PositionAnchor, configOverride?: Partial>): void; /** * Clear the current position anchor and accumulated displacement. */ clearAnchor(): void; /** * Reset the EKF state. * Call on floor changes or large position jumps. */ resetEKF(): void; /** * Reset the heading fusion filter state. * Call on floor changes or when heading should be re-initialized. */ resetHeadingFilter(): void; /** * Get the heading fusion filter for configuration. * @internal Used by debug tools for runtime tuning. */ get headingFilter(): HeadingFusionFilter; /** * Get the multi-sensor EKF for inspection. * @internal Used by debug tools for runtime inspection. */ get ekf(): MultiSensorEKF; /** * Get the last fused position. * @returns The last fused position, or undefined if none available */ getLastPosition(): FusedPosition | undefined; destroy(): void; } //#endregion //#region src/blue-dot.d.ts /** * Show a Blue Dot indicating the device's position on the map. * * @example * ```ts * import { show3dMap } from '@mappedin/mappedin-js'; * import { BlueDot } from '@mappedin/blue-dot'; * * const mapView = await show3dMap(...); * * // Enable BlueDot * const blueDot = new BlueDot(mapView).enable(); * * // Option 1: Listen for position updates from the device * blueDot.on('position-update', (position) => { * console.log('User position:', position); * }); * * // Option 2: Update position manually * blueDot.update({ latitude, longitude, accuracy, floorOrFloorId }); * * ``` */ declare class BlueDot implements MapViewExtension { #private; /** * Create a new {@link BlueDot} instance. */ constructor(mapView: MapView); /** Sensors API */ get Sensors(): SensorRegistry; /** * Fusion engine API. * @internal Used by debug tools for runtime tuning and inspection. */ get Fusion(): FusionEngine; /** * Get the current position anchor if one is set and not expired. * Anchors provide absolute ground truth positions from calibration sources (VPS, AI Localizer). */ get anchor(): Readonly | undefined; /** * Get the current accumulated displacement from the anchor in meters. * Returns { dx: 0, dy: 0 } if no anchor is set or no movement has occurred. */ get displacement(): Readonly; /** * Get the Model for the BlueDot core element. */ get dotModel(): Model | undefined; /** * Get the Model for the accuracy ring. */ get accuracyRingModel(): Model | undefined; /** * Get the Model for the heading cone. */ get headingConeModel(): Model | undefined; /** * Whether the BlueDot is currently enabled. */ get isEnabled(): boolean; /** * The current state of the BlueDot. Can be 'hidden', 'active', 'inactive', or 'disabled'. * Listen for state changes using the 'status-change' event. * * @example * mapView.BlueDot.on('status-change', ({ status }) => { * if (status === 'active') { * // BlueDot is visible and tracking * } * }); */ get status(): BlueDotStatus; /** * Whether the BlueDot is currently following the user (camera follow mode). */ get isFollowing(): boolean; /** * The direction the user is facing in degrees from north clockwise. * @see https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates/heading */ get heading(): GeolocationPosition['coords']['heading'] | undefined; /** * The accuracy of the current position in metres. */ get accuracy(): GeolocationPosition['coords']['accuracy'] | undefined; /** * The coordinate of the current position. */ get coordinate(): Coordinate | undefined; /** * Returns the current Blue Dot state. */ getState(): ReadonlyDeep; /** * The floor the Blue Dot is currently on. If undefined, the Blue Dot will appear on every floor. */ get floor(): Floor | undefined; /** * Force the Blue Dot to a specific position for a duration, overriding all other sensors. * * This sets a high-confidence anchor that completely dominates the fusion engine * for the specified duration. GPS, compass, and other sensors are ignored while * the forced position is active. After the duration expires, normal sensor fusion resumes. * * Use this when you have an authoritative position from an external system * (e.g., VPS, QR code scan, manual user correction) that should override everything else. * * @param position - The position to force * @param durationMs - How long to maintain this position in milliseconds (default: 30 seconds) * * @example Force position from VPS scan * ```ts * blueDot.forcePosition({ * latitude: 43.4723, * longitude: -80.5449, * heading: 90, * floorLevel: 2, * }, 30000); // 30 seconds * ``` * * @example Force position indefinitely (call again with different position or let it expire) * ```ts * blueDot.forcePosition({ * latitude: 43.4723, * longitude: -80.5449, * }, Infinity); * ``` */ forcePosition(position: { latitude: number; longitude: number; heading?: number; floorLevel?: number; }, durationMs?: number): void; /** * Report a position to the fusion engine with a confidence score. * * Unlike {@link forcePosition}, this does NOT override other sensors. Instead, * the reported position is weighted by confidence and combined with GPS, compass, * and other active sensors through the fusion engine. * * Use this for integrating external positioning systems (IPS, beacons, WiFi RTT) * that should contribute to but not dominate the fused position. * * @param options - Position data with confidence score * * @example Simple IPS integration * ```ts * // Low confidence - will blend with GPS * blueDot.reportPosition({ * latitude: 43.4723, * longitude: -80.5449, * accuracy: 10, * confidence: 0.3, * }); * ``` * * @example High confidence beacon * ```ts * // High confidence - will dominate over GPS * blueDot.reportPosition({ * latitude: 43.4723, * longitude: -80.5449, * accuracy: 2, * confidence: 0.95, * }); * ``` */ reportPosition(options: ManualPositionOptions): void; /** * Enable a sensor by ID and request permissions if needed. * @param sensorId - The ID of the sensor to enable * @returns The permission state after enabling */ __enableSensor(sensorId: string): Promise; /** * Disable a sensor by ID. * @param sensorId - The ID of the sensor to disable */ __disableSensor(sensorId: string): void; /** * Check the current permission state for a sensor. * @param sensorId - The ID of the sensor to check * @returns The current permission state */ __checkSensorPermission(sensorId: string): Promise; /** * Request permission for a sensor. Must be called in response to a user gesture. * @param sensorId - The ID of the sensor * @returns The permission state after requesting */ __requestSensorPermission(sensorId: string): Promise; /** * Check if a sensor is currently enabled. * @param sensorId - The ID of the sensor to check * @returns Whether the sensor is enabled */ __isSensorEnabled(sensorId: string): boolean; /** * Enable the Blue Dot. It will be hidden until a position is received either from the browser or by calling {@link BlueDot.update}. * @param options - The options to setup the Blue Dot (see {@link BlueDotUpdateState}). * * @example Enable with default options * ```ts * const blueDot = new BlueDot(mapView); * blueDot.enable(); * ``` * @example Enable with custom color and accuracy ring * ```ts * const blueDot = new BlueDot(mapView); * blueDot.enable({ color: '#00ff00', accuracyRing: { color: '#00ff00', opacity: 0.2 } }); * ``` * * @see See the [BlueDot Guide](https://developer.mappedin.com/web-sdk/blue-dot) for more information. */ enable: (options?: BlueDotUpdateState) => void; /** * Disable the Blue Dot. It will be hidden and no longer update. * * The status transition to 'disabled' occurs after all cleanup operations complete. * This ensures that any 'status-change' event listeners see the BlueDot in a fully * cleaned up state with all resources released. */ disable: () => void; /** * Subscribe to a BlueDot event. * @param eventName The name of the event to listen for. * @param fn The function to call when the event is emitted. */ on: (eventName: BlueDotEventName, fn: (payload: BlueDotEventPayloads[BlueDotEventName] extends { data: null; } ? BlueDotEventPayloads[BlueDotEventName]["data"] : BlueDotEventPayloads[BlueDotEventName]) => void) => () => void; /** * Unsubscribe from a BlueDot event. * @param eventName The name of the event to unsubscribe from. * @param fn The function to unsubscribe from the event. */ off: (eventName: BlueDotEventName, fn: (payload: BlueDotEventPayloads[BlueDotEventName] extends { data: null; } ? BlueDotEventPayloads[BlueDotEventName]["data"] : BlueDotEventPayloads[BlueDotEventName]) => void) => void; /** * Update the BlueDot state after it has been enabled. * This allows overriding previously set values like colors, and other options. * @param options - The options to update * * @example Update color and accuracy ring * ```ts * const blueDot = new BlueDot(mapView); * blueDot.updateState({ * color: '#ff0000', * accuracyRing: { color: '#ff0000', opacity: 0.5 } * }); * ``` */ updateState: (options: BlueDotUpdateState) => void; /** * Enable or disable the devices's geolocation listener to automatically position the Blue Dot. * If enabled, the device will request permission to access the user's precise location. * * @remarks This will emit a 'position-update' event every time a new position is received. * @deprecated Use the {@link BlueDot.Sensors} API and fusion engine for position updates instead. * * @param watch - Whether to enable or disable the listener. */ watchDevicePosition: (watch: boolean) => void; /** * Enable or disable the device orientation listener to automatically update the Blue Dot's heading. * This must be enabled in response to a user action such as a tap or click and cannot be enabled automatically. * * @remarks This will emit a 'device-orientation-update' event every time the device's orientation changes. * Device orientation changes will not emit a 'position-update' event. * @deprecated Use the {@link BlueDot.Sensors} API and fusion engine for heading updates instead. * * @see https://www.w3.org/TR/orientation-event/#dom-deviceorientationevent-requestpermission * * @param watch - Whether to enable or disable the listener. * * @example Enable device orientation listener * ```ts * const blueDot = new BlueDot(mapView); * // Enable device orientation on button click * button.addEventListener('click', () => { * blueDot.watchDeviceOrientation(true); * }); * ``` */ watchDeviceOrientation: (watch: boolean) => Promise; /** * Set the Blue Dot position to specific coordinates or override some properties of the device geolocation. * * @deprecated Use {@link forcePosition} to set an authoritative position that overrides all sensors, * or {@link reportPosition} to feed a confidence-weighted position into the fusion engine. * * @remarks If `floorLevel` is provided with {@link GeolocationPositionExtended}, the floor will be resolved asynchronously and the position will not be applied until this is completed. * * @param position - The position to set. Use {@link BlueDotPositionUpdate} for manual positioning, * a GeolocationPosition object, or undefined to clear the manual position. * @param options - Optional update options (silent, animate). * * @example Set position with floor * ```ts * blueDot.update({ * latitude: 43.123, * longitude: -79.456, * accuracy: 5, * floorOrFloorId: floor * }); * ``` * @example Clear manual position properties and return to device geolocation * ```ts * blueDot.update(undefined); * ``` */ update: (position: GeolocationPositionExtended | BlueDotPositionUpdate | undefined, options?: BlueDotUpdateOptions) => void; /** * Set the camera to follow the BlueDot in various modes. User interaction will cancel following automatically. * @param mode The follow mode ('position-only', 'position-and-heading', 'position-and-path-direction', or false to disable). * @param cameraOptions Optional camera options (zoom, pitch, etc.). * * @example * mapView.BlueDot.follow('position-and-heading', { zoomLevel: 21, pitch: 45 }); */ follow: (mode: FollowMode, cameraOptions?: FollowCameraOptions) => void; /** * Set a position processor callback that allows intercepting and modifying device/geolocation position updates before they are applied. * * **Note**: This processor only applies to automatic position updates from device geolocation. * Manual position updates via `update()` method bypass the processor and are applied directly. * * @param processor - A callback function that receives current state and incoming update. Return undefined to discard the update, or return a modified update object. * * @example Discard inaccurate positions * ```ts * blueDot.setPositionProcessor((current, incoming) => { * if (incoming.accuracy && incoming.accuracy > 50) { * return undefined; // Discard update * } * return incoming; // Accept update * }); * ``` * * @example Modify incoming positions * ```ts * blueDot.setPositionProcessor((current, incoming) => { * // Apply custom smoothing or validation logic * return { * ...incoming, * accuracy: Math.min(incoming.accuracy || 100, 10) // Cap accuracy * }; * }); * ``` */ setPositionProcessor(processor?: BlueDotPositionProcessor): void; destroy: () => void; } //#endregion //#region src/debug/tuning-config.d.ts /** * Human-readable tuning knobs for indoor positioning. * These are debug-only settings that can be adjusted per-map/venue. */ /** * DeviceOrientation (compass/heading) tuning options. * Controls how compass readings influence the displayed heading. */ interface HeadingTuningConfig { /** * Controls how quickly compass readings influence heading. * Range: 0-100 (percentage) * - Higher values = respond faster to compass but may jitter * - Lower values = smoother heading but slower to correct * @default 30 */ headingResponsiveness: number; /** * Controls how aggressively to ignore noisy compass readings. * Range: 0-100 (percentage) * - Higher values = favor stability, ignore small movements * - Lower values = more reactive to subtle heading changes * @default 50 */ compassNoiseTolerance: number; /** * If compass differs by more than this threshold (degrees), snap immediately. * Range: 10-90 degrees * - Lower values = correct faster but may cause visible jumps * - Higher values = smoother blending but slower error correction * @default 30 */ headingSnapThreshold: number; /** * Enable magnetic disturbance detection. * When enabled, temporarily reduces compass influence during erratic readings. * @default true */ magneticDisturbanceGating: boolean; /** * Minimum heading change (degrees) before emitting update. * Range: 1-10 degrees * - Higher values = reduce noise but feel less responsive * - Lower values = more updates but may jitter * @default 2 */ headingChangeThreshold: number; } /** * DeviceMotion (PDR/steps) tuning options. * Controls step detection and dead reckoning behavior. */ interface MotionTuningConfig { /** * Controls how easily steps are detected. * Range: 0-100 (percentage) * - Higher values = detect more steps, may create false positives * - Lower values = only detect strong steps, may miss gentle walking * @default 50 */ stepSensitivity: number; /** * Average distance per step in meters. * Range: 0.4-1.2 meters * - Adjust based on user demographics (adults ~0.7m, shorter stride ~0.5m) * @default 0.7 */ averageStepLength: number; /** * Allow step length to vary with walking cadence. * Range: 0-100 (percentage) * - Higher values = adapt stride to walking speed (faster = longer steps) * - Lower values = fixed step length * @default 0 */ strideVariability: number; /** * Controls how quickly motion state changes between stationary/moving. * Range: 0-100 (percentage) * - Higher values = react faster to motion changes * - Lower values = require sustained motion before changing state * @default 50 */ motionResponsiveness: number; /** * Master toggle for step-based dead reckoning. * When disabled, PDR displacement is not calculated. * @default true */ pdrEnabled: boolean; } /** * Complete tuning configuration for indoor positioning. */ interface TuningConfig { heading: HeadingTuningConfig; motion: MotionTuningConfig; } /** * Default tuning configuration values. */ declare const DEFAULT_TUNING_CONFIG: TuningConfig; /** * Convert human-readable heading config to internal filter parameters. */ declare function headingConfigToFilterParams(config: HeadingTuningConfig): { complementaryAlpha: number; gyroNoiseThreshold: number; maxHeadingError: number; magneticGatingEnabled: boolean; deltaThreshold: number; }; /** * Convert human-readable motion config to internal sensor parameters. */ declare function motionConfigToSensorParams(config: MotionTuningConfig): { stepDetectionThreshold: number; stepLength: number; strideAdaptation: number; motionThreshold: number; pdrEnabled: boolean; }; /** * Validate and normalize a tuning config, clamping values to valid ranges. */ declare function normalizeTuningConfig(config: Partial): TuningConfig; //#endregion //#region src/debug/debug-hud.d.ts /** * Mobile-friendly debug HUD for BlueDot. * Shows a minimal collapsed bar at the bottom of the screen that expands to show full details. */ declare class DebugHUD { #private; constructor(blueDot: BlueDot); /** * Clear all log entries from the console. */ clearConsole(): void; /** * Get the current tuning configuration. */ getTuningConfig(): TuningConfig; /** * Set the tuning configuration. */ setTuningConfig(config: Partial): void; /** * Show the HUD (if hidden). */ show(): void; /** * Hide the HUD. */ hide(): void; /** * Whether the HUD is currently expanded (full-screen mode). */ get isExpanded(): boolean; /** * Expand the HUD to full-screen mode. * Use this to programmatically show the detailed view. */ expand(): void; /** * Collapse the HUD to the minimal bar. * Use this to programmatically hide the detailed view. */ collapse(): void; /** * Toggle between expanded and collapsed states. * @returns The new expanded state (true = expanded, false = collapsed) */ toggleExpanded(): boolean; /** * Clean up the HUD and remove it from the DOM. */ destroy(): void; } //#endregion //#region src/debug/telemetry.d.ts /** * Debug telemetry for tracking sensor and fusion events. * Used internally by the debug panel to display real-time diagnostics. */ /** * Telemetry event types. */ interface TelemetryEvents { /** Compass update received */ 'compass-update': { heading: number; timestamp: number; }; /** Gyroscope update received */ 'gyro-update': { angularVelocity: number; timestamp: number; }; /** Step detected by PDR */ 'step-detected': { stepLength: number; heading: number; timestamp: number; }; /** Motion state changed */ 'motion-state': { isMoving: boolean; timestamp: number; }; /** Magnetic disturbance detected/cleared */ 'magnetic-disturbance': { active: boolean; timestamp: number; }; /** Heading fused */ 'heading-fused': { heading: number; compassHeading: number | null; timestamp: number; }; /** Position update */ 'position-update': { latitude: number; longitude: number; accuracy: number; timestamp: number; }; } type TelemetryEventName = keyof TelemetryEvents; type TelemetryCallback = (data: TelemetryEvents[T]) => void; /** * Rolling statistics tracker for a numeric metric. */ declare class MetricTracker { #private; constructor(maxSamples?: number); /** * Record a sample value. */ record(value: number, timestamp?: number): void; /** * Get the rate of events per second over the sample window. */ getRate(): number; /** * Get the average of recorded samples. */ getAverage(): number; /** * Get the latest sample. */ getLatest(): number | undefined; /** * Get the sample count. */ getCount(): number; /** * Clear all samples. */ clear(): void; } /** * Event counter for tracking event frequency. */ declare class EventCounter { #private; constructor(windowMs?: number); /** * Record an event occurrence. */ tick(timestamp?: number): void; /** * Get events per second in the window. */ getRate(now?: number): number; /** * Get count of events in the window. */ getCount(now?: number): number; /** * Clear all events. */ clear(): void; } /** * Telemetry collector for BlueDot debug diagnostics. */ declare class Telemetry { #private; /** Event counters */ compassUpdates: EventCounter; gyroUpdates: EventCounter; stepsDetected: EventCounter; positionUpdates: EventCounter; /** Metric trackers */ compassHeading: MetricTracker; gyroAngularVelocity: MetricTracker; stepLength: MetricTracker; positionAccuracy: MetricTracker; /** State flags */ isMoving: boolean; magneticDisturbanceActive: boolean; lastFusedHeading: number | null; /** * Enable telemetry collection. */ enable(): void; /** * Disable telemetry collection. */ disable(): void; /** * Check if telemetry is enabled. */ get isEnabled(): boolean; /** * Emit a telemetry event. */ emit(event: T, data: TelemetryEvents[T]): void; /** * Subscribe to a telemetry event. */ on(event: T, callback: TelemetryCallback): () => void; /** * Get a summary of current telemetry state. */ getSummary(): TelemetrySummary; /** * Clear all telemetry data. */ clear(): void; /** * Destroy the telemetry instance. */ destroy(): void; } /** * Telemetry summary for display. */ interface TelemetrySummary { compassRate: number; gyroRate: number; stepRate: number; positionRate: number; avgCompassHeading: number; avgGyroVelocity: number; avgStepLength: number; avgAccuracy: number; isMoving: boolean; magneticDisturbanceActive: boolean; lastFusedHeading: number | null; } //#endregion //#region src/debug/index.d.ts /** * Enable the debug HUD overlay for a BlueDot instance. * Shows real-time sensor data, fusion state, and position information in a mobile-friendly overlay. * * The HUD appears as a small bar at the bottom of the screen that can be tapped to expand * and show full details including Position, Fusion state, and Sensor status. * * @param blueDot - The BlueDot instance to debug * @returns The DebugHUD instance. Call `.destroy()` to remove the HUD. * * @example * ```ts * import { BlueDot } from '@mappedin/blue-dot'; * import { enableDebugHUD } from '@mappedin/blue-dot/debug'; * * const blueDot = new BlueDot(mapView); * blueDot.enable(); * * // Enable debug HUD * const debugHUD = enableDebugHUD(blueDot); * * // Later, to remove the HUD: * debugHUD.destroy(); * ``` */ declare function enableDebugHUD(blueDot: BlueDot): DebugHUD; //#endregion export { DEFAULT_TUNING_CONFIG, type DebugHUD, EventCounter, type HeadingTuningConfig, MetricTracker, type MotionTuningConfig, Telemetry, type TelemetrySummary, type TuningConfig, enableDebugHUD, headingConfigToFilterParams, motionConfigToSensorParams, normalizeTuningConfig };