chore: fix client & server processing for invoke

This commit is contained in:
Khietsly Tristan 2026-04-22 08:41:14 +07:00
commit 57f381d335
1313 changed files with 1643 additions and 523700 deletions

View file

@ -1,26 +0,0 @@
--[[
The Context object implements a write-once key-value store. It also allows
for a new Context object to inherit the entries from an existing one.
]]
local Context = {}
function Context.new(parent)
local meta = {}
local index = {}
meta.__index = index
if parent then
for key, value in pairs(getmetatable(parent).__index) do
index[key] = value
end
end
function meta.__newindex(_obj, key, value)
assert(index[key] == nil, string.format("Cannot reassign %s in context", tostring(key)))
index[key] = value
end
return setmetatable({}, meta)
end
return Context

View file

@ -1,311 +0,0 @@
--[[
Allows creation of expectation statements designed for behavior-driven
testing (BDD). See Chai (JS) or RSpec (Ruby) for examples of other BDD
frameworks.
The Expectation class is exposed to tests as a function called `expect`:
expect(5).to.equal(5)
expect(foo()).to.be.ok()
Expectations can be negated using .never:
expect(true).never.to.equal(false)
Expectations throw errors when their conditions are not met.
]]
local Expectation = {}
--[[
These keys don't do anything except make expectations read more cleanly
]]
local SELF_KEYS = {
to = true,
be = true,
been = true,
have = true,
was = true,
at = true,
}
--[[
These keys invert the condition expressed by the Expectation.
]]
local NEGATION_KEYS = {
never = true,
}
--[[
Extension of Lua's 'assert' that lets you specify an error level.
]]
local function assertLevel(condition, message, level)
message = message or "Assertion failed!"
level = level or 1
if not condition then
error(message, level + 1)
end
end
--[[
Returns a version of the given method that can be called with either . or :
]]
local function bindSelf(self, method)
return function(firstArg, ...)
if firstArg == self then
return method(self, ...)
else
return method(self, firstArg, ...)
end
end
end
local function formatMessage(result, trueMessage, falseMessage)
if result then
return trueMessage
else
return falseMessage
end
end
--[[
Create a new expectation
]]
function Expectation.new(value)
local self = {
value = value,
successCondition = true,
condition = false,
matchers = {},
_boundMatchers = {},
}
setmetatable(self, Expectation)
self.a = bindSelf(self, self.a)
self.an = self.a
self.ok = bindSelf(self, self.ok)
self.equal = bindSelf(self, self.equal)
self.throw = bindSelf(self, self.throw)
self.near = bindSelf(self, self.near)
return self
end
function Expectation.checkMatcherNameCollisions(name)
if SELF_KEYS[name] or NEGATION_KEYS[name] or Expectation[name] then
return false
end
return true
end
function Expectation:extend(matchers)
self.matchers = matchers or {}
for name, implementation in pairs(self.matchers) do
self._boundMatchers[name] = bindSelf(self, function(_self, ...)
local result = implementation(self.value, ...)
local pass = result.pass == self.successCondition
assertLevel(pass, result.message, 3)
self:_resetModifiers()
return self
end)
end
return self
end
function Expectation.__index(self, key)
-- Keys that don't do anything except improve readability
if SELF_KEYS[key] then
return self
end
-- Invert your assertion
if NEGATION_KEYS[key] then
local newExpectation = Expectation.new(self.value):extend(self.matchers)
newExpectation.successCondition = not self.successCondition
return newExpectation
end
if self._boundMatchers[key] then
return self._boundMatchers[key]
end
-- Fall back to methods provided by Expectation
return Expectation[key]
end
--[[
Called by expectation terminators to reset modifiers in a statement.
This makes chains like:
expect(5)
.never.to.equal(6)
.to.equal(5)
Work as expected.
]]
function Expectation:_resetModifiers()
self.successCondition = true
end
--[[
Assert that the expectation value is the given type.
expect(5).to.be.a("number")
]]
function Expectation:a(typeName)
local result = (type(self.value) == typeName) == self.successCondition
local message = formatMessage(self.successCondition,
("Expected value of type %q, got value %q of type %s"):format(
typeName,
tostring(self.value),
type(self.value)
),
("Expected value not of type %q, got value %q of type %s"):format(
typeName,
tostring(self.value),
type(self.value)
)
)
assertLevel(result, message, 3)
self:_resetModifiers()
return self
end
-- Make alias public on class
Expectation.an = Expectation.a
--[[
Assert that our expectation value is truthy
]]
function Expectation:ok()
local result = (self.value ~= nil) == self.successCondition
local message = formatMessage(self.successCondition,
("Expected value %q to be non-nil"):format(
tostring(self.value)
),
("Expected value %q to be nil"):format(
tostring(self.value)
)
)
assertLevel(result, message, 3)
self:_resetModifiers()
return self
end
--[[
Assert that our expectation value is equal to another value
]]
function Expectation:equal(otherValue)
local result = (self.value == otherValue) == self.successCondition
local message = formatMessage(self.successCondition,
("Expected value %q (%s), got %q (%s) instead"):format(
tostring(otherValue),
type(otherValue),
tostring(self.value),
type(self.value)
),
("Expected anything but value %q (%s)"):format(
tostring(otherValue),
type(otherValue)
)
)
assertLevel(result, message, 3)
self:_resetModifiers()
return self
end
--[[
Assert that our expectation value is equal to another value within some
inclusive limit.
]]
function Expectation:near(otherValue, limit)
assert(type(self.value) == "number", "Expectation value must be a number to use 'near'")
assert(type(otherValue) == "number", "otherValue must be a number")
assert(type(limit) == "number" or limit == nil, "limit must be a number or nil")
limit = limit or 1e-7
local result = (math.abs(self.value - otherValue) <= limit) == self.successCondition
local message = formatMessage(self.successCondition,
("Expected value to be near %f (within %f) but got %f instead"):format(
otherValue,
limit,
self.value
),
("Expected value to not be near %f (within %f) but got %f instead"):format(
otherValue,
limit,
self.value
)
)
assertLevel(result, message, 3)
self:_resetModifiers()
return self
end
--[[
Assert that our functoid expectation value throws an error when called.
An optional error message can be passed to assert that the error message
contains the given value.
]]
function Expectation:throw(messageSubstring)
local ok, err = pcall(self.value)
local result = ok ~= self.successCondition
if messageSubstring and not ok then
if self.successCondition then
result = err:find(messageSubstring, 1, true) ~= nil
else
result = err:find(messageSubstring, 1, true) == nil
end
end
local message
if messageSubstring then
message = formatMessage(self.successCondition,
("Expected function to throw an error containing %q, but it %s"):format(
messageSubstring,
err and ("threw: %s"):format(err) or "did not throw."
),
("Expected function to never throw an error containing %q, but it threw: %s"):format(
messageSubstring,
tostring(err)
)
)
else
message = formatMessage(self.successCondition,
"Expected function to throw an error, but it did not throw.",
("Expected function to succeed, but it threw an error: %s"):format(
tostring(err)
)
)
end
assertLevel(result, message, 3)
self:_resetModifiers()
return self
end
return Expectation

View file

@ -1,38 +0,0 @@
local Expectation = require(script.Parent.Expectation)
local checkMatcherNameCollisions = Expectation.checkMatcherNameCollisions
local function copy(t)
local result = {}
for key, value in pairs(t) do
result[key] = value
end
return result
end
local ExpectationContext = {}
ExpectationContext.__index = ExpectationContext
function ExpectationContext.new(parent)
local self = {
_extensions = parent and copy(parent._extensions) or {},
}
return setmetatable(self, ExpectationContext)
end
function ExpectationContext:startExpectationChain(...)
return Expectation.new(...):extend(self._extensions)
end
function ExpectationContext:extend(config)
for key, value in pairs(config) do
assert(self._extensions[key] == nil, string.format("Cannot reassign %q in expect.extend", key))
assert(checkMatcherNameCollisions(key), string.format("Cannot overwrite matcher %q; it already exists", key))
self._extensions[key] = value
end
end
return ExpectationContext

View file

@ -1,89 +0,0 @@
local TestEnum = require(script.Parent.TestEnum)
local LifecycleHooks = {}
LifecycleHooks.__index = LifecycleHooks
function LifecycleHooks.new()
local self = {
_stack = {},
}
return setmetatable(self, LifecycleHooks)
end
--[[
Returns an array of `beforeEach` hooks in FIFO order
]]
function LifecycleHooks:getBeforeEachHooks()
local key = TestEnum.NodeType.BeforeEach
local hooks = {}
for _, level in ipairs(self._stack) do
for _, hook in ipairs(level[key]) do
table.insert(hooks, hook)
end
end
return hooks
end
--[[
Returns an array of `afterEach` hooks in FILO order
]]
function LifecycleHooks:getAfterEachHooks()
local key = TestEnum.NodeType.AfterEach
local hooks = {}
for _, level in ipairs(self._stack) do
for _, hook in ipairs(level[key]) do
table.insert(hooks, 1, hook)
end
end
return hooks
end
--[[
Pushes uncalled beforeAll and afterAll hooks back up the stack
]]
function LifecycleHooks:popHooks()
table.remove(self._stack, #self._stack)
end
function LifecycleHooks:pushHooksFrom(planNode)
assert(planNode ~= nil)
table.insert(self._stack, {
[TestEnum.NodeType.BeforeAll] = self:_getHooksOfType(planNode.children, TestEnum.NodeType.BeforeAll),
[TestEnum.NodeType.AfterAll] = self:_getHooksOfType(planNode.children, TestEnum.NodeType.AfterAll),
[TestEnum.NodeType.BeforeEach] = self:_getHooksOfType(planNode.children, TestEnum.NodeType.BeforeEach),
[TestEnum.NodeType.AfterEach] = self:_getHooksOfType(planNode.children, TestEnum.NodeType.AfterEach),
})
end
--[[
Get the beforeAll hooks from the current level.
]]
function LifecycleHooks:getBeforeAllHooks()
return self._stack[#self._stack][TestEnum.NodeType.BeforeAll]
end
--[[
Get the afterAll hooks from the current level.
]]
function LifecycleHooks:getAfterAllHooks()
return self._stack[#self._stack][TestEnum.NodeType.AfterAll]
end
function LifecycleHooks:_getHooksOfType(nodes, key)
local hooks = {}
for _, node in ipairs(nodes) do
if node.type == key then
table.insert(hooks, node.callback)
end
end
return hooks
end
return LifecycleHooks

View file

@ -1,102 +0,0 @@
local TestService = game:GetService("TestService")
local TestEnum = require(script.Parent.Parent.TestEnum)
local TeamCityReporter = {}
local function teamCityEscape(str)
str = string.gsub(str, "([]|'[])","|%1")
str = string.gsub(str, "\r", "|r")
str = string.gsub(str, "\n", "|n")
return str
end
local function teamCityEnterSuite(suiteName)
return string.format("##teamcity[testSuiteStarted name='%s']", teamCityEscape(suiteName))
end
local function teamCityLeaveSuite(suiteName)
return string.format("##teamcity[testSuiteFinished name='%s']", teamCityEscape(suiteName))
end
local function teamCityEnterCase(caseName)
return string.format("##teamcity[testStarted name='%s']", teamCityEscape(caseName))
end
local function teamCityLeaveCase(caseName)
return string.format("##teamcity[testFinished name='%s']", teamCityEscape(caseName))
end
local function teamCityFailCase(caseName, errorMessage)
return string.format("##teamcity[testFailed name='%s' message='%s']",
teamCityEscape(caseName), teamCityEscape(errorMessage))
end
local function reportNode(node, buffer, level)
buffer = buffer or {}
level = level or 0
if node.status == TestEnum.TestStatus.Skipped then
return buffer
end
if node.planNode.type == TestEnum.NodeType.Describe then
table.insert(buffer, teamCityEnterSuite(node.planNode.phrase))
for _, child in ipairs(node.children) do
reportNode(child, buffer, level + 1)
end
table.insert(buffer, teamCityLeaveSuite(node.planNode.phrase))
else
table.insert(buffer, teamCityEnterCase(node.planNode.phrase))
if node.status == TestEnum.TestStatus.Failure then
table.insert(buffer, teamCityFailCase(node.planNode.phrase, table.concat(node.errors,"\n")))
end
table.insert(buffer, teamCityLeaveCase(node.planNode.phrase))
end
end
local function reportRoot(node)
local buffer = {}
for _, child in ipairs(node.children) do
reportNode(child, buffer, 0)
end
return buffer
end
local function report(root)
local buffer = reportRoot(root)
return table.concat(buffer, "\n")
end
function TeamCityReporter.report(results)
local resultBuffer = {
"Test results:",
report(results),
("%d passed, %d failed, %d skipped"):format(
results.successCount,
results.failureCount,
results.skippedCount
)
}
print(table.concat(resultBuffer, "\n"))
if results.failureCount > 0 then
print(("%d test nodes reported failures."):format(results.failureCount))
end
if #results.errors > 0 then
print("Errors reported by tests:")
print("")
for _, message in ipairs(results.errors) do
TestService:Error(message)
-- Insert a blank line after each error
print("")
end
end
end
return TeamCityReporter

View file

@ -1,106 +0,0 @@
--[[
The TextReporter uses the results from a completed test to output text to
standard output and TestService.
]]
local TestService = game:GetService("TestService")
local TestEnum = require(script.Parent.Parent.TestEnum)
local INDENT = (" "):rep(3)
local STATUS_SYMBOLS = {
[TestEnum.TestStatus.Success] = "+",
[TestEnum.TestStatus.Failure] = "-",
[TestEnum.TestStatus.Skipped] = "~"
}
local UNKNOWN_STATUS_SYMBOL = "?"
local TextReporter = {}
local function compareNodes(a, b)
return a.planNode.phrase:lower() < b.planNode.phrase:lower()
end
local function reportNode(node, buffer, level)
buffer = buffer or {}
level = level or 0
if node.status == TestEnum.TestStatus.Skipped then
return buffer
end
local line
if node.status then
local symbol = STATUS_SYMBOLS[node.status] or UNKNOWN_STATUS_SYMBOL
line = ("%s[%s] %s"):format(
INDENT:rep(level),
symbol,
node.planNode.phrase
)
else
line = ("%s%s"):format(
INDENT:rep(level),
node.planNode.phrase
)
end
table.insert(buffer, line)
table.sort(node.children, compareNodes)
for _, child in ipairs(node.children) do
reportNode(child, buffer, level + 1)
end
return buffer
end
local function reportRoot(node)
local buffer = {}
table.sort(node.children, compareNodes)
for _, child in ipairs(node.children) do
reportNode(child, buffer, 0)
end
return buffer
end
local function report(root)
local buffer = reportRoot(root)
return table.concat(buffer, "\n")
end
function TextReporter.report(results)
local resultBuffer = {
"Test results:",
report(results),
("%d passed, %d failed, %d skipped"):format(
results.successCount,
results.failureCount,
results.skippedCount
)
}
print(table.concat(resultBuffer, "\n"))
if results.failureCount > 0 then
print(("%d test nodes reported failures."):format(results.failureCount))
end
if #results.errors > 0 then
print("Errors reported by tests:")
print("")
for _, message in ipairs(results.errors) do
TestService:Error(message)
-- Insert a blank line after each error
print("")
end
end
end
return TextReporter

View file

@ -1,97 +0,0 @@
--[[
Copy of TextReporter that doesn't output successful tests.
This should be temporary, it's just a workaround to make CI environments
happy in the short-term.
]]
local TestService = game:GetService("TestService")
local TestEnum = require(script.Parent.Parent.TestEnum)
local INDENT = (" "):rep(3)
local STATUS_SYMBOLS = {
[TestEnum.TestStatus.Success] = "+",
[TestEnum.TestStatus.Failure] = "-",
[TestEnum.TestStatus.Skipped] = "~"
}
local UNKNOWN_STATUS_SYMBOL = "?"
local TextReporterQuiet = {}
local function reportNode(node, buffer, level)
buffer = buffer or {}
level = level or 0
if node.status == TestEnum.TestStatus.Skipped then
return buffer
end
local line
if node.status ~= TestEnum.TestStatus.Success then
local symbol = STATUS_SYMBOLS[node.status] or UNKNOWN_STATUS_SYMBOL
line = ("%s[%s] %s"):format(
INDENT:rep(level),
symbol,
node.planNode.phrase
)
end
table.insert(buffer, line)
for _, child in ipairs(node.children) do
reportNode(child, buffer, level + 1)
end
return buffer
end
local function reportRoot(node)
local buffer = {}
for _, child in ipairs(node.children) do
reportNode(child, buffer, 0)
end
return buffer
end
local function report(root)
local buffer = reportRoot(root)
return table.concat(buffer, "\n")
end
function TextReporterQuiet.report(results)
local resultBuffer = {
"Test results:",
report(results),
("%d passed, %d failed, %d skipped"):format(
results.successCount,
results.failureCount,
results.skippedCount
)
}
print(table.concat(resultBuffer, "\n"))
if results.failureCount > 0 then
print(("%d test nodes reported failures."):format(results.failureCount))
end
if #results.errors > 0 then
print("Errors reported by tests:")
print("")
for _, message in ipairs(results.errors) do
TestService:Error(message)
-- Insert a blank line after each error
print("")
end
end
end
return TextReporterQuiet

View file

@ -1,147 +0,0 @@
--[[
Provides an interface to quickly run and report tests from a given object.
]]
local TestPlanner = require(script.Parent.TestPlanner)
local TestRunner = require(script.Parent.TestRunner)
local TextReporter = require(script.Parent.Reporters.TextReporter)
local TestBootstrap = {}
local function stripSpecSuffix(name)
return (name:gsub("%.spec$", ""))
end
local function isSpecScript(aScript)
return aScript:IsA("ModuleScript") and aScript.Name:match("%.spec$")
end
local function getPath(module, root)
root = root or game
local path = {}
local last = module
if last.Name == "init.spec" then
-- Use the directory's node for init.spec files.
last = last.Parent
end
while last ~= nil and last ~= root do
table.insert(path, stripSpecSuffix(last.Name))
last = last.Parent
end
table.insert(path, stripSpecSuffix(root.Name))
return path
end
local function toStringPath(tablePath)
local stringPath = ""
local first = true
for _, element in ipairs(tablePath) do
if first then
stringPath = element
first = false
else
stringPath = element .. " " .. stringPath
end
end
return stringPath
end
function TestBootstrap:getModulesImpl(root, modules, current)
modules = modules or {}
current = current or root
if isSpecScript(current) then
local method = require(current)
local path = getPath(current, root)
local pathString = toStringPath(path)
table.insert(modules, {
method = method,
path = path,
pathStringForSorting = pathString:lower()
})
end
end
--[[
Find all the ModuleScripts in this tree that are tests.
]]
function TestBootstrap:getModules(root)
local modules = {}
self:getModulesImpl(root, modules)
for _, child in ipairs(root:GetDescendants()) do
self:getModulesImpl(root, modules, child)
end
return modules
end
--[[
Runs all test and reports the results using the given test reporter.
If no reporter is specified, a reasonable default is provided.
This function demonstrates the expected workflow with this testing system:
1. Locate test modules
2. Generate test plan
3. Run test plan
4. Report test results
This means we could hypothetically present a GUI to the developer that shows
the test plan before we execute it, allowing them to toggle specific tests
before they're run, but after they've been identified!
]]
function TestBootstrap:run(roots, reporter, otherOptions)
reporter = reporter or TextReporter
otherOptions = otherOptions or {}
local showTimingInfo = otherOptions["showTimingInfo"] or false
local testNamePattern = otherOptions["testNamePattern"]
local extraEnvironment = otherOptions["extraEnvironment"] or {}
if type(roots) ~= "table" then
error(("Bad argument #1 to TestBootstrap:run. Expected table, got %s"):format(typeof(roots)), 2)
end
local startTime = tick()
local modules = {}
for _, subRoot in ipairs(roots) do
local newModules = self:getModules(subRoot)
for _, newModule in ipairs(newModules) do
table.insert(modules, newModule)
end
end
local afterModules = tick()
local plan = TestPlanner.createPlan(modules, testNamePattern, extraEnvironment)
local afterPlan = tick()
local results = TestRunner.runPlan(plan)
local afterRun = tick()
reporter.report(results)
local afterReport = tick()
if showTimingInfo then
local timing = {
("Took %f seconds to locate test modules"):format(afterModules - startTime),
("Took %f seconds to create test plan"):format(afterPlan - afterModules),
("Took %f seconds to run tests"):format(afterRun - afterPlan),
("Took %f seconds to report tests"):format(afterReport - afterRun),
}
print(table.concat(timing, "\n"))
end
return results
end
return TestBootstrap

View file

@ -1,28 +0,0 @@
--[[
Constants used throughout the testing framework.
]]
local TestEnum = {}
TestEnum.TestStatus = {
Success = "Success",
Failure = "Failure",
Skipped = "Skipped"
}
TestEnum.NodeType = {
Describe = "Describe",
It = "It",
BeforeAll = "BeforeAll",
AfterAll = "AfterAll",
BeforeEach = "BeforeEach",
AfterEach = "AfterEach"
}
TestEnum.NodeModifier = {
None = "None",
Skip = "Skip",
Focus = "Focus"
}
return TestEnum

View file

@ -1,304 +0,0 @@
--[[
Represents a tree of tests that have been loaded but not necessarily
executed yet.
TestPlan objects are produced by TestPlanner.
]]
local TestEnum = require(script.Parent.TestEnum)
local Expectation = require(script.Parent.Expectation)
local function newEnvironment(currentNode, extraEnvironment)
local env = {}
if extraEnvironment then
if type(extraEnvironment) ~= "table" then
error(("Bad argument #2 to newEnvironment. Expected table, got %s"):format(
typeof(extraEnvironment)), 2)
end
for key, value in pairs(extraEnvironment) do
env[key] = value
end
end
local function addChild(phrase, callback, nodeType, nodeModifier)
local node = currentNode:addChild(phrase, nodeType, nodeModifier)
node.callback = callback
if nodeType == TestEnum.NodeType.Describe then
node:expand()
end
return node
end
function env.describeFOCUS(phrase, callback)
addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.Focus)
end
function env.describeSKIP(phrase, callback)
addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.Skip)
end
function env.describe(phrase, callback, nodeModifier)
addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.None)
end
function env.itFOCUS(phrase, callback)
addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Focus)
end
function env.itSKIP(phrase, callback)
addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Skip)
end
function env.itFIXME(phrase, callback)
local node = addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Skip)
warn("FIXME: broken test", node:getFullName())
end
function env.it(phrase, callback, nodeModifier)
addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.None)
end
-- Incrementing counter used to ensure that beforeAll, afterAll, beforeEach, afterEach have unique phrases
local lifecyclePhaseId = 0
local lifecycleHooks = {
[TestEnum.NodeType.BeforeAll] = "beforeAll",
[TestEnum.NodeType.AfterAll] = "afterAll",
[TestEnum.NodeType.BeforeEach] = "beforeEach",
[TestEnum.NodeType.AfterEach] = "afterEach"
}
for nodeType, name in pairs(lifecycleHooks) do
env[name] = function(callback)
addChild(name .. "_" .. tostring(lifecyclePhaseId), callback, nodeType, TestEnum.NodeModifier.None)
lifecyclePhaseId = lifecyclePhaseId + 1
end
end
function env.FIXME(optionalMessage)
warn("FIXME: broken test", currentNode:getFullName(), optionalMessage or "")
currentNode.modifier = TestEnum.NodeModifier.Skip
end
function env.FOCUS()
currentNode.modifier = TestEnum.NodeModifier.Focus
end
function env.SKIP()
currentNode.modifier = TestEnum.NodeModifier.Skip
end
--[[
This function is deprecated. Calling it is a no-op beyond generating a
warning.
]]
function env.HACK_NO_XPCALL()
warn("HACK_NO_XPCALL is deprecated. It is now safe to yield in an " ..
"xpcall, so this is no longer necessary. It can be safely deleted.")
end
env.fit = env.itFOCUS
env.xit = env.itSKIP
env.fdescribe = env.describeFOCUS
env.xdescribe = env.describeSKIP
env.expect = setmetatable({
extend = function(...)
error("Cannot call \"expect.extend\" from within a \"describe\" node.")
end,
}, {
__call = function(_self, ...)
return Expectation.new(...)
end,
})
return env
end
local TestNode = {}
TestNode.__index = TestNode
--[[
Create a new test node. A pointer to the test plan, a phrase to describe it
and the type of node it is are required. The modifier is optional and will
be None if left blank.
]]
function TestNode.new(plan, phrase, nodeType, nodeModifier)
nodeModifier = nodeModifier or TestEnum.NodeModifier.None
local node = {
plan = plan,
phrase = phrase,
type = nodeType,
modifier = nodeModifier,
children = {},
callback = nil,
parent = nil,
}
node.environment = newEnvironment(node, plan.extraEnvironment)
return setmetatable(node, TestNode)
end
local function getModifier(name, pattern, modifier)
if pattern and (modifier == nil or modifier == TestEnum.NodeModifier.None) then
if name:match(pattern) then
return TestEnum.NodeModifier.Focus
else
return TestEnum.NodeModifier.Skip
end
end
return modifier
end
function TestNode:addChild(phrase, nodeType, nodeModifier)
if nodeType == TestEnum.NodeType.It then
for _, child in pairs(self.children) do
if child.phrase == phrase then
error("Duplicate it block found: " .. child:getFullName())
end
end
end
local childName = self:getFullName() .. " " .. phrase
nodeModifier = getModifier(childName, self.plan.testNamePattern, nodeModifier)
local child = TestNode.new(self.plan, phrase, nodeType, nodeModifier)
child.parent = self
table.insert(self.children, child)
return child
end
--[[
Join the names of all the nodes back to the parent.
]]
function TestNode:getFullName()
if self.parent then
local parentPhrase = self.parent:getFullName()
if parentPhrase then
return parentPhrase .. " " .. self.phrase
end
end
return self.phrase
end
--[[
Expand a node by setting its callback environment and then calling it. Any
further it and describe calls within the callback will be added to the tree.
]]
function TestNode:expand()
local originalEnv = getfenv(self.callback)
local callbackEnv = setmetatable({}, { __index = originalEnv })
for key, value in pairs(self.environment) do
callbackEnv[key] = value
end
-- Copy 'script' directly to new env to make Studio debugger happy.
-- Studio debugger does not look into __index, because of security reasons
callbackEnv.script = originalEnv.script
setfenv(self.callback, callbackEnv)
local success, result = xpcall(self.callback, function(message)
return debug.traceback(tostring(message), 2)
end)
if not success then
self.loadError = result
end
end
local TestPlan = {}
TestPlan.__index = TestPlan
--[[
Create a new, empty TestPlan.
]]
function TestPlan.new(testNamePattern, extraEnvironment)
local plan = {
children = {},
testNamePattern = testNamePattern,
extraEnvironment = extraEnvironment,
}
return setmetatable(plan, TestPlan)
end
--[[
Add a new child under the test plan's root node.
]]
function TestPlan:addChild(phrase, nodeType, nodeModifier)
nodeModifier = getModifier(phrase, self.testNamePattern, nodeModifier)
local child = TestNode.new(self, phrase, nodeType, nodeModifier)
table.insert(self.children, child)
return child
end
--[[
Add a new describe node with the given method as a callback. Generates or
reuses all the describe nodes along the path.
]]
function TestPlan:addRoot(path, method)
local curNode = self
for i = #path, 1, -1 do
local nextNode = nil
for _, child in ipairs(curNode.children) do
if child.phrase == path[i] then
nextNode = child
break
end
end
if nextNode == nil then
nextNode = curNode:addChild(path[i], TestEnum.NodeType.Describe)
end
curNode = nextNode
end
curNode.callback = method
curNode:expand()
end
--[[
Calls the given callback on all nodes in the tree, traversed depth-first.
]]
function TestPlan:visitAllNodes(callback, root, level)
root = root or self
level = level or 0
for _, child in ipairs(root.children) do
callback(child, level)
self:visitAllNodes(callback, child, level + 1)
end
end
--[[
Visualizes the test plan in a simple format, suitable for debugging the test
plan's structure.
]]
function TestPlan:visualize()
local buffer = {}
self:visitAllNodes(function(node, level)
table.insert(buffer, (" "):rep(3 * level) .. node.phrase)
end)
return table.concat(buffer, "\n")
end
--[[
Gets a list of all nodes in the tree for which the given callback returns
true.
]]
function TestPlan:findNodes(callback)
local results = {}
self:visitAllNodes(function(node)
if callback(node) then
table.insert(results, node)
end
end)
return results
end
return TestPlan

View file

@ -1,40 +0,0 @@
--[[
Turns a series of specification functions into a test plan.
Uses a TestPlanBuilder to keep track of the state of the tree being built.
]]
local TestPlan = require(script.Parent.TestPlan)
local TestPlanner = {}
--[[
Create a new TestPlan from a list of specification functions.
These functions should call a combination of `describe` and `it` (and their
variants), which will be turned into a test plan to be executed.
Parameters:
- modulesList - list of tables describing test modules {
method, -- specification function described above
path, -- array of parent entires, first element is the leaf that owns `method`
pathStringForSorting -- a string representation of `path`, used for sorting of the test plan
}
- testNamePattern - Only tests matching this Lua pattern string will run. Pass empty or nil to run all tests
- extraEnvironment - Lua table holding additional functions and variables to be injected into the specification
function during execution
]]
function TestPlanner.createPlan(modulesList, testNamePattern, extraEnvironment)
local plan = TestPlan.new(testNamePattern, extraEnvironment)
table.sort(modulesList, function(a, b)
return a.pathStringForSorting < b.pathStringForSorting
end)
for _, module in ipairs(modulesList) do
plan:addRoot(module.path, module.method)
end
return plan
end
return TestPlanner

View file

@ -1,112 +0,0 @@
--[[
Represents a tree of test results.
Each node in the tree corresponds directly to a node in a corresponding
TestPlan, accessible via the 'planNode' field.
TestResults objects are produced by TestRunner using TestSession as state.
]]
local TestEnum = require(script.Parent.TestEnum)
local STATUS_SYMBOLS = {
[TestEnum.TestStatus.Success] = "+",
[TestEnum.TestStatus.Failure] = "-",
[TestEnum.TestStatus.Skipped] = "~"
}
local TestResults = {}
TestResults.__index = TestResults
--[[
Create a new TestResults tree that's linked to the given TestPlan.
]]
function TestResults.new(plan)
local self = {
successCount = 0,
failureCount = 0,
skippedCount = 0,
planNode = plan,
children = {},
errors = {}
}
setmetatable(self, TestResults)
return self
end
--[[
Create a new result node that can be inserted into a TestResult tree.
]]
function TestResults.createNode(planNode)
local node = {
planNode = planNode,
children = {},
errors = {},
status = nil
}
return node
end
--[[
Visit all test result nodes, depth-first.
]]
function TestResults:visitAllNodes(callback, root)
root = root or self
for _, child in ipairs(root.children) do
callback(child)
self:visitAllNodes(callback, child)
end
end
--[[
Creates a debug visualization of the test results.
]]
function TestResults:visualize(root, level)
root = root or self
level = level or 0
local buffer = {}
for _, child in ipairs(root.children) do
if child.planNode.type == TestEnum.NodeType.It then
local symbol = STATUS_SYMBOLS[child.status] or "?"
local str = ("%s[%s] %s"):format(
(" "):rep(3 * level),
symbol,
child.planNode.phrase
)
if child.messages and #child.messages > 0 then
str = str .. "\n " .. (" "):rep(3 * level) .. table.concat(child.messages, "\n " .. (" "):rep(3 * level))
end
table.insert(buffer, str)
else
local str = ("%s%s"):format(
(" "):rep(3 * level),
child.planNode.phrase or ""
)
if child.status then
str = str .. (" (%s)"):format(child.status)
end
table.insert(buffer, str)
if #child.children > 0 then
local text = self:visualize(child, level + 1)
table.insert(buffer, text)
end
end
end
return table.concat(buffer, "\n")
end
return TestResults

View file

@ -1,188 +0,0 @@
--[[
Contains the logic to run a test plan and gather test results from it.
TestRunner accepts a TestPlan object, executes the planned tests, and
produces a TestResults object. While the tests are running, the system's
state is contained inside a TestSession object.
]]
local TestEnum = require(script.Parent.TestEnum)
local TestSession = require(script.Parent.TestSession)
local LifecycleHooks = require(script.Parent.LifecycleHooks)
local RUNNING_GLOBAL = "__TESTEZ_RUNNING_TEST__"
local TestRunner = {
environment = {}
}
local function wrapExpectContextWithPublicApi(expectationContext)
return setmetatable({
extend = function(...)
expectationContext:extend(...)
end,
}, {
__call = function(_self, ...)
return expectationContext:startExpectationChain(...)
end,
})
end
--[[
Runs the given TestPlan and returns a TestResults object representing the
results of the run.
]]
function TestRunner.runPlan(plan)
local session = TestSession.new(plan)
local lifecycleHooks = LifecycleHooks.new()
local exclusiveNodes = plan:findNodes(function(node)
return node.modifier == TestEnum.NodeModifier.Focus
end)
session.hasFocusNodes = #exclusiveNodes > 0
TestRunner.runPlanNode(session, plan, lifecycleHooks)
return session:finalize()
end
--[[
Run the given test plan node and its descendants, using the given test
session to store all of the results.
]]
function TestRunner.runPlanNode(session, planNode, lifecycleHooks)
local function runCallback(callback, messagePrefix)
local success = true
local errorMessage
-- Any code can check RUNNING_GLOBAL to fork behavior based on
-- whether a test is running. We use this to avoid accessing
-- protected APIs; it's a workaround that will go away someday.
_G[RUNNING_GLOBAL] = true
messagePrefix = messagePrefix or ""
local testEnvironment = getfenv(callback)
for key, value in pairs(TestRunner.environment) do
testEnvironment[key] = value
end
testEnvironment.fail = function(message)
if message == nil then
message = "fail() was called."
end
success = false
errorMessage = messagePrefix .. debug.traceback(tostring(message), 2)
end
testEnvironment.expect = wrapExpectContextWithPublicApi(session:getExpectationContext())
local context = session:getContext()
local nodeSuccess, nodeResult = xpcall(
function()
callback(context)
end,
function(message)
return messagePrefix .. debug.traceback(tostring(message), 2)
end
)
-- If a node threw an error, we prefer to use that message over
-- one created by fail() if it was set.
if not nodeSuccess then
success = false
errorMessage = nodeResult
end
_G[RUNNING_GLOBAL] = nil
return success, errorMessage
end
local function runNode(childPlanNode)
-- Errors can be set either via `error` propagating upwards or
-- by a test calling fail([message]).
for _, hook in ipairs(lifecycleHooks:getBeforeEachHooks()) do
local success, errorMessage = runCallback(hook, "beforeEach hook: ")
if not success then
return false, errorMessage
end
end
local testSuccess, testErrorMessage = runCallback(childPlanNode.callback)
for _, hook in ipairs(lifecycleHooks:getAfterEachHooks()) do
local success, errorMessage = runCallback(hook, "afterEach hook: ")
if not success then
if not testSuccess then
return false, testErrorMessage .. "\nWhile cleaning up the failed test another error was found:\n" .. errorMessage
end
return false, errorMessage
end
end
if not testSuccess then
return false, testErrorMessage
end
return true, nil
end
lifecycleHooks:pushHooksFrom(planNode)
local halt = false
for _, hook in ipairs(lifecycleHooks:getBeforeAllHooks()) do
local success, errorMessage = runCallback(hook, "beforeAll hook: ")
if not success then
session:addDummyError("beforeAll", errorMessage)
halt = true
end
end
if not halt then
for _, childPlanNode in ipairs(planNode.children) do
if childPlanNode.type == TestEnum.NodeType.It then
session:pushNode(childPlanNode)
if session:shouldSkip() then
session:setSkipped()
else
local success, errorMessage = runNode(childPlanNode)
if success then
session:setSuccess()
else
session:setError(errorMessage)
end
end
session:popNode()
elseif childPlanNode.type == TestEnum.NodeType.Describe then
session:pushNode(childPlanNode)
TestRunner.runPlanNode(session, childPlanNode, lifecycleHooks)
-- Did we have an error trying build a test plan?
if childPlanNode.loadError then
local message = "Error during planning: " .. childPlanNode.loadError
session:setError(message)
else
session:setStatusFromChildren()
end
session:popNode()
end
end
end
for _, hook in ipairs(lifecycleHooks:getAfterAllHooks()) do
local success, errorMessage = runCallback(hook, "afterAll hook: ")
if not success then
session:addDummyError("afterAll", errorMessage)
end
end
lifecycleHooks:popHooks()
end
return TestRunner

View file

@ -1,243 +0,0 @@
--[[
Represents the state relevant while executing a test plan.
Used by TestRunner to produce a TestResults object.
Uses the same tree building structure as TestPlanBuilder; TestSession keeps
track of a stack of nodes that represent the current path through the tree.
]]
local TestEnum = require(script.Parent.TestEnum)
local TestResults = require(script.Parent.TestResults)
local Context = require(script.Parent.Context)
local ExpectationContext = require(script.Parent.ExpectationContext)
local TestSession = {}
TestSession.__index = TestSession
--[[
Create a TestSession related to the given TestPlan.
The resulting TestResults object will be linked to this TestPlan.
]]
function TestSession.new(plan)
local self = {
results = TestResults.new(plan),
nodeStack = {},
contextStack = {},
expectationContextStack = {},
hasFocusNodes = false
}
setmetatable(self, TestSession)
return self
end
--[[
Calculate success, failure, and skipped test counts in the tree at the
current point in the execution.
]]
function TestSession:calculateTotals()
local results = self.results
results.successCount = 0
results.failureCount = 0
results.skippedCount = 0
results:visitAllNodes(function(node)
local status = node.status
local nodeType = node.planNode.type
if nodeType == TestEnum.NodeType.It then
if status == TestEnum.TestStatus.Success then
results.successCount = results.successCount + 1
elseif status == TestEnum.TestStatus.Failure then
results.failureCount = results.failureCount + 1
elseif status == TestEnum.TestStatus.Skipped then
results.skippedCount = results.skippedCount + 1
end
end
end)
end
--[[
Gathers all of the errors reported by tests and puts them at the top level
of the TestResults object.
]]
function TestSession:gatherErrors()
local results = self.results
results.errors = {}
results:visitAllNodes(function(node)
if #node.errors > 0 then
for _, message in ipairs(node.errors) do
table.insert(results.errors, message)
end
end
end)
end
--[[
Calculates test totals, verifies the tree is valid, and returns results.
]]
function TestSession:finalize()
if #self.nodeStack ~= 0 then
error("Cannot finalize TestResults with nodes still on the stack!", 2)
end
self:calculateTotals()
self:gatherErrors()
return self.results
end
--[[
Create a new test result node and push it onto the navigation stack.
]]
function TestSession:pushNode(planNode)
local node = TestResults.createNode(planNode)
local lastNode = self.nodeStack[#self.nodeStack] or self.results
table.insert(lastNode.children, node)
table.insert(self.nodeStack, node)
local lastContext = self.contextStack[#self.contextStack]
local context = Context.new(lastContext)
table.insert(self.contextStack, context)
local lastExpectationContext = self.expectationContextStack[#self.expectationContextStack]
local expectationContext = ExpectationContext.new(lastExpectationContext)
table.insert(self.expectationContextStack, expectationContext)
end
--[[
Pops a node off of the navigation stack.
]]
function TestSession:popNode()
assert(#self.nodeStack > 0, "Tried to pop from an empty node stack!")
table.remove(self.nodeStack, #self.nodeStack)
table.remove(self.contextStack, #self.contextStack)
table.remove(self.expectationContextStack, #self.expectationContextStack)
end
--[[
Gets the Context object for the current node.
]]
function TestSession:getContext()
assert(#self.contextStack > 0, "Tried to get context from an empty stack!")
return self.contextStack[#self.contextStack]
end
function TestSession:getExpectationContext()
assert(#self.expectationContextStack > 0, "Tried to get expectationContext from an empty stack!")
return self.expectationContextStack[#self.expectationContextStack]
end
--[[
Tells whether the current test we're in should be skipped.
]]
function TestSession:shouldSkip()
-- If our test tree had any exclusive tests, then normal tests are skipped!
if self.hasFocusNodes then
for i = #self.nodeStack, 1, -1 do
local node = self.nodeStack[i]
-- Skipped tests are still skipped
if node.planNode.modifier == TestEnum.NodeModifier.Skip then
return true
end
-- Focused tests are the only ones that aren't skipped
if node.planNode.modifier == TestEnum.NodeModifier.Focus then
return false
end
end
return true
else
for i = #self.nodeStack, 1, -1 do
local node = self.nodeStack[i]
if node.planNode.modifier == TestEnum.NodeModifier.Skip then
return true
end
end
end
return false
end
--[[
Set the current node's status to Success.
]]
function TestSession:setSuccess()
assert(#self.nodeStack > 0, "Attempting to set success status on empty stack")
self.nodeStack[#self.nodeStack].status = TestEnum.TestStatus.Success
end
--[[
Set the current node's status to Skipped.
]]
function TestSession:setSkipped()
assert(#self.nodeStack > 0, "Attempting to set skipped status on empty stack")
self.nodeStack[#self.nodeStack].status = TestEnum.TestStatus.Skipped
end
--[[
Set the current node's status to Failure and adds a message to its list of
errors.
]]
function TestSession:setError(message)
assert(#self.nodeStack > 0, "Attempting to set error status on empty stack")
local last = self.nodeStack[#self.nodeStack]
last.status = TestEnum.TestStatus.Failure
table.insert(last.errors, message)
end
--[[
Add a dummy child node to the current node to hold the given error. This
allows an otherwise empty describe node to report an error in a more natural
way.
]]
function TestSession:addDummyError(phrase, message)
self:pushNode({type = TestEnum.NodeType.It, phrase = phrase})
self:setError(message)
self:popNode()
self.nodeStack[#self.nodeStack].status = TestEnum.TestStatus.Failure
end
--[[
Set the current node's status based on that of its children. If all children
are skipped, mark it as skipped. If any are fails, mark it as failed.
Otherwise, mark it as success.
]]
function TestSession:setStatusFromChildren()
assert(#self.nodeStack > 0, "Attempting to set status from children on empty stack")
local last = self.nodeStack[#self.nodeStack]
local status = TestEnum.TestStatus.Success
local skipped = true
-- If all children were skipped, then we were skipped
-- If any child failed, then we failed!
for _, child in ipairs(last.children) do
if child.status ~= TestEnum.TestStatus.Skipped then
skipped = false
if child.status == TestEnum.TestStatus.Failure then
status = TestEnum.TestStatus.Failure
end
end
end
if skipped then
status = TestEnum.TestStatus.Skipped
end
last.status = status
end
return TestSession

View file

@ -1,42 +0,0 @@
--!native
--!optimize 2
local Expectation = require(script.Expectation)
local TestBootstrap = require(script.TestBootstrap)
local TestEnum = require(script.TestEnum)
local TestPlan = require(script.TestPlan)
local TestPlanner = require(script.TestPlanner)
local TestResults = require(script.TestResults)
local TestRunner = require(script.TestRunner)
local TestSession = require(script.TestSession)
local TextReporter = require(script.Reporters.TextReporter)
local TextReporterQuiet = require(script.Reporters.TextReporterQuiet)
local TeamCityReporter = require(script.Reporters.TeamCityReporter)
local function run(testRoot, callback)
local modules = TestBootstrap:getModules(testRoot)
local plan = TestPlanner.createPlan(modules)
local results = TestRunner.runPlan(plan)
callback(results)
end
local TestEZ = {
run = run,
Expectation = Expectation,
TestBootstrap = TestBootstrap,
TestEnum = TestEnum,
TestPlan = TestPlan,
TestPlanner = TestPlanner,
TestResults = TestResults,
TestRunner = TestRunner,
TestSession = TestSession,
Reporters = {
TextReporter = TextReporter,
TextReporterQuiet = TextReporterQuiet,
TeamCityReporter = TeamCityReporter,
},
}
return TestEZ

BIN
Warp.rbxm

Binary file not shown.

View file

@ -5,7 +5,7 @@ For efficient data serialization and schema definition with optimized packing.
## Getting the Buffer Object
```lua
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
```
## Schema System <Badge type="tip" text="v1.1" />
@ -19,11 +19,10 @@ Define strict data schemas for optimized serialization and type safety.
-- Basic types
"boolean",
"string",
"nil",
-- Numeric types
"u8", -- usigned-int
"u16",
"u8", -- unsigned-int
"u16",
"u32",
"i8", -- signed-int
"i16",
@ -33,12 +32,26 @@ Define strict data schemas for optimized serialization and type safety.
"f64",
-- Roblox types
"buffer"
"vector2", -- f16
"vector3", -- f16
"cframe", -- f32 & f16
"color3", -- u8
"buffer",
"vector2", -- f16 x/y
"vector3", -- f16 x/y/z
"vector2int16", -- i16 x/y
"vector3int16", -- i16 x/y/z
"cframe", -- f32 position + compressed rotation (f16)
"color3", -- u8 r/g/b
"color3f16",
"udim",
"udim2",
"rect",
"ray",
"numberrange",
"colorsequence",
"numbersequence",
"brickcolor",
"tweeninfo",
"physicalproperties",
"font",
"datetime",
"instance",
-- other types
@ -49,6 +62,10 @@ Define strict data schemas for optimized serialization and type safety.
}
```
::: info
there is no standalone `"nil"` schema type. To represent a value that can be `nil`, wrap it with `"optional"` (e.g. `Buffer.Schema.optional(Buffer.Schema.u16)`).
:::
## Custom Datatypes
### `.custom_datatype`
@ -64,7 +81,7 @@ Define strict data schemas for optimized serialization and type safety.
```
```luau [Example]
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
-- # this custom datatype must be registered on both server & client side
Buffer.Schema.custom_datatype("u64", {}, function(w: Buffer.Writer, value: any) -- just for reference
@ -94,7 +111,7 @@ Create a new buffer writer for serializing data.
```
```luau [Example]
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
local writer = Buffer.createWriter(256) -- Pre-allocate 256 bytes
```
:::
@ -111,7 +128,7 @@ Build the final buffer for transmission.
```
```luau [Example]
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
local writer = Buffer.createWriter()
-- Write some data
@ -136,7 +153,7 @@ Build the final buffer with instance references for transmission.
```
```luau [Example]
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
local writer = Buffer.createWriter()
-- Write some data with instances
@ -161,7 +178,7 @@ Reset a writer for reuse, clearing all data.
```
```luau [Example]
local Buffer = Warp.Buffer()
local Buffer = Warp.Buffer
local writer = Buffer.createWriter()
-- Use writer for first batch

View file

@ -20,7 +20,7 @@ this is optional and conditional, you may have to use this if you had a problem
```luau [Variable]
(
namespaces: { string },
) -> Connection
)
```
```luau [Example]

View file

@ -40,6 +40,8 @@ local Players = game:GetService("Players")
local Warp = require(path.to.warp).Client()
local Schemas = require(path.to.schemas)
Warp.awaitReady() -- this is optional, but recommended if facing any issues with race-condition, or remotes not registered
-- Use schemas
for eventName, schema in Schemas do
Warp.useSchema(eventName, schema)

View file

@ -1,17 +0,0 @@
# @algolia/autocomplete-core
The [`autocomplete-core`](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-core/createAutocomplete) package is the foundation of Autocomplete. It exposes primitives to build an autocomplete experience.
You likely dont need to use this package directly unless youre building a [renderer](https://www.algolia.com/doc/ui-libraries/autocomplete/guides/creating-a-renderer).
## Installation
```sh
yarn add @algolia/autocomplete-core
# or
npm install @algolia/autocomplete-core
```
## Documentation
See [**Documentation**](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-core).

View file

@ -1,2 +0,0 @@
import { AutocompleteOptions, BaseItem } from './types';
export declare function checkOptions<TItem extends BaseItem>(options: AutocompleteOptions<TItem>): void;

View file

@ -1,4 +0,0 @@
import { warn } from '@algolia/autocomplete-shared';
export function checkOptions(options) {
process.env.NODE_ENV !== 'production' ? warn(!options.debug, 'The `debug` option is meant for development debugging and should not be used in production.') : void 0;
}

View file

@ -1,8 +0,0 @@
import { AutocompleteApi, AutocompleteOptions as AutocompleteCoreOptions, BaseItem } from './types';
export interface AutocompleteOptionsWithMetadata<TItem extends BaseItem> extends AutocompleteCoreOptions<TItem> {
/**
* @internal
*/
__autocomplete_metadata?: Record<string, unknown>;
}
export declare function createAutocomplete<TItem extends BaseItem, TEvent = Event, TMouseEvent = MouseEvent, TKeyboardEvent = KeyboardEvent>(options: AutocompleteOptionsWithMetadata<TItem>): AutocompleteApi<TItem, TEvent, TMouseEvent, TKeyboardEvent>;

View file

@ -1,106 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { createAlgoliaInsightsPlugin } from '@algolia/autocomplete-plugin-algolia-insights';
import { checkOptions } from './checkOptions';
import { createStore } from './createStore';
import { getAutocompleteSetters } from './getAutocompleteSetters';
import { getDefaultProps } from './getDefaultProps';
import { getPropGetters } from './getPropGetters';
import { getMetadata, injectMetadata } from './metadata';
import { onInput } from './onInput';
import { stateReducer } from './stateReducer';
export function createAutocomplete(options) {
checkOptions(options);
var subscribers = [];
var props = getDefaultProps(options, subscribers);
var store = createStore(stateReducer, props, onStoreStateChange);
var setters = getAutocompleteSetters({
store: store
});
var propGetters = getPropGetters(_objectSpread({
props: props,
refresh: refresh,
store: store,
navigator: props.navigator
}, setters));
function onStoreStateChange(_ref) {
var _state$context, _state$context$algoli;
var prevState = _ref.prevState,
state = _ref.state;
props.onStateChange(_objectSpread({
prevState: prevState,
state: state,
refresh: refresh,
navigator: props.navigator
}, setters));
if (!isAlgoliaInsightsPluginEnabled() && (_state$context = state.context) !== null && _state$context !== void 0 && (_state$context$algoli = _state$context.algoliaInsightsPlugin) !== null && _state$context$algoli !== void 0 && _state$context$algoli.__automaticInsights && props.insights !== false) {
var plugin = createAlgoliaInsightsPlugin({
__autocomplete_clickAnalytics: false
});
props.plugins.push(plugin);
subscribePlugins([plugin]);
}
}
function refresh() {
return onInput(_objectSpread({
event: new Event('input'),
nextState: {
isOpen: store.getState().isOpen
},
props: props,
navigator: props.navigator,
query: store.getState().query,
refresh: refresh,
store: store
}, setters));
}
function subscribePlugins(plugins) {
plugins.forEach(function (plugin) {
var _plugin$subscribe;
return (_plugin$subscribe = plugin.subscribe) === null || _plugin$subscribe === void 0 ? void 0 : _plugin$subscribe.call(plugin, _objectSpread(_objectSpread({}, setters), {}, {
navigator: props.navigator,
refresh: refresh,
onSelect: function onSelect(fn) {
subscribers.push({
onSelect: fn
});
},
onActive: function onActive(fn) {
subscribers.push({
onActive: fn
});
},
onResolve: function onResolve(fn) {
subscribers.push({
onResolve: fn
});
}
}));
});
}
function isAlgoliaInsightsPluginEnabled() {
return props.plugins.some(function (plugin) {
return plugin.name === 'aa.algoliaInsightsPlugin';
});
}
if (props.insights && !isAlgoliaInsightsPluginEnabled()) {
var insightsParams = typeof props.insights === 'boolean' ? {} : props.insights;
props.plugins.push(createAlgoliaInsightsPlugin(insightsParams));
}
subscribePlugins(props.plugins);
injectMetadata({
metadata: getMetadata({
plugins: props.plugins,
options: options
}),
environment: props.environment
});
return _objectSpread(_objectSpread({
refresh: refresh,
navigator: props.navigator
}, propGetters), setters);
}

View file

@ -1,7 +0,0 @@
import { AutocompleteState, AutocompleteStore, BaseItem, InternalAutocompleteOptions, Reducer } from './types';
declare type OnStoreStateChange<TItem extends BaseItem> = ({ prevState, state, }: {
prevState: AutocompleteState<TItem>;
state: AutocompleteState<TItem>;
}) => void;
export declare function createStore<TItem extends BaseItem>(reducer: Reducer, props: InternalAutocompleteOptions<TItem>, onStoreStateChange: OnStoreStateChange<TItem>): AutocompleteStore<TItem>;
export {};

View file

@ -1,28 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { createCancelablePromiseList } from './utils';
export function createStore(reducer, props, onStoreStateChange) {
var state = props.initialState;
return {
getState: function getState() {
return state;
},
dispatch: function dispatch(action, payload) {
var prevState = _objectSpread({}, state);
state = reducer(state, {
type: action,
props: props,
payload: payload
});
onStoreStateChange({
state: state,
prevState: prevState
});
},
pendingRequests: createCancelablePromiseList()
};
}

View file

@ -1,13 +0,0 @@
import { AutocompleteCollection, AutocompleteStore, BaseItem } from './types';
interface GetAutocompleteSettersOptions<TItem extends BaseItem> {
store: AutocompleteStore<TItem>;
}
export declare function getAutocompleteSetters<TItem extends BaseItem>({ store, }: GetAutocompleteSettersOptions<TItem>): {
setActiveItemId: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<number | null>;
setQuery: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<string>;
setCollections: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<(AutocompleteCollection<TItem> | import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteCollection").AutocompleteCollectionItemsArray<TItem>)[]>;
setIsOpen: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<boolean>;
setStatus: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<"idle" | "loading" | "stalled" | "error">;
setContext: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSetters").StateUpdater<import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteContext").AutocompleteContext>;
};
export {};

View file

@ -1,48 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { flatten } from '@algolia/autocomplete-shared';
export function getAutocompleteSetters(_ref) {
var store = _ref.store;
var setActiveItemId = function setActiveItemId(value) {
store.dispatch('setActiveItemId', value);
};
var setQuery = function setQuery(value) {
store.dispatch('setQuery', value);
};
var setCollections = function setCollections(rawValue) {
var baseItemId = 0;
var value = rawValue.map(function (collection) {
return _objectSpread(_objectSpread({}, collection), {}, {
// We flatten the stored items to support calling `getAlgoliaResults`
// from the source itself.
items: flatten(collection.items).map(function (item) {
return _objectSpread(_objectSpread({}, item), {}, {
__autocomplete_id: baseItemId++
});
})
});
});
store.dispatch('setCollections', value);
};
var setIsOpen = function setIsOpen(value) {
store.dispatch('setIsOpen', value);
};
var setStatus = function setStatus(value) {
store.dispatch('setStatus', value);
};
var setContext = function setContext(value) {
store.dispatch('setContext', value);
};
return {
setActiveItemId: setActiveItemId,
setQuery: setQuery,
setCollections: setCollections,
setIsOpen: setIsOpen,
setStatus: setStatus,
setContext: setContext
};
}

View file

@ -1,6 +0,0 @@
import { AutocompleteState, BaseItem } from './types';
interface GetCompletionProps<TItem extends BaseItem> {
state: AutocompleteState<TItem>;
}
export declare function getCompletion<TItem extends BaseItem>({ state, }: GetCompletionProps<TItem>): string | null;
export {};

View file

@ -1,9 +0,0 @@
import { getActiveItem } from './utils';
export function getCompletion(_ref) {
var _getActiveItem;
var state = _ref.state;
if (state.isOpen === false || state.activeItemId === null) {
return null;
}
return ((_getActiveItem = getActiveItem(state)) === null || _getActiveItem === void 0 ? void 0 : _getActiveItem.itemInputValue) || null;
}

View file

@ -1,2 +0,0 @@
import { AutocompleteOptions, AutocompleteSubscribers, BaseItem, InternalAutocompleteOptions } from './types';
export declare function getDefaultProps<TItem extends BaseItem>(props: AutocompleteOptions<TItem>, pluginSubscribers: AutocompleteSubscribers<TItem>): InternalAutocompleteOptions<TItem>;

View file

@ -1,130 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { getItemsCount, generateAutocompleteId, flatten } from '@algolia/autocomplete-shared';
import { getNormalizedSources } from './utils';
export function getDefaultProps(props, pluginSubscribers) {
var _props$id;
/* eslint-disable no-restricted-globals */
var environment = typeof window !== 'undefined' ? window : {};
/* eslint-enable no-restricted-globals */
var plugins = props.plugins || [];
return _objectSpread(_objectSpread({
debug: false,
openOnFocus: false,
enterKeyHint: undefined,
ignoreCompositionEvents: false,
placeholder: '',
autoFocus: false,
defaultActiveItemId: null,
stallThreshold: 300,
insights: undefined,
environment: environment,
shouldPanelOpen: function shouldPanelOpen(_ref) {
var state = _ref.state;
return getItemsCount(state) > 0;
},
reshape: function reshape(_ref2) {
var sources = _ref2.sources;
return sources;
}
}, props), {}, {
// Since `generateAutocompleteId` triggers a side effect (it increments
// an internal counter), we don't want to execute it if unnecessary.
id: (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : generateAutocompleteId(),
plugins: plugins,
// The following props need to be deeply defaulted.
initialState: _objectSpread({
activeItemId: null,
query: '',
completion: null,
collections: [],
isOpen: false,
status: 'idle',
context: {}
}, props.initialState),
onStateChange: function onStateChange(params) {
var _props$onStateChange;
(_props$onStateChange = props.onStateChange) === null || _props$onStateChange === void 0 ? void 0 : _props$onStateChange.call(props, params);
plugins.forEach(function (x) {
var _x$onStateChange;
return (_x$onStateChange = x.onStateChange) === null || _x$onStateChange === void 0 ? void 0 : _x$onStateChange.call(x, params);
});
},
onSubmit: function onSubmit(params) {
var _props$onSubmit;
(_props$onSubmit = props.onSubmit) === null || _props$onSubmit === void 0 ? void 0 : _props$onSubmit.call(props, params);
plugins.forEach(function (x) {
var _x$onSubmit;
return (_x$onSubmit = x.onSubmit) === null || _x$onSubmit === void 0 ? void 0 : _x$onSubmit.call(x, params);
});
},
onReset: function onReset(params) {
var _props$onReset;
(_props$onReset = props.onReset) === null || _props$onReset === void 0 ? void 0 : _props$onReset.call(props, params);
plugins.forEach(function (x) {
var _x$onReset;
return (_x$onReset = x.onReset) === null || _x$onReset === void 0 ? void 0 : _x$onReset.call(x, params);
});
},
getSources: function getSources(params) {
return Promise.all([].concat(_toConsumableArray(plugins.map(function (plugin) {
return plugin.getSources;
})), [props.getSources]).filter(Boolean).map(function (getSources) {
return getNormalizedSources(getSources, params);
})).then(function (nested) {
return flatten(nested);
}).then(function (sources) {
return sources.map(function (source) {
return _objectSpread(_objectSpread({}, source), {}, {
onSelect: function onSelect(params) {
source.onSelect(params);
pluginSubscribers.forEach(function (x) {
var _x$onSelect;
return (_x$onSelect = x.onSelect) === null || _x$onSelect === void 0 ? void 0 : _x$onSelect.call(x, params);
});
},
onActive: function onActive(params) {
source.onActive(params);
pluginSubscribers.forEach(function (x) {
var _x$onActive;
return (_x$onActive = x.onActive) === null || _x$onActive === void 0 ? void 0 : _x$onActive.call(x, params);
});
},
onResolve: function onResolve(params) {
source.onResolve(params);
pluginSubscribers.forEach(function (x) {
var _x$onResolve;
return (_x$onResolve = x.onResolve) === null || _x$onResolve === void 0 ? void 0 : _x$onResolve.call(x, params);
});
}
});
});
});
},
navigator: _objectSpread({
navigate: function navigate(_ref3) {
var itemUrl = _ref3.itemUrl;
environment.location.assign(itemUrl);
},
navigateNewTab: function navigateNewTab(_ref4) {
var itemUrl = _ref4.itemUrl;
var windowReference = environment.open(itemUrl, '_blank', 'noopener');
windowReference === null || windowReference === void 0 ? void 0 : windowReference.focus();
},
navigateNewWindow: function navigateNewWindow(_ref5) {
var itemUrl = _ref5.itemUrl;
environment.open(itemUrl, '_blank', 'noopener');
}
}, props.navigator)
});
}

View file

@ -1,16 +0,0 @@
import { AutocompleteScopeApi, AutocompleteStore, BaseItem, GetEnvironmentProps, GetFormProps, GetInputProps, GetItemProps, GetLabelProps, GetListProps, GetPanelProps, GetRootProps, InternalAutocompleteOptions } from './types';
interface GetPropGettersOptions<TItem extends BaseItem> extends AutocompleteScopeApi<TItem> {
store: AutocompleteStore<TItem>;
props: InternalAutocompleteOptions<TItem>;
}
export declare function getPropGetters<TItem extends BaseItem, TEvent, TMouseEvent, TKeyboardEvent>({ props, refresh, store, ...setters }: GetPropGettersOptions<TItem>): {
getEnvironmentProps: GetEnvironmentProps;
getRootProps: GetRootProps;
getFormProps: GetFormProps<TEvent>;
getLabelProps: GetLabelProps;
getInputProps: GetInputProps<TEvent, TMouseEvent, TKeyboardEvent>;
getPanelProps: GetPanelProps<TMouseEvent>;
getListProps: GetListProps;
getItemProps: GetItemProps<any, TMouseEvent>;
};
export {};

View file

@ -1,335 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
var _excluded = ["props", "refresh", "store"],
_excluded2 = ["inputElement", "formElement", "panelElement"],
_excluded3 = ["inputElement"],
_excluded4 = ["inputElement", "maxLength"],
_excluded5 = ["source"],
_excluded6 = ["item", "source"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { noop } from '@algolia/autocomplete-shared';
import { onInput } from './onInput';
import { onKeyDown as _onKeyDown } from './onKeyDown';
import { getActiveItem, getAutocompleteElementId, isOrContainsNode, isSamsung, getNativeEvent } from './utils';
export function getPropGetters(_ref) {
var props = _ref.props,
refresh = _ref.refresh,
store = _ref.store,
setters = _objectWithoutProperties(_ref, _excluded);
var getEnvironmentProps = function getEnvironmentProps(providedProps) {
var inputElement = providedProps.inputElement,
formElement = providedProps.formElement,
panelElement = providedProps.panelElement,
rest = _objectWithoutProperties(providedProps, _excluded2);
function onMouseDownOrTouchStart(event) {
// The `onTouchStart`/`onMouseDown` events shouldn't trigger the `blur`
// handler when it's not an interaction with Autocomplete.
// We detect it with the following heuristics:
// - the panel is closed AND there are no pending requests
// (no interaction with the autocomplete, no future state updates)
// - OR the touched target is the input element (should open the panel)
var isAutocompleteInteraction = store.getState().isOpen || !store.pendingRequests.isEmpty();
if (!isAutocompleteInteraction || event.target === inputElement) {
return;
}
// @TODO: support cases where there are multiple Autocomplete instances.
// Right now, a second instance makes this computation return false.
var isTargetWithinAutocomplete = [formElement, panelElement].some(function (contextNode) {
return isOrContainsNode(contextNode, event.target);
});
if (isTargetWithinAutocomplete === false) {
store.dispatch('blur', null);
// If requests are still pending when the user closes the panel, they
// could reopen the panel once they resolve.
// We want to prevent any subsequent query from reopening the panel
// because it would result in an unsolicited UI behavior.
if (!props.debug) {
store.pendingRequests.cancelAll();
}
}
}
return _objectSpread({
// We do not rely on the native `blur` event of the input to close the
// panel, but rather on a custom `touchstart`/`mousedown` event outside
// of the autocomplete elements.
// This ensures we don't mistakenly interpret interactions within the
// autocomplete (but outside of the input) as a signal to close the panel.
// For example, clicking reset button causes an input blur, but if
// `openOnFocus=true`, it shouldn't close the panel.
// On touch devices, scrolling results (`touchmove`) causes an input blur
// but shouldn't close the panel.
onTouchStart: onMouseDownOrTouchStart,
onMouseDown: onMouseDownOrTouchStart,
// When scrolling on touch devices (mobiles, tablets, etc.), we want to
// mimic the native platform behavior where the input is blurred to
// hide the virtual keyboard. This gives more vertical space to
// discover all the suggestions showing up in the panel.
onTouchMove: function onTouchMove(event) {
if (store.getState().isOpen === false || inputElement !== props.environment.document.activeElement || event.target === inputElement) {
return;
}
inputElement.blur();
}
}, rest);
};
var getRootProps = function getRootProps(rest) {
return _objectSpread({
role: 'combobox',
'aria-expanded': store.getState().isOpen,
'aria-haspopup': 'listbox',
'aria-controls': store.getState().isOpen ? store.getState().collections.map(function (_ref2) {
var source = _ref2.source;
return getAutocompleteElementId(props.id, 'list', source);
}).join(' ') : undefined,
'aria-labelledby': getAutocompleteElementId(props.id, 'label')
}, rest);
};
var getFormProps = function getFormProps(providedProps) {
var inputElement = providedProps.inputElement,
rest = _objectWithoutProperties(providedProps, _excluded3);
return _objectSpread({
action: '',
noValidate: true,
role: 'search',
onSubmit: function onSubmit(event) {
var _providedProps$inputE;
event.preventDefault();
props.onSubmit(_objectSpread({
event: event,
refresh: refresh,
state: store.getState()
}, setters));
store.dispatch('submit', null);
(_providedProps$inputE = providedProps.inputElement) === null || _providedProps$inputE === void 0 ? void 0 : _providedProps$inputE.blur();
},
onReset: function onReset(event) {
var _providedProps$inputE2;
event.preventDefault();
props.onReset(_objectSpread({
event: event,
refresh: refresh,
state: store.getState()
}, setters));
store.dispatch('reset', null);
(_providedProps$inputE2 = providedProps.inputElement) === null || _providedProps$inputE2 === void 0 ? void 0 : _providedProps$inputE2.focus();
}
}, rest);
};
var getInputProps = function getInputProps(providedProps) {
var _props$environment$na;
function onFocus(event) {
// We want to trigger a query when `openOnFocus` is true
// because the panel should open with the current query.
if (props.openOnFocus || Boolean(store.getState().query)) {
onInput(_objectSpread({
event: event,
props: props,
query: store.getState().completion || store.getState().query,
refresh: refresh,
store: store
}, setters));
}
store.dispatch('focus', null);
}
var _ref3 = providedProps || {},
inputElement = _ref3.inputElement,
_ref3$maxLength = _ref3.maxLength,
maxLength = _ref3$maxLength === void 0 ? 512 : _ref3$maxLength,
rest = _objectWithoutProperties(_ref3, _excluded4);
var activeItem = getActiveItem(store.getState());
var userAgent = ((_props$environment$na = props.environment.navigator) === null || _props$environment$na === void 0 ? void 0 : _props$environment$na.userAgent) || '';
var shouldFallbackKeyHint = isSamsung(userAgent);
var enterKeyHint = props.enterKeyHint || (activeItem !== null && activeItem !== void 0 && activeItem.itemUrl && !shouldFallbackKeyHint ? 'go' : 'search');
return _objectSpread({
'aria-autocomplete': 'both',
'aria-activedescendant': store.getState().isOpen && store.getState().activeItemId !== null ? getAutocompleteElementId(props.id, "item-".concat(store.getState().activeItemId), activeItem === null || activeItem === void 0 ? void 0 : activeItem.source) : undefined,
'aria-controls': store.getState().isOpen ? store.getState().collections.map(function (_ref4) {
var source = _ref4.source;
return getAutocompleteElementId(props.id, 'list', source);
}).join(' ') : undefined,
'aria-labelledby': getAutocompleteElementId(props.id, 'label'),
value: store.getState().completion || store.getState().query,
id: getAutocompleteElementId(props.id, 'input'),
autoComplete: 'off',
autoCorrect: 'off',
autoCapitalize: 'off',
enterKeyHint: enterKeyHint,
spellCheck: 'false',
autoFocus: props.autoFocus,
placeholder: props.placeholder,
maxLength: maxLength,
type: 'search',
onChange: function onChange(event) {
var value = event.currentTarget.value;
if (props.ignoreCompositionEvents && getNativeEvent(event).isComposing) {
setters.setQuery(value);
return;
}
onInput(_objectSpread({
event: event,
props: props,
query: value.slice(0, maxLength),
refresh: refresh,
store: store
}, setters));
},
onCompositionEnd: function onCompositionEnd(event) {
onInput(_objectSpread({
event: event,
props: props,
query: event.currentTarget.value.slice(0, maxLength),
refresh: refresh,
store: store
}, setters));
},
onKeyDown: function onKeyDown(event) {
if (getNativeEvent(event).isComposing) {
return;
}
_onKeyDown(_objectSpread({
event: event,
props: props,
refresh: refresh,
store: store
}, setters));
},
onFocus: onFocus,
// We don't rely on the `blur` event.
// See explanation in `onTouchStart`/`onMouseDown`.
// @MAJOR See if we need to keep this handler.
onBlur: noop,
onClick: function onClick(event) {
// When the panel is closed and you click on the input while
// the input is focused, the `onFocus` event is not triggered
// (default browser behavior).
// In an autocomplete context, it makes sense to open the panel in this
// case.
// We mimic this event by catching the `onClick` event which
// triggers the `onFocus` for the panel to open.
if (providedProps.inputElement === props.environment.document.activeElement && !store.getState().isOpen) {
onFocus(event);
}
}
}, rest);
};
var getLabelProps = function getLabelProps(rest) {
return _objectSpread({
htmlFor: getAutocompleteElementId(props.id, 'input'),
id: getAutocompleteElementId(props.id, 'label')
}, rest);
};
var getListProps = function getListProps(providedProps) {
var _ref5 = providedProps || {},
source = _ref5.source,
rest = _objectWithoutProperties(_ref5, _excluded5);
return _objectSpread({
role: 'listbox',
'aria-labelledby': getAutocompleteElementId(props.id, 'label'),
id: getAutocompleteElementId(props.id, 'list', source)
}, rest);
};
var getPanelProps = function getPanelProps(rest) {
return _objectSpread({
onMouseDown: function onMouseDown(event) {
// Prevents the `activeElement` from being changed to the panel so
// that the blur event is not triggered, otherwise it closes the
// panel.
event.preventDefault();
},
onMouseLeave: function onMouseLeave() {
store.dispatch('mouseleave', null);
}
}, rest);
};
var getItemProps = function getItemProps(providedProps) {
var item = providedProps.item,
source = providedProps.source,
rest = _objectWithoutProperties(providedProps, _excluded6);
return _objectSpread({
id: getAutocompleteElementId(props.id, "item-".concat(item.__autocomplete_id), source),
role: 'option',
'aria-selected': store.getState().activeItemId === item.__autocomplete_id,
onMouseMove: function onMouseMove(event) {
if (item.__autocomplete_id === store.getState().activeItemId) {
return;
}
store.dispatch('mousemove', item.__autocomplete_id);
var activeItem = getActiveItem(store.getState());
if (store.getState().activeItemId !== null && activeItem) {
var _item = activeItem.item,
itemInputValue = activeItem.itemInputValue,
itemUrl = activeItem.itemUrl,
_source = activeItem.source;
_source.onActive(_objectSpread({
event: event,
item: _item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: _source,
state: store.getState()
}, setters));
}
},
onMouseDown: function onMouseDown(event) {
// Prevents the `activeElement` from being changed to the item so it
// can remain with the current `activeElement`.
event.preventDefault();
},
onClick: function onClick(event) {
var itemInputValue = source.getItemInputValue({
item: item,
state: store.getState()
});
var itemUrl = source.getItemUrl({
item: item,
state: store.getState()
});
// If `getItemUrl` is provided, it means that the suggestion
// is a link, not plain text that aims at updating the query.
// We can therefore skip the state change because it will update
// the `activeItemId`, resulting in a UI flash, especially
// noticeable on mobile.
var runPreCommand = itemUrl ? Promise.resolve() : onInput(_objectSpread({
event: event,
nextState: {
isOpen: false
},
props: props,
query: itemInputValue,
refresh: refresh,
store: store
}, setters));
runPreCommand.then(function () {
source.onSelect(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
});
}
}, rest);
};
return {
getEnvironmentProps: getEnvironmentProps,
getRootProps: getRootProps,
getFormProps: getFormProps,
getLabelProps: getLabelProps,
getInputProps: getInputProps,
getPanelProps: getPanelProps,
getListProps: getListProps,
getItemProps: getItemProps
};
}

View file

@ -1,3 +0,0 @@
export * from './createAutocomplete';
export * from './getDefaultProps';
export * from './types';

View file

@ -1,3 +0,0 @@
export * from './createAutocomplete';
export * from './getDefaultProps';
export * from './types';

View file

@ -1,33 +0,0 @@
import { UserAgent } from '@algolia/autocomplete-shared';
import { AutocompleteEnvironment, AutocompleteOptionsWithMetadata, AutocompletePlugin, BaseItem } from '.';
declare type AutocompleteMetadata = {
plugins: Array<{
name: string | undefined;
options: string[];
}>;
options: Record<string, string[]>;
ua: UserAgent[];
};
declare type GetMetadataParams<TItem extends BaseItem, TData = unknown> = {
plugins: Array<AutocompletePlugin<TItem, TData>>;
options: AutocompleteOptionsWithMetadata<TItem>;
};
export declare function getMetadata<TItem extends BaseItem, TData = unknown>({ plugins, options, }: GetMetadataParams<TItem, TData>): {
plugins: {
name: string | undefined;
options: string[];
}[];
options: {
'autocomplete-core': string[];
};
ua: {
segment: string;
version: string;
}[];
};
declare type InlineMetadataParams = {
metadata: AutocompleteMetadata;
environment: AutocompleteEnvironment;
};
export declare function injectMetadata({ metadata, environment, }: InlineMetadataParams): void;
export {};

View file

@ -1,41 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { userAgents } from '@algolia/autocomplete-shared';
export function getMetadata(_ref) {
var _, _options$__autocomple, _options$__autocomple2, _options$__autocomple3;
var plugins = _ref.plugins,
options = _ref.options;
var optionsKey = (_ = (((_options$__autocomple = options.__autocomplete_metadata) === null || _options$__autocomple === void 0 ? void 0 : _options$__autocomple.userAgents) || [])[0]) === null || _ === void 0 ? void 0 : _.segment;
var extraOptions = optionsKey ? _defineProperty({}, optionsKey, Object.keys(((_options$__autocomple2 = options.__autocomplete_metadata) === null || _options$__autocomple2 === void 0 ? void 0 : _options$__autocomple2.options) || {})) : {};
return {
plugins: plugins.map(function (plugin) {
return {
name: plugin.name,
options: Object.keys(plugin.__autocomplete_pluginOptions || [])
};
}),
options: _objectSpread({
'autocomplete-core': Object.keys(options)
}, extraOptions),
ua: userAgents.concat(((_options$__autocomple3 = options.__autocomplete_metadata) === null || _options$__autocomple3 === void 0 ? void 0 : _options$__autocomple3.userAgents) || [])
};
}
export function injectMetadata(_ref3) {
var _environment$navigato, _environment$navigato2;
var metadata = _ref3.metadata,
environment = _ref3.environment;
var isMetadataEnabled = (_environment$navigato = environment.navigator) === null || _environment$navigato === void 0 ? void 0 : (_environment$navigato2 = _environment$navigato.userAgent) === null || _environment$navigato2 === void 0 ? void 0 : _environment$navigato2.includes('Algolia Crawler');
if (isMetadataEnabled) {
var metadataContainer = environment.document.createElement('meta');
var headRef = environment.document.querySelector('head');
metadataContainer.name = 'algolia:metadata';
setTimeout(function () {
metadataContainer.content = JSON.stringify(metadata);
headRef.appendChild(metadataContainer);
}, 0);
}
}

View file

@ -1,18 +0,0 @@
import { AutocompleteScopeApi, AutocompleteState, AutocompleteStore, BaseItem, InternalAutocompleteOptions } from './types';
import { CancelablePromise } from './utils';
interface OnInputParams<TItem extends BaseItem> extends AutocompleteScopeApi<TItem> {
event: any;
/**
* The next partial state to apply after the function is called.
*
* This is useful when we call `onInput` in a different scenario than an
* actual input. For example, we use `onInput` when we click on an item,
* but we want to close the panel in that case.
*/
nextState?: Partial<AutocompleteState<TItem>>;
props: InternalAutocompleteOptions<TItem>;
query: string;
store: AutocompleteStore<TItem>;
}
export declare function onInput<TItem extends BaseItem>({ event, nextState, props, query, refresh, store, ...setters }: OnInputParams<TItem>): CancelablePromise<void>;
export {};

View file

@ -1,143 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
var _excluded = ["event", "nextState", "props", "query", "refresh", "store"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { reshape } from './reshape';
import { preResolve, resolve, postResolve } from './resolve';
import { cancelable, createConcurrentSafePromise, getActiveItem } from './utils';
var lastStalledId = null;
var runConcurrentSafePromise = createConcurrentSafePromise();
export function onInput(_ref) {
var event = _ref.event,
_ref$nextState = _ref.nextState,
nextState = _ref$nextState === void 0 ? {} : _ref$nextState,
props = _ref.props,
query = _ref.query,
refresh = _ref.refresh,
store = _ref.store,
setters = _objectWithoutProperties(_ref, _excluded);
if (lastStalledId) {
props.environment.clearTimeout(lastStalledId);
}
var setCollections = setters.setCollections,
setIsOpen = setters.setIsOpen,
setQuery = setters.setQuery,
setActiveItemId = setters.setActiveItemId,
setStatus = setters.setStatus,
setContext = setters.setContext;
setQuery(query);
setActiveItemId(props.defaultActiveItemId);
if (!query && props.openOnFocus === false) {
var _nextState$isOpen;
var collections = store.getState().collections.map(function (collection) {
return _objectSpread(_objectSpread({}, collection), {}, {
items: []
});
});
setStatus('idle');
setCollections(collections);
setIsOpen((_nextState$isOpen = nextState.isOpen) !== null && _nextState$isOpen !== void 0 ? _nextState$isOpen : props.shouldPanelOpen({
state: store.getState()
}));
// We make sure to update the latest resolved value of the tracked
// promises to keep late resolving promises from "cancelling" the state
// updates performed in this code path.
// We chain with a void promise to respect `onInput`'s expected return type.
var _request = cancelable(runConcurrentSafePromise(collections).then(function () {
return Promise.resolve();
}));
return store.pendingRequests.add(_request);
}
setStatus('loading');
lastStalledId = props.environment.setTimeout(function () {
setStatus('stalled');
}, props.stallThreshold);
// We track the entire promise chain triggered by `onInput` before mutating
// the Autocomplete state to make sure that any state manipulation is based on
// fresh data regardless of when promises individually resolve.
// We don't track nested promises and only rely on the full chain resolution,
// meaning we should only ever manipulate the state once this concurrent-safe
// promise is resolved.
var request = cancelable(runConcurrentSafePromise(props.getSources(_objectSpread({
query: query,
refresh: refresh,
state: store.getState()
}, setters)).then(function (sources) {
return Promise.all(sources.map(function (source) {
return Promise.resolve(source.getItems(_objectSpread({
query: query,
refresh: refresh,
state: store.getState()
}, setters))).then(function (itemsOrDescription) {
return preResolve(itemsOrDescription, source.sourceId, store.getState());
});
})).then(resolve).then(function (responses) {
var __automaticInsights = responses.some(function (_ref2) {
var items = _ref2.items;
return isSearchResponseWithAutomaticInsightsFlag(items);
});
// No need to pollute the context if `__automaticInsights=false`
if (__automaticInsights) {
var _store$getState$conte;
setContext({
algoliaInsightsPlugin: _objectSpread(_objectSpread({}, ((_store$getState$conte = store.getState().context) === null || _store$getState$conte === void 0 ? void 0 : _store$getState$conte.algoliaInsightsPlugin) || {}), {}, {
__automaticInsights: __automaticInsights
})
});
}
return postResolve(responses, sources, store);
}).then(function (collections) {
return reshape({
collections: collections,
props: props,
state: store.getState()
});
});
}))).then(function (collections) {
var _nextState$isOpen2;
// Parameters passed to `onInput` could be stale when the following code
// executes, because `onInput` calls may not resolve in order.
// If it becomes a problem we'll need to save the last passed parameters.
// See: https://codesandbox.io/s/agitated-cookies-y290z
setStatus('idle');
setCollections(collections);
var isPanelOpen = props.shouldPanelOpen({
state: store.getState()
});
setIsOpen((_nextState$isOpen2 = nextState.isOpen) !== null && _nextState$isOpen2 !== void 0 ? _nextState$isOpen2 : props.openOnFocus && !query && isPanelOpen || isPanelOpen);
var highlightedItem = getActiveItem(store.getState());
if (store.getState().activeItemId !== null && highlightedItem) {
var item = highlightedItem.item,
itemInputValue = highlightedItem.itemInputValue,
itemUrl = highlightedItem.itemUrl,
source = highlightedItem.source;
source.onActive(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
}
}).finally(function () {
setStatus('idle');
if (lastStalledId) {
props.environment.clearTimeout(lastStalledId);
}
});
return store.pendingRequests.add(request);
}
function isSearchResponseWithAutomaticInsightsFlag(items) {
return !Array.isArray(items) && Boolean(items === null || items === void 0 ? void 0 : items._automaticInsights);
}

View file

@ -1,8 +0,0 @@
import { AutocompleteScopeApi, AutocompleteStore, BaseItem, InternalAutocompleteOptions } from './types';
interface OnKeyDownOptions<TItem extends BaseItem> extends AutocompleteScopeApi<TItem> {
event: KeyboardEvent;
props: InternalAutocompleteOptions<TItem>;
store: AutocompleteStore<TItem>;
}
export declare function onKeyDown<TItem extends BaseItem>({ event, props, refresh, store, ...setters }: OnKeyDownOptions<TItem>): void;
export {};

View file

@ -1,196 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
var _excluded = ["event", "props", "refresh", "store"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { onInput } from './onInput';
import { getActiveItem, getAutocompleteElementId } from './utils';
export function onKeyDown(_ref) {
var event = _ref.event,
props = _ref.props,
refresh = _ref.refresh,
store = _ref.store,
setters = _objectWithoutProperties(_ref, _excluded);
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
// eslint-disable-next-line no-inner-declarations
var triggerScrollIntoView = function triggerScrollIntoView() {
var highlightedItem = getActiveItem(store.getState());
var nodeItem = props.environment.document.getElementById(getAutocompleteElementId(props.id, "item-".concat(store.getState().activeItemId), highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.source));
if (nodeItem) {
if (nodeItem.scrollIntoViewIfNeeded) {
nodeItem.scrollIntoViewIfNeeded(false);
} else {
nodeItem.scrollIntoView(false);
}
}
}; // eslint-disable-next-line no-inner-declarations
var triggerOnActive = function triggerOnActive() {
var highlightedItem = getActiveItem(store.getState());
if (store.getState().activeItemId !== null && highlightedItem) {
var item = highlightedItem.item,
itemInputValue = highlightedItem.itemInputValue,
itemUrl = highlightedItem.itemUrl,
source = highlightedItem.source;
source.onActive(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
}
}; // Default browser behavior changes the caret placement on ArrowUp and
// ArrowDown.
event.preventDefault();
// When re-opening the panel, we need to split the logic to keep the actions
// synchronized as `onInput` returns a promise.
if (store.getState().isOpen === false && (props.openOnFocus || Boolean(store.getState().query))) {
onInput(_objectSpread({
event: event,
props: props,
query: store.getState().query,
refresh: refresh,
store: store
}, setters)).then(function () {
store.dispatch(event.key, {
nextActiveItemId: props.defaultActiveItemId
});
triggerOnActive();
// Since we rely on the DOM, we need to wait for all the micro tasks to
// finish (which include re-opening the panel) to make sure all the
// elements are available.
setTimeout(triggerScrollIntoView, 0);
});
} else {
store.dispatch(event.key, {});
triggerOnActive();
triggerScrollIntoView();
}
} else if (event.key === 'Escape') {
// This prevents the default browser behavior on `input[type="search"]`
// from removing the query right away because we first want to close the
// panel.
event.preventDefault();
store.dispatch(event.key, null);
// Hitting the `Escape` key signals the end of a user interaction with the
// autocomplete. At this point, we should ignore any requests that are still
// pending and could reopen the panel once they resolve, because that would
// result in an unsolicited UI behavior.
store.pendingRequests.cancelAll();
} else if (event.key === 'Tab') {
store.dispatch('blur', null);
// Hitting the `Tab` key signals the end of a user interaction with the
// autocomplete. At this point, we should ignore any requests that are still
// pending and could reopen the panel once they resolve, because that would
// result in an unsolicited UI behavior.
store.pendingRequests.cancelAll();
} else if (event.key === 'Enter') {
// No active item, so we let the browser handle the native `onSubmit` form
// event.
if (store.getState().activeItemId === null || store.getState().collections.every(function (collection) {
return collection.items.length === 0;
})) {
// If requests are still pending when the panel closes, they could reopen
// the panel once they resolve.
// We want to prevent any subsequent query from reopening the panel
// because it would result in an unsolicited UI behavior.
if (!props.debug) {
store.pendingRequests.cancelAll();
}
return;
}
// This prevents the `onSubmit` event to be sent because an item is
// highlighted.
event.preventDefault();
var _ref2 = getActiveItem(store.getState()),
item = _ref2.item,
itemInputValue = _ref2.itemInputValue,
itemUrl = _ref2.itemUrl,
source = _ref2.source;
if (event.metaKey || event.ctrlKey) {
if (itemUrl !== undefined) {
source.onSelect(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
props.navigator.navigateNewTab({
itemUrl: itemUrl,
item: item,
state: store.getState()
});
}
} else if (event.shiftKey) {
if (itemUrl !== undefined) {
source.onSelect(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
props.navigator.navigateNewWindow({
itemUrl: itemUrl,
item: item,
state: store.getState()
});
}
} else if (event.altKey) {
// Keep native browser behavior
} else {
if (itemUrl !== undefined) {
source.onSelect(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
props.navigator.navigate({
itemUrl: itemUrl,
item: item,
state: store.getState()
});
return;
}
onInput(_objectSpread({
event: event,
nextState: {
isOpen: false
},
props: props,
query: itemInputValue,
refresh: refresh,
store: store
}, setters)).then(function () {
source.onSelect(_objectSpread({
event: event,
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
refresh: refresh,
source: source,
state: store.getState()
}, setters));
});
}
}
}

View file

@ -1,11 +0,0 @@
import { AutocompleteCollection, AutocompleteState, BaseItem, InternalAutocompleteOptions } from './types';
declare type ReshapeParams<TItem extends BaseItem> = {
collections: Array<AutocompleteCollection<any>>;
props: InternalAutocompleteOptions<TItem>;
state: AutocompleteState<TItem>;
};
export declare function reshape<TItem extends BaseItem>({ collections, props, state, }: ReshapeParams<TItem>): {
source: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteReshape").AutocompleteReshapeSource<TItem>;
items: TItem[];
}[];
export {};

View file

@ -1,45 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { flatten } from '@algolia/autocomplete-shared';
export function reshape(_ref) {
var collections = _ref.collections,
props = _ref.props,
state = _ref.state;
// Sources are grouped by `sourceId` to conveniently pick them via destructuring.
// Example: `const { recentSearchesPlugin } = sourcesBySourceId`
var originalSourcesBySourceId = collections.reduce(function (acc, collection) {
return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, collection.source.sourceId, _objectSpread(_objectSpread({}, collection.source), {}, {
getItems: function getItems() {
// We provide the resolved items from the collection to the `reshape` prop.
return flatten(collection.items);
}
})));
}, {});
var _props$plugins$reduce = props.plugins.reduce(function (acc, plugin) {
if (plugin.reshape) {
return plugin.reshape(acc);
}
return acc;
}, {
sourcesBySourceId: originalSourcesBySourceId,
state: state
}),
sourcesBySourceId = _props$plugins$reduce.sourcesBySourceId;
var reshapeSources = props.reshape({
sourcesBySourceId: sourcesBySourceId,
sources: Object.values(sourcesBySourceId),
state: state
});
// We reconstruct the collections with the items modified by the `reshape` prop.
return flatten(reshapeSources).filter(Boolean).map(function (source) {
return {
source: source,
items: source.getItems()
};
});
}

View file

@ -1,43 +0,0 @@
import type { ExecuteResponse, RequesterDescription, TransformResponse } from '@algolia/autocomplete-preset-algolia';
import type { SearchResponse } from '@algolia/autocomplete-shared';
import { MultipleQueriesQuery, SearchForFacetValuesResponse } from '@algolia/client-search';
import { AutocompleteState, AutocompleteStore, BaseItem, InternalAutocompleteSource } from './types';
declare type RequestDescriptionPreResolved<TItem extends BaseItem> = Pick<RequesterDescription<TItem>, 'execute' | 'requesterId' | 'searchClient' | 'transformResponse'> & {
requests: Array<{
query: MultipleQueriesQuery;
sourceId: string;
transformResponse: TransformResponse<TItem>;
}>;
};
declare type RequestDescriptionPreResolvedCustom<TItem extends BaseItem> = {
items: TItem[] | TItem[][];
sourceId: string;
transformResponse?: undefined;
};
export declare function preResolve<TItem extends BaseItem>(itemsOrDescription: TItem[] | TItem[][] | RequesterDescription<TItem>, sourceId: string, state: AutocompleteState<TItem>): RequestDescriptionPreResolved<TItem> | RequestDescriptionPreResolvedCustom<TItem>;
export declare function resolve<TItem extends BaseItem>(items: Array<RequestDescriptionPreResolved<TItem> | RequestDescriptionPreResolvedCustom<TItem>>): Promise<(RequestDescriptionPreResolvedCustom<TItem> | {
items: SearchForFacetValuesResponse | SearchResponse<TItem>;
sourceId: string;
transformResponse: TransformResponse<TItem>;
})[]>;
export declare function postResolve<TItem extends BaseItem>(responses: Array<RequestDescriptionPreResolvedCustom<TItem> | ExecuteResponse<TItem>[0]>, sources: Array<InternalAutocompleteSource<TItem>>, store: AutocompleteStore<TItem>): {
source: InternalAutocompleteSource<TItem>;
items: {
label: string;
count: number;
_highlightResult: {
label: {
value: string;
};
};
}[][] | {
label: string;
count: number;
_highlightResult: {
label: {
value: string;
};
};
}[] | import("@algolia/client-search").Hit<TItem>[] | (SearchForFacetValuesResponse | SearchResponse<TItem> | TItem[] | TItem[][])[];
}[];
export {};

View file

@ -1,114 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
import { decycle, flatten, invariant } from '@algolia/autocomplete-shared';
import { mapToAlgoliaResponse } from './utils';
function isDescription(item) {
return Boolean(item.execute);
}
function isRequesterDescription(description) {
return Boolean(description === null || description === void 0 ? void 0 : description.execute);
}
export function preResolve(itemsOrDescription, sourceId, state) {
if (isRequesterDescription(itemsOrDescription)) {
var contextParameters = itemsOrDescription.requesterId === 'algolia' ? Object.assign.apply(Object, [{}].concat(_toConsumableArray(Object.keys(state.context).map(function (key) {
var _state$context$key;
return (_state$context$key = state.context[key]) === null || _state$context$key === void 0 ? void 0 : _state$context$key.__algoliaSearchParameters;
})))) : {};
return _objectSpread(_objectSpread({}, itemsOrDescription), {}, {
requests: itemsOrDescription.queries.map(function (query) {
return {
query: itemsOrDescription.requesterId === 'algolia' ? _objectSpread(_objectSpread({}, query), {}, {
params: _objectSpread(_objectSpread({}, contextParameters), query.params)
}) : query,
sourceId: sourceId,
transformResponse: itemsOrDescription.transformResponse
};
})
});
}
return {
items: itemsOrDescription,
sourceId: sourceId
};
}
export function resolve(items) {
var packed = items.reduce(function (acc, current) {
if (!isDescription(current)) {
acc.push(current);
return acc;
}
var searchClient = current.searchClient,
execute = current.execute,
requesterId = current.requesterId,
requests = current.requests;
var container = acc.find(function (item) {
return isDescription(current) && isDescription(item) && item.searchClient === searchClient && Boolean(requesterId) && item.requesterId === requesterId;
});
if (container) {
var _container$items;
(_container$items = container.items).push.apply(_container$items, _toConsumableArray(requests));
} else {
var request = {
execute: execute,
requesterId: requesterId,
items: requests,
searchClient: searchClient
};
acc.push(request);
}
return acc;
}, []);
var values = packed.map(function (maybeDescription) {
if (!isDescription(maybeDescription)) {
return Promise.resolve(maybeDescription);
}
var _ref = maybeDescription,
execute = _ref.execute,
items = _ref.items,
searchClient = _ref.searchClient;
return execute({
searchClient: searchClient,
requests: items
});
});
return Promise.all(values).then(function (responses) {
return flatten(responses);
});
}
export function postResolve(responses, sources, store) {
return sources.map(function (source) {
var matches = responses.filter(function (response) {
return response.sourceId === source.sourceId;
});
var results = matches.map(function (_ref2) {
var items = _ref2.items;
return items;
});
var transform = matches[0].transformResponse;
var items = transform ? transform(mapToAlgoliaResponse(results)) : results;
source.onResolve({
source: source,
results: results,
items: items,
state: store.getState()
});
invariant(Array.isArray(items), function () {
return "The `getItems` function from source \"".concat(source.sourceId, "\" must return an array of items but returned type ").concat(JSON.stringify(_typeof(items)), ":\n\n").concat(JSON.stringify(decycle(items), null, 2), ".\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems");
});
invariant(items.every(Boolean), "The `getItems` function from source \"".concat(source.sourceId, "\" must return an array of items but returned ").concat(JSON.stringify(undefined), ".\n\nDid you forget to return items?\n\nSee: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/sources/#param-getitems"));
return {
source: source,
items: items
};
});
}

View file

@ -1,2 +0,0 @@
import { Reducer } from './types';
export declare const stateReducer: Reducer;

View file

@ -1,145 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { getItemsCount, invariant } from '@algolia/autocomplete-shared';
import { getCompletion } from './getCompletion';
import { getNextActiveItemId } from './utils';
export var stateReducer = function stateReducer(state, action) {
switch (action.type) {
case 'setActiveItemId':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: action.payload
});
}
case 'setQuery':
{
return _objectSpread(_objectSpread({}, state), {}, {
query: action.payload,
completion: null
});
}
case 'setCollections':
{
return _objectSpread(_objectSpread({}, state), {}, {
collections: action.payload
});
}
case 'setIsOpen':
{
return _objectSpread(_objectSpread({}, state), {}, {
isOpen: action.payload
});
}
case 'setStatus':
{
return _objectSpread(_objectSpread({}, state), {}, {
status: action.payload
});
}
case 'setContext':
{
return _objectSpread(_objectSpread({}, state), {}, {
context: _objectSpread(_objectSpread({}, state.context), action.payload)
});
}
case 'ArrowDown':
{
var nextState = _objectSpread(_objectSpread({}, state), {}, {
activeItemId: action.payload.hasOwnProperty('nextActiveItemId') ? action.payload.nextActiveItemId : getNextActiveItemId(1, state.activeItemId, getItemsCount(state), action.props.defaultActiveItemId)
});
return _objectSpread(_objectSpread({}, nextState), {}, {
completion: getCompletion({
state: nextState
})
});
}
case 'ArrowUp':
{
var _nextState = _objectSpread(_objectSpread({}, state), {}, {
activeItemId: getNextActiveItemId(-1, state.activeItemId, getItemsCount(state), action.props.defaultActiveItemId)
});
return _objectSpread(_objectSpread({}, _nextState), {}, {
completion: getCompletion({
state: _nextState
})
});
}
case 'Escape':
{
if (state.isOpen) {
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: null,
isOpen: false,
completion: null
});
}
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: null,
query: '',
status: 'idle',
collections: []
});
}
case 'submit':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: null,
isOpen: false,
status: 'idle'
});
}
case 'reset':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId:
// Since we open the panel on reset when openOnFocus=true
// we need to restore the highlighted index to the defaultActiveItemId. (DocSearch use-case)
// Since we close the panel when openOnFocus=false
// we lose track of the highlighted index. (Query-suggestions use-case)
action.props.openOnFocus === true ? action.props.defaultActiveItemId : null,
status: 'idle',
completion: null,
query: ''
});
}
case 'focus':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: action.props.defaultActiveItemId,
isOpen: (action.props.openOnFocus || Boolean(state.query)) && action.props.shouldPanelOpen({
state: state
})
});
}
case 'blur':
{
if (action.props.debug) {
return state;
}
return _objectSpread(_objectSpread({}, state), {}, {
isOpen: false,
activeItemId: null
});
}
case 'mousemove':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: action.payload
});
}
case 'mouseleave':
{
return _objectSpread(_objectSpread({}, state), {}, {
activeItemId: action.props.defaultActiveItemId
});
}
default:
invariant(false, "The reducer action ".concat(JSON.stringify(action.type), " is not supported."));
return state;
}
};

View file

@ -1,15 +0,0 @@
import { CancelablePromiseList } from '../utils';
import { BaseItem, InternalAutocompleteOptions, AutocompleteState } from './';
export interface AutocompleteStore<TItem extends BaseItem> {
getState(): AutocompleteState<TItem>;
dispatch(action: ActionType, payload: any): void;
pendingRequests: CancelablePromiseList<void>;
}
export declare type Reducer = <TItem extends BaseItem>(state: AutocompleteState<TItem>, action: Action<TItem, any>) => AutocompleteState<TItem>;
declare type Action<TItem extends BaseItem, TPayload> = {
type: ActionType;
props: InternalAutocompleteOptions<TItem>;
payload: TPayload;
};
export declare type ActionType = 'setActiveItemId' | 'setQuery' | 'setCollections' | 'setIsOpen' | 'setStatus' | 'setContext' | 'ArrowUp' | 'ArrowDown' | 'Escape' | 'Enter' | 'submit' | 'reset' | 'focus' | 'blur' | 'mousemove' | 'mouseleave' | 'click';
export {};

View file

@ -1 +0,0 @@
export {};

View file

@ -1,7 +0,0 @@
import { BaseItem, OnActiveParams, OnResolveParams, OnSelectParams } from './';
export declare type AutocompleteSubscriber<TItem extends BaseItem> = {
onSelect(params: OnSelectParams<TItem>): void;
onActive(params: OnActiveParams<TItem>): void;
onResolve(params: OnResolveParams<TItem>): void;
};
export declare type AutocompleteSubscribers<TItem extends BaseItem> = Array<Partial<AutocompleteSubscriber<TItem>>>;

View file

@ -1 +0,0 @@
export {};

View file

@ -1,22 +0,0 @@
export * from '@algolia/autocomplete-shared/dist/esm/core';
export * from './AutocompleteStore';
export * from './AutocompleteSubscribers';
import { CreateAlgoliaInsightsPluginParams, AutocompleteInsightsApi as _AutocompleteInsightsApi, AlgoliaInsightsHit as _AlgoliaInsightsHit } from '@algolia/autocomplete-plugin-algolia-insights';
import { AutocompleteOptions as _AutocompleteOptions, InternalAutocompleteOptions as _InternalAutocompleteOptions, BaseItem } from '@algolia/autocomplete-shared/dist/esm/core';
export declare type AutocompleteInsightsApi = _AutocompleteInsightsApi;
export declare type AlgoliaInsightsHit = _AlgoliaInsightsHit;
declare type InsightsOption = {
/**
* Whether to enable the Insights plugin and load the Insights library if it has not been loaded yet.
*
* See [**autocomplete-plugin-algolia-insights**](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/) for more information.
*
* @default undefined
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-insights
*/
insights?: CreateAlgoliaInsightsPluginParams | boolean | undefined;
};
export interface AutocompleteOptions<TItem extends BaseItem> extends _AutocompleteOptions<TItem>, InsightsOption {
}
export interface InternalAutocompleteOptions<TItem extends BaseItem> extends _InternalAutocompleteOptions<TItem>, InsightsOption {
}

View file

@ -1,4 +0,0 @@
export * from '@algolia/autocomplete-shared/dist/esm/core';
export * from './AutocompleteStore';
export * from './AutocompleteSubscribers';
export {};

View file

@ -1,15 +0,0 @@
declare type PromiseExecutor<TValue> = (resolve: (value: TValue | PromiseLike<TValue>) => void, reject: (reason?: any) => void) => void;
export declare type CancelablePromise<TValue> = {
then<TResultFulfilled = TValue, TResultRejected = never>(onfulfilled?: ((value: TValue) => TResultFulfilled | PromiseLike<TResultFulfilled> | CancelablePromise<TResultFulfilled>) | undefined | null, onrejected?: ((reason: any) => TResultRejected | PromiseLike<TResultRejected> | CancelablePromise<TResultRejected>) | undefined | null): CancelablePromise<TResultFulfilled | TResultRejected>;
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult> | CancelablePromise<TResult>) | undefined | null): CancelablePromise<TValue | TResult>;
finally(onfinally?: (() => void) | undefined | null): CancelablePromise<TValue>;
cancel(): void;
isCanceled(): boolean;
};
export declare function createCancelablePromise<TValue>(executor: PromiseExecutor<TValue>): CancelablePromise<TValue>;
export declare namespace createCancelablePromise {
var resolve: <TValue>(value?: TValue | PromiseLike<TValue> | CancelablePromise<TValue> | undefined) => CancelablePromise<TValue | CancelablePromise<TValue> | undefined>;
var reject: (reason?: any) => CancelablePromise<never>;
}
export declare function cancelable<TValue>(promise: Promise<TValue>): CancelablePromise<TValue>;
export {};

View file

@ -1,62 +0,0 @@
function createInternalCancelablePromise(promise, initialState) {
var state = initialState;
return {
then: function then(onfulfilled, onrejected) {
return createInternalCancelablePromise(promise.then(createCallback(onfulfilled, state, promise), createCallback(onrejected, state, promise)), state);
},
catch: function _catch(onrejected) {
return createInternalCancelablePromise(promise.catch(createCallback(onrejected, state, promise)), state);
},
finally: function _finally(onfinally) {
if (onfinally) {
state.onCancelList.push(onfinally);
}
return createInternalCancelablePromise(promise.finally(createCallback(onfinally && function () {
state.onCancelList = [];
return onfinally();
}, state, promise)), state);
},
cancel: function cancel() {
state.isCanceled = true;
var callbacks = state.onCancelList;
state.onCancelList = [];
callbacks.forEach(function (callback) {
callback();
});
},
isCanceled: function isCanceled() {
return state.isCanceled === true;
}
};
}
export function createCancelablePromise(executor) {
return createInternalCancelablePromise(new Promise(function (resolve, reject) {
return executor(resolve, reject);
}), {
isCanceled: false,
onCancelList: []
});
}
createCancelablePromise.resolve = function (value) {
return cancelable(Promise.resolve(value));
};
createCancelablePromise.reject = function (reason) {
return cancelable(Promise.reject(reason));
};
export function cancelable(promise) {
return createInternalCancelablePromise(promise, {
isCanceled: false,
onCancelList: []
});
}
function createCallback(onResult, state, fallback) {
if (!onResult) {
return fallback;
}
return function callback(arg) {
if (state.isCanceled) {
return arg;
}
return onResult(arg);
};
}

View file

@ -1,21 +0,0 @@
import { CancelablePromise } from '.';
export declare type CancelablePromiseList<TValue> = {
/**
* Add a cancelable promise to the list.
*
* @param cancelablePromise The cancelable promise to add.
*/
add(cancelablePromise: CancelablePromise<TValue>): CancelablePromise<TValue>;
/**
* Cancel all pending promises.
*
* Requests aren't actually stopped. All pending promises will settle, but
* attached handlers won't run.
*/
cancelAll(): void;
/**
* Whether there are pending promises in the list.
*/
isEmpty(): boolean;
};
export declare function createCancelablePromiseList<TValue>(): CancelablePromiseList<TValue>;

View file

@ -1,21 +0,0 @@
export function createCancelablePromiseList() {
var list = [];
return {
add: function add(cancelablePromise) {
list.push(cancelablePromise);
return cancelablePromise.finally(function () {
list = list.filter(function (item) {
return item !== cancelablePromise;
});
});
},
cancelAll: function cancelAll() {
list.forEach(function (promise) {
return promise.cancel();
});
},
isEmpty: function isEmpty() {
return list.length === 0;
}
};
}

View file

@ -1,8 +0,0 @@
import { MaybePromise } from '@algolia/autocomplete-shared';
/**
* Creates a runner that executes promises in a concurrent-safe way.
*
* This is useful to prevent older promises to resolve after a newer promise,
* otherwise resulting in stale resolved values.
*/
export declare function createConcurrentSafePromise(): <TValue>(promise: MaybePromise<TValue>) => Promise<TValue>;

View file

@ -1,36 +0,0 @@
/**
* Creates a runner that executes promises in a concurrent-safe way.
*
* This is useful to prevent older promises to resolve after a newer promise,
* otherwise resulting in stale resolved values.
*/
export function createConcurrentSafePromise() {
var basePromiseId = -1;
var latestResolvedId = -1;
var latestResolvedValue = undefined;
return function runConcurrentSafePromise(promise) {
basePromiseId++;
var currentPromiseId = basePromiseId;
return Promise.resolve(promise).then(function (x) {
// The promise might take too long to resolve and get outdated. This would
// result in resolving stale values.
// When this happens, we ignore the promise value and return the one
// coming from the latest resolved value.
//
// +----------------------------------+
// | 100ms |
// | run(1) +---> R1 |
// | 300ms |
// | run(2) +-------------> R2 (SKIP) |
// | 200ms |
// | run(3) +--------> R3 |
// +----------------------------------+
if (latestResolvedValue && currentPromiseId < latestResolvedId) {
return latestResolvedValue;
}
latestResolvedId = currentPromiseId;
latestResolvedValue = x;
return x;
});
};
}

View file

@ -1,7 +0,0 @@
import { AutocompleteState, BaseItem } from '../types';
export declare function getActiveItem<TItem extends BaseItem>(state: AutocompleteState<TItem>): {
item: TItem;
itemInputValue: string;
itemUrl: string | undefined;
source: import("@algolia/autocomplete-shared/dist/esm/core/AutocompleteSource").InternalAutocompleteSource<TItem>;
} | null;

View file

@ -1,77 +0,0 @@
// We don't have access to the autocomplete source when we call `onKeyDown`
// or `onClick` because those are native browser events.
// However, we can get the source from the suggestion index.
function getCollectionFromActiveItemId(state) {
// Given 3 sources with respectively 1, 2 and 3 suggestions: [1, 2, 3]
// We want to get the accumulated counts:
// [1, 1 + 2, 1 + 2 + 3] = [1, 3, 3 + 3] = [1, 3, 6]
var accumulatedCollectionsCount = state.collections.map(function (collections) {
return collections.items.length;
}).reduce(function (acc, collectionsCount, index) {
var previousValue = acc[index - 1] || 0;
var nextValue = previousValue + collectionsCount;
acc.push(nextValue);
return acc;
}, []);
// Based on the accumulated counts, we can infer the index of the suggestion.
var collectionIndex = accumulatedCollectionsCount.reduce(function (acc, current) {
if (current <= state.activeItemId) {
return acc + 1;
}
return acc;
}, 0);
return state.collections[collectionIndex];
}
/**
* Gets the highlighted index relative to a suggestion object (not the absolute
* highlighted index).
*
* Example:
* [['a', 'b'], ['c', 'd', 'e'], ['f']]
*
* (absolute: 3, relative: 1)
*/
function getRelativeActiveItemId(_ref) {
var state = _ref.state,
collection = _ref.collection;
var isOffsetFound = false;
var counter = 0;
var previousItemsOffset = 0;
while (isOffsetFound === false) {
var currentCollection = state.collections[counter];
if (currentCollection === collection) {
isOffsetFound = true;
break;
}
previousItemsOffset += currentCollection.items.length;
counter++;
}
return state.activeItemId - previousItemsOffset;
}
export function getActiveItem(state) {
var collection = getCollectionFromActiveItemId(state);
if (!collection) {
return null;
}
var item = collection.items[getRelativeActiveItemId({
state: state,
collection: collection
})];
var source = collection.source;
var itemInputValue = source.getItemInputValue({
item: item,
state: state
});
var itemUrl = source.getItemUrl({
item: item,
state: state
});
return {
item: item,
itemInputValue: itemInputValue,
itemUrl: itemUrl,
source: source
};
}

View file

@ -1,17 +0,0 @@
/**
* Returns the next active item ID from the current state.
*
* We allow circular keyboard navigation from the base index.
* The base index can either be `null` (nothing is highlighted) or `0`
* (the first item is highlighted).
* The base index is allowed to get assigned `null` only if
* `props.defaultActiveItemId` is `null`. This pattern allows to "stop"
* by the actual query before navigating to other suggestions as seen on
* Google or Amazon.
*
* @param moveAmount The offset to increment (or decrement) the last index
* @param baseIndex The current index to compute the next index from
* @param itemCount The number of items
* @param defaultActiveItemId The default active index to fallback to
*/
export declare function getNextActiveItemId(moveAmount: number, baseIndex: number | null, itemCount: number, defaultActiveItemId: number | null): number | null;

View file

@ -1,29 +0,0 @@
/**
* Returns the next active item ID from the current state.
*
* We allow circular keyboard navigation from the base index.
* The base index can either be `null` (nothing is highlighted) or `0`
* (the first item is highlighted).
* The base index is allowed to get assigned `null` only if
* `props.defaultActiveItemId` is `null`. This pattern allows to "stop"
* by the actual query before navigating to other suggestions as seen on
* Google or Amazon.
*
* @param moveAmount The offset to increment (or decrement) the last index
* @param baseIndex The current index to compute the next index from
* @param itemCount The number of items
* @param defaultActiveItemId The default active index to fallback to
*/
export function getNextActiveItemId(moveAmount, baseIndex, itemCount, defaultActiveItemId) {
if (!itemCount) {
return null;
}
if (moveAmount < 0 && (baseIndex === null || defaultActiveItemId !== null && baseIndex === 0)) {
return itemCount + moveAmount;
}
var numericIndex = (baseIndex === null ? -1 : baseIndex) + moveAmount;
if (numericIndex <= -1 || numericIndex >= itemCount) {
return defaultActiveItemId === null ? null : 0;
}
return numericIndex;
}

View file

@ -1,2 +0,0 @@
import { BaseItem, GetSources, GetSourcesParams, InternalGetSources } from '../types';
export declare function getNormalizedSources<TItem extends BaseItem>(getSources: GetSources<TItem>, params: GetSourcesParams<TItem>): ReturnType<InternalGetSources<TItem>>;

View file

@ -1,48 +0,0 @@
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
import { invariant, decycle, noop } from '@algolia/autocomplete-shared';
export function getNormalizedSources(getSources, params) {
var seenSourceIds = [];
return Promise.resolve(getSources(params)).then(function (sources) {
invariant(Array.isArray(sources), function () {
return "The `getSources` function must return an array of sources but returned type ".concat(JSON.stringify(_typeof(sources)), ":\n\n").concat(JSON.stringify(decycle(sources), null, 2));
});
return Promise.all(sources
// We allow `undefined` and `false` sources to allow users to use
// `Boolean(query) && source` (=> `false`).
// We need to remove these values at this point.
.filter(function (maybeSource) {
return Boolean(maybeSource);
}).map(function (source) {
invariant(typeof source.sourceId === 'string', 'A source must provide a `sourceId` string.');
if (seenSourceIds.includes(source.sourceId)) {
throw new Error("[Autocomplete] The `sourceId` ".concat(JSON.stringify(source.sourceId), " is not unique."));
}
seenSourceIds.push(source.sourceId);
var defaultSource = {
getItemInputValue: function getItemInputValue(_ref) {
var state = _ref.state;
return state.query;
},
getItemUrl: function getItemUrl() {
return undefined;
},
onSelect: function onSelect(_ref2) {
var setIsOpen = _ref2.setIsOpen;
setIsOpen(false);
},
onActive: noop,
onResolve: noop
};
Object.keys(defaultSource).forEach(function (key) {
defaultSource[key].__default = true;
});
var normalizedSource = _objectSpread(_objectSpread({}, defaultSource), source);
return Promise.resolve(normalizedSource);
}));
});
}

View file

@ -1,11 +0,0 @@
export * from './createCancelablePromise';
export * from './createCancelablePromiseList';
export * from './createConcurrentSafePromise';
export * from './getNextActiveItemId';
export * from './getNormalizedSources';
export * from './getActiveItem';
export * from './getAutocompleteElementId';
export * from './isOrContainsNode';
export * from './isSamsung';
export * from './mapToAlgoliaResponse';
export * from './getNativeEvent';

View file

@ -1,11 +0,0 @@
export * from './createCancelablePromise';
export * from './createCancelablePromiseList';
export * from './createConcurrentSafePromise';
export * from './getNextActiveItemId';
export * from './getNormalizedSources';
export * from './getActiveItem';
export * from './getAutocompleteElementId';
export * from './isOrContainsNode';
export * from './isSamsung';
export * from './mapToAlgoliaResponse';
export * from './getNativeEvent';

View file

@ -1 +0,0 @@
export declare function isOrContainsNode(parent: Node, child: Node): boolean;

View file

@ -1,3 +0,0 @@
export function isOrContainsNode(parent, child) {
return parent === child || parent.contains(child);
}

View file

@ -1 +0,0 @@
export declare function isSamsung(userAgent: string): boolean;

View file

@ -1,4 +0,0 @@
var regex = /((gt|sm)-|galaxy nexus)|samsung[- ]|samsungbrowser/i;
export function isSamsung(userAgent) {
return Boolean(userAgent && userAgent.match(regex));
}

View file

@ -1,15 +0,0 @@
import type { SearchResponse } from '@algolia/autocomplete-shared';
import type { SearchForFacetValuesResponse } from '@algolia/client-search';
export declare function mapToAlgoliaResponse<THit>(rawResults: Array<SearchResponse<THit> | SearchForFacetValuesResponse>): {
results: (SearchResponse<THit> | SearchForFacetValuesResponse)[];
hits: import("@algolia/client-search").Hit<THit>[][];
facetHits: {
label: string;
count: number;
_highlightResult: {
label: {
value: string;
};
};
}[][];
};

View file

@ -1,23 +0,0 @@
export function mapToAlgoliaResponse(rawResults) {
return {
results: rawResults,
hits: rawResults.map(function (result) {
return result.hits;
}).filter(Boolean),
facetHits: rawResults.map(function (result) {
var _facetHits;
return (_facetHits = result.facetHits) === null || _facetHits === void 0 ? void 0 : _facetHits.map(function (facetHit) {
// Bring support for the highlighting components.
return {
label: facetHit.value,
count: facetHit.count,
_highlightResult: {
label: {
value: facetHit.highlighted
}
}
};
});
}).filter(Boolean)
};
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,42 +0,0 @@
{
"name": "@algolia/autocomplete-core",
"description": "Core primitives for building autocomplete experiences.",
"version": "1.17.7",
"license": "MIT",
"homepage": "https://github.com/algolia/autocomplete",
"repository": "algolia/autocomplete",
"author": {
"name": "Algolia, Inc.",
"url": "https://www.algolia.com"
},
"source": "src/index.ts",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
"main": "dist/umd/index.production.js",
"umd:main": "dist/umd/index.production.js",
"unpkg": "dist/umd/index.production.js",
"jsdelivr": "dist/umd/index.production.js",
"sideEffects": false,
"files": [
"dist/"
],
"scripts": {
"build:clean": "rm -rf ./dist",
"build:esm": "babel src --root-mode upward --extensions '.ts,.tsx' --out-dir dist/esm --ignore '**/*/__tests__/'",
"build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm",
"build:umd": "rollup --config",
"build": "yarn build:clean && yarn build:umd && yarn build:esm && yarn build:types",
"on:change": "concurrently \"yarn build:esm\" \"yarn build:types\"",
"prepare": "yarn build:esm && yarn build:types",
"watch": "watch \"yarn on:change\" --ignoreDirectoryPattern \"/dist/\""
},
"dependencies": {
"@algolia/autocomplete-plugin-algolia-insights": "1.17.7",
"@algolia/autocomplete-shared": "1.17.7"
},
"devDependencies": {
"@algolia/autocomplete-preset-algolia": "1.17.7",
"@algolia/client-search": "4.16.0",
"algoliasearch": "4.16.0"
}
}

View file

@ -1,15 +0,0 @@
# @algolia/autocomplete-plugin-algolia-insights
The Algolia Insights plugin automatically sends click and conversion events to the [Algolia Insights API](https://www.algolia.com/doc/rest-api/insights]) whenever a user interacts with the autocomplete.
## Installation
```sh
yarn add @algolia/autocomplete-plugin-algolia-insights
# or
npm install @algolia/autocomplete-plugin-algolia-insights
```
## Documentation
See [**Documentation**](https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights).

View file

@ -1,47 +0,0 @@
import { AutocompletePlugin } from '@algolia/autocomplete-shared';
import { InsightsClient, InsightsMethodMap, OnActiveParams, OnItemsChangeParams, OnSelectParams } from './types';
export declare type CreateAlgoliaInsightsPluginParams = {
/**
* The initialized Search Insights client.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-insightsclient
*/
insightsClient?: InsightsClient;
/**
* Insights parameters to forward to the Insights clients init method.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-insightsinitparams
*/
insightsInitParams?: Partial<InsightsMethodMap['init'][0]>;
/**
* Hook to send an Insights event when the items change.
*
* By default, it sends a `viewedObjectIDs` event.
*
* In as-you-type experiences, items change as the user types. This hook is debounced every 400ms to reflect actual items that users notice and avoid generating too many events for items matching "in progress" queries.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-onitemschange
*/
onItemsChange?(params: OnItemsChangeParams): void;
/**
* Hook to send an Insights event when an item is selected.
*
* By default, it sends a clickedObjectIDsAfterSearch event.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-onselect
*/
onSelect?(params: OnSelectParams): void;
/**
* Hook to send an Insights event when an item is active.
*
* By default, it doesn't send any events.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-onactive
*/
onActive?(params: OnActiveParams): void;
/**
* @internal
*/
__autocomplete_clickAnalytics?: boolean;
};
export declare function createAlgoliaInsightsPlugin(options: CreateAlgoliaInsightsPluginParams): AutocompletePlugin<any, undefined>;

View file

@ -1,269 +0,0 @@
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { createRef, debounce, isEqual, noop, safelyRunOnBrowser } from '@algolia/autocomplete-shared';
import { createClickedEvent } from './createClickedEvent';
import { createSearchInsightsApi } from './createSearchInsightsApi';
import { createViewedEvents } from './createViewedEvents';
import { isAlgoliaInsightsHit } from './isAlgoliaInsightsHit';
var VIEW_EVENT_DELAY = 400;
var ALGOLIA_INSIGHTS_VERSION = '2.15.0';
var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@".concat(ALGOLIA_INSIGHTS_VERSION, "/dist/search-insights.min.js");
var sendViewedObjectIDs = debounce(function (_ref) {
var onItemsChange = _ref.onItemsChange,
items = _ref.items,
insights = _ref.insights,
state = _ref.state;
onItemsChange({
insights: insights,
insightsEvents: createViewedEvents({
items: items
}).map(function (event) {
return _objectSpread({
eventName: 'Items Viewed'
}, event);
}),
state: state
});
}, VIEW_EVENT_DELAY);
export function createAlgoliaInsightsPlugin(options) {
var _getOptions = getOptions(options),
providedInsightsClient = _getOptions.insightsClient,
insightsInitParams = _getOptions.insightsInitParams,
onItemsChange = _getOptions.onItemsChange,
onSelectEvent = _getOptions.onSelect,
onActiveEvent = _getOptions.onActive,
__autocomplete_clickAnalytics = _getOptions.__autocomplete_clickAnalytics;
var insightsClient = providedInsightsClient;
if (!providedInsightsClient) {
safelyRunOnBrowser(function (_ref2) {
var window = _ref2.window;
var pointer = window.AlgoliaAnalyticsObject || 'aa';
if (typeof pointer === 'string') {
insightsClient = window[pointer];
}
if (!insightsClient) {
window.AlgoliaAnalyticsObject = pointer;
if (!window[pointer]) {
window[pointer] = function () {
if (!window[pointer].queue) {
window[pointer].queue = [];
}
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
window[pointer].queue.push(args);
};
}
window[pointer].version = ALGOLIA_INSIGHTS_VERSION;
insightsClient = window[pointer];
loadInsights(window);
}
});
}
// We return an empty plugin if `insightsClient` is still undefined at
// this stage, which can happen in server environments.
if (!insightsClient) {
return {};
}
if (insightsInitParams) {
insightsClient('init', _objectSpread({
partial: true
}, insightsInitParams));
}
var insights = createSearchInsightsApi(insightsClient);
var previousItems = createRef([]);
var debouncedOnStateChange = debounce(function (_ref3) {
var state = _ref3.state;
if (!state.isOpen) {
return;
}
var items = state.collections.reduce(function (acc, current) {
return [].concat(_toConsumableArray(acc), _toConsumableArray(current.items));
}, []).filter(isAlgoliaInsightsHit);
if (!isEqual(previousItems.current.map(function (x) {
return x.objectID;
}), items.map(function (x) {
return x.objectID;
}))) {
previousItems.current = items;
if (items.length > 0) {
sendViewedObjectIDs({
onItemsChange: onItemsChange,
items: items,
insights: insights,
state: state
});
}
}
}, 0);
return {
name: 'aa.algoliaInsightsPlugin',
subscribe: function subscribe(_ref4) {
var setContext = _ref4.setContext,
onSelect = _ref4.onSelect,
onActive = _ref4.onActive;
var isAuthenticatedToken = false;
function setInsightsContext(userToken) {
setContext({
algoliaInsightsPlugin: {
__algoliaSearchParameters: _objectSpread(_objectSpread({}, __autocomplete_clickAnalytics ? {
clickAnalytics: true
} : {}), userToken ? {
userToken: normalizeUserToken(userToken)
} : {}),
insights: insights
}
});
}
insightsClient('addAlgoliaAgent', 'insights-plugin');
setInsightsContext();
// Handles user token changes
insightsClient('onUserTokenChange', function (userToken) {
if (!isAuthenticatedToken) {
setInsightsContext(userToken);
}
});
insightsClient('getUserToken', null, function (_error, userToken) {
if (!isAuthenticatedToken) {
setInsightsContext(userToken);
}
});
// Handles authenticated user token changes
insightsClient('onAuthenticatedUserTokenChange', function (authenticatedUserToken) {
if (authenticatedUserToken) {
isAuthenticatedToken = true;
setInsightsContext(authenticatedUserToken);
} else {
isAuthenticatedToken = false;
insightsClient('getUserToken', null, function (_error, userToken) {
return setInsightsContext(userToken);
});
}
});
insightsClient('getAuthenticatedUserToken', null, function (_error, authenticatedUserToken) {
if (authenticatedUserToken) {
isAuthenticatedToken = true;
setInsightsContext(authenticatedUserToken);
}
});
onSelect(function (_ref5) {
var item = _ref5.item,
state = _ref5.state,
event = _ref5.event,
source = _ref5.source;
if (!isAlgoliaInsightsHit(item)) {
return;
}
onSelectEvent({
state: state,
event: event,
insights: insights,
item: item,
insightsEvents: [_objectSpread({
eventName: 'Item Selected'
}, createClickedEvent({
item: item,
items: source.getItems().filter(isAlgoliaInsightsHit)
}))]
});
});
onActive(function (_ref6) {
var item = _ref6.item,
source = _ref6.source,
state = _ref6.state,
event = _ref6.event;
if (!isAlgoliaInsightsHit(item)) {
return;
}
onActiveEvent({
state: state,
event: event,
insights: insights,
item: item,
insightsEvents: [_objectSpread({
eventName: 'Item Active'
}, createClickedEvent({
item: item,
items: source.getItems().filter(isAlgoliaInsightsHit)
}))]
});
});
},
onStateChange: function onStateChange(_ref7) {
var state = _ref7.state;
debouncedOnStateChange({
state: state
});
},
__autocomplete_pluginOptions: options
};
}
function getAlgoliaSources() {
var _context$algoliaInsig;
var algoliaSourceBase = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var context = arguments.length > 1 ? arguments[1] : undefined;
return [].concat(_toConsumableArray(algoliaSourceBase), ['autocomplete-internal'], _toConsumableArray((_context$algoliaInsig = context.algoliaInsightsPlugin) !== null && _context$algoliaInsig !== void 0 && _context$algoliaInsig.__automaticInsights ? ['autocomplete-automatic'] : []));
}
function getOptions(options) {
return _objectSpread({
onItemsChange: function onItemsChange(_ref8) {
var insights = _ref8.insights,
insightsEvents = _ref8.insightsEvents,
state = _ref8.state;
insights.viewedObjectIDs.apply(insights, _toConsumableArray(insightsEvents.map(function (event) {
return _objectSpread(_objectSpread({}, event), {}, {
algoliaSource: getAlgoliaSources(event.algoliaSource, state.context)
});
})));
},
onSelect: function onSelect(_ref9) {
var insights = _ref9.insights,
insightsEvents = _ref9.insightsEvents,
state = _ref9.state;
insights.clickedObjectIDsAfterSearch.apply(insights, _toConsumableArray(insightsEvents.map(function (event) {
return _objectSpread(_objectSpread({}, event), {}, {
algoliaSource: getAlgoliaSources(event.algoliaSource, state.context)
});
})));
},
onActive: noop,
__autocomplete_clickAnalytics: true
}, options);
}
function loadInsights(environment) {
var errorMessage = "[Autocomplete]: Could not load search-insights.js. Please load it manually following https://alg.li/insights-autocomplete";
try {
var script = environment.document.createElement('script');
script.async = true;
script.src = ALGOLIA_INSIGHTS_SRC;
script.onerror = function () {
// eslint-disable-next-line no-console
console.error(errorMessage);
};
document.body.appendChild(script);
} catch (cause) {
// eslint-disable-next-line no-console
console.error(errorMessage);
}
}
/**
* While `search-insights` supports both string and number user tokens,
* the Search API only accepts strings. This function normalizes the user token.
*/
function normalizeUserToken(userToken) {
return typeof userToken === 'number' ? userToken.toString() : userToken;
}

View file

@ -1,9 +0,0 @@
import type { AlgoliaInsightsHit, ClickedObjectIDsAfterSearchParams, InsightsParamsWithItems } from './types';
declare type CreateClickedEventParams = {
item: AlgoliaInsightsHit;
items: AlgoliaInsightsHit[];
};
export declare function createClickedEvent({ item, items, }: CreateClickedEventParams): Omit<InsightsParamsWithItems<ClickedObjectIDsAfterSearchParams>, 'eventName'> & {
algoliaSource?: string[];
};
export {};

View file

@ -1,14 +0,0 @@
export function createClickedEvent(_ref) {
var item = _ref.item,
_ref$items = _ref.items,
items = _ref$items === void 0 ? [] : _ref$items;
return {
index: item.__autocomplete_indexName,
items: [item],
positions: [1 + items.findIndex(function (x) {
return x.objectID === item.objectID;
})],
queryID: item.__autocomplete_queryID,
algoliaSource: ['autocomplete']
};
}

View file

@ -1,66 +0,0 @@
import { ClickedFiltersParams, ClickedObjectIDsAfterSearchParams, ClickedObjectIDsParams, ConvertedFiltersParams, ConvertedObjectIDsAfterSearchParams, ConvertedObjectIDsParams, InsightsClient, WithArbitraryParams, InsightsParamsWithItems, ViewedFiltersParams, ViewedObjectIDsParams } from './types';
export declare function createSearchInsightsApi(searchInsights: InsightsClient): {
/**
* Initializes Insights with Algolia credentials.
*/
init(appId: string, apiKey: string): void;
/**
* Sets the authenticated user token to attach to events.
* Unsets the authenticated token by passing `undefined`.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/set-authenticated-user-token/
*/
setAuthenticatedUserToken(authenticatedUserToken: string | undefined): void;
/**
* Sets the user token to attach to events.
*/
setUserToken(userToken: string): void;
/**
* Sends click events to capture a query and its clicked items and positions.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-object-ids-after-search/
*/
clickedObjectIDsAfterSearch(...params: Array<WithArbitraryParams<InsightsParamsWithItems<ClickedObjectIDsAfterSearchParams>>>): void;
/**
* Sends click events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-object-ids/
*/
clickedObjectIDs(...params: Array<WithArbitraryParams<InsightsParamsWithItems<ClickedObjectIDsParams>>>): void;
/**
* Sends click events to capture the filters a user clicks on.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-filters/
*/
clickedFilters(...params: Array<WithArbitraryParams<ClickedFiltersParams>>): void;
/**
* Sends conversion events to capture a query and its clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-object-ids-after-search/
*/
convertedObjectIDsAfterSearch(...params: Array<WithArbitraryParams<InsightsParamsWithItems<ConvertedObjectIDsAfterSearchParams>>>): void;
/**
* Sends conversion events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-object-ids/
*/
convertedObjectIDs(...params: Array<WithArbitraryParams<InsightsParamsWithItems<ConvertedObjectIDsParams>>>): void;
/**
* Sends conversion events to capture the filters a user uses when converting.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-filters/
*/
convertedFilters(...params: Array<WithArbitraryParams<ConvertedFiltersParams>>): void;
/**
* Sends view events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/viewed-object-ids/
*/
viewedObjectIDs(...params: Array<WithArbitraryParams<InsightsParamsWithItems<ViewedObjectIDsParams>>>): void;
/**
* Sends view events to capture the filters a user uses when viewing.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/viewed-filters/
*/
viewedFilters(...params: Array<WithArbitraryParams<ViewedFiltersParams>>): void;
};

View file

@ -1,206 +0,0 @@
var _excluded = ["items"],
_excluded2 = ["items"];
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { isModernInsightsClient } from './isModernInsightsClient';
function chunk(item) {
var chunkSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
var chunks = [];
for (var i = 0; i < item.objectIDs.length; i += chunkSize) {
chunks.push(_objectSpread(_objectSpread({}, item), {}, {
objectIDs: item.objectIDs.slice(i, i + chunkSize)
}));
}
return chunks;
}
function mapToInsightsParamsApi(params) {
return params.map(function (_ref) {
var items = _ref.items,
param = _objectWithoutProperties(_ref, _excluded);
return _objectSpread(_objectSpread({}, param), {}, {
objectIDs: (items === null || items === void 0 ? void 0 : items.map(function (_ref2) {
var objectID = _ref2.objectID;
return objectID;
})) || param.objectIDs
});
});
}
export function createSearchInsightsApi(searchInsights) {
var canSendHeaders = isModernInsightsClient(searchInsights);
function sendToInsights(method, payloads, items) {
if (canSendHeaders && typeof items !== 'undefined') {
var _items$0$__autocomple = items[0].__autocomplete_algoliaCredentials,
appId = _items$0$__autocomple.appId,
apiKey = _items$0$__autocomple.apiKey;
var headers = {
'X-Algolia-Application-Id': appId,
'X-Algolia-API-Key': apiKey
};
searchInsights.apply(void 0, [method].concat(_toConsumableArray(payloads), [{
headers: headers
}]));
} else {
searchInsights.apply(void 0, [method].concat(_toConsumableArray(payloads)));
}
}
return {
/**
* Initializes Insights with Algolia credentials.
*/
init: function init(appId, apiKey) {
searchInsights('init', {
appId: appId,
apiKey: apiKey
});
},
/**
* Sets the authenticated user token to attach to events.
* Unsets the authenticated token by passing `undefined`.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/set-authenticated-user-token/
*/
setAuthenticatedUserToken: function setAuthenticatedUserToken(authenticatedUserToken) {
searchInsights('setAuthenticatedUserToken', authenticatedUserToken);
},
/**
* Sets the user token to attach to events.
*/
setUserToken: function setUserToken(userToken) {
searchInsights('setUserToken', userToken);
},
/**
* Sends click events to capture a query and its clicked items and positions.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-object-ids-after-search/
*/
clickedObjectIDsAfterSearch: function clickedObjectIDsAfterSearch() {
for (var _len = arguments.length, params = new Array(_len), _key = 0; _key < _len; _key++) {
params[_key] = arguments[_key];
}
if (params.length > 0) {
sendToInsights('clickedObjectIDsAfterSearch', mapToInsightsParamsApi(params), params[0].items);
}
},
/**
* Sends click events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-object-ids/
*/
clickedObjectIDs: function clickedObjectIDs() {
for (var _len2 = arguments.length, params = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
params[_key2] = arguments[_key2];
}
if (params.length > 0) {
sendToInsights('clickedObjectIDs', mapToInsightsParamsApi(params), params[0].items);
}
},
/**
* Sends click events to capture the filters a user clicks on.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/clicked-filters/
*/
clickedFilters: function clickedFilters() {
for (var _len3 = arguments.length, params = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
params[_key3] = arguments[_key3];
}
if (params.length > 0) {
searchInsights.apply(void 0, ['clickedFilters'].concat(params));
}
},
/**
* Sends conversion events to capture a query and its clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-object-ids-after-search/
*/
convertedObjectIDsAfterSearch: function convertedObjectIDsAfterSearch() {
for (var _len4 = arguments.length, params = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
params[_key4] = arguments[_key4];
}
if (params.length > 0) {
sendToInsights('convertedObjectIDsAfterSearch', mapToInsightsParamsApi(params), params[0].items);
}
},
/**
* Sends conversion events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-object-ids/
*/
convertedObjectIDs: function convertedObjectIDs() {
for (var _len5 = arguments.length, params = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
params[_key5] = arguments[_key5];
}
if (params.length > 0) {
sendToInsights('convertedObjectIDs', mapToInsightsParamsApi(params), params[0].items);
}
},
/**
* Sends conversion events to capture the filters a user uses when converting.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/converted-filters/
*/
convertedFilters: function convertedFilters() {
for (var _len6 = arguments.length, params = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
params[_key6] = arguments[_key6];
}
if (params.length > 0) {
searchInsights.apply(void 0, ['convertedFilters'].concat(params));
}
},
/**
* Sends view events to capture clicked items.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/viewed-object-ids/
*/
viewedObjectIDs: function viewedObjectIDs() {
for (var _len7 = arguments.length, params = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
params[_key7] = arguments[_key7];
}
if (params.length > 0) {
params.reduce(function (acc, _ref3) {
var items = _ref3.items,
param = _objectWithoutProperties(_ref3, _excluded2);
return [].concat(_toConsumableArray(acc), _toConsumableArray(chunk(_objectSpread(_objectSpread({}, param), {}, {
objectIDs: (items === null || items === void 0 ? void 0 : items.map(function (_ref4) {
var objectID = _ref4.objectID;
return objectID;
})) || param.objectIDs
})).map(function (payload) {
return {
items: items,
payload: payload
};
})));
}, []).forEach(function (_ref5) {
var items = _ref5.items,
payload = _ref5.payload;
return sendToInsights('viewedObjectIDs', [payload], items);
});
}
},
/**
* Sends view events to capture the filters a user uses when viewing.
*
* @link https://www.algolia.com/doc/api-reference/api-methods/viewed-filters/
*/
viewedFilters: function viewedFilters() {
for (var _len8 = arguments.length, params = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
params[_key8] = arguments[_key8];
}
if (params.length > 0) {
searchInsights.apply(void 0, ['viewedFilters'].concat(params));
}
}
};
}

View file

@ -1,6 +0,0 @@
import { AlgoliaInsightsHit, InsightsParamsWithItems, ViewedObjectIDsParams } from './types';
declare type CreateViewedEventsParams = {
items: AlgoliaInsightsHit[];
};
export declare function createViewedEvents({ items, }: CreateViewedEventsParams): Array<Omit<InsightsParamsWithItems<ViewedObjectIDsParams>, 'eventName'>>;
export {};

View file

@ -1,16 +0,0 @@
export function createViewedEvents(_ref) {
var items = _ref.items;
var itemsByIndexName = items.reduce(function (acc, current) {
var _acc$current$__autoco;
acc[current.__autocomplete_indexName] = ((_acc$current$__autoco = acc[current.__autocomplete_indexName]) !== null && _acc$current$__autoco !== void 0 ? _acc$current$__autoco : []).concat(current);
return acc;
}, {});
return Object.keys(itemsByIndexName).map(function (indexName) {
var items = itemsByIndexName[indexName];
return {
index: indexName,
items: items,
algoliaSource: ['autocomplete']
};
});
}

View file

@ -1 +0,0 @@
export {};

View file

@ -1,2 +0,0 @@
export * from './types';
export * from './createAlgoliaInsightsPlugin';

View file

@ -1,2 +0,0 @@
export * from './types';
export * from './createAlgoliaInsightsPlugin';

View file

@ -1,2 +0,0 @@
import { AlgoliaInsightsHit } from './types';
export declare function isAlgoliaInsightsHit(hit: any): hit is AlgoliaInsightsHit;

View file

@ -1,3 +0,0 @@
export function isAlgoliaInsightsHit(hit) {
return hit.objectID && hit.__autocomplete_indexName && hit.__autocomplete_queryID;
}

View file

@ -1,6 +0,0 @@
import type { InsightsClient } from './types';
/**
* Determines if a given insights `client` supports the optional call to `init`
* and the ability to set credentials via extra parameters when sending events.
*/
export declare function isModernInsightsClient(client: InsightsClient): boolean;

View file

@ -1,23 +0,0 @@
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
/**
* Determines if a given insights `client` supports the optional call to `init`
* and the ability to set credentials via extra parameters when sending events.
*/
export function isModernInsightsClient(client) {
var _split$map = (client.version || '').split('.').map(Number),
_split$map2 = _slicedToArray(_split$map, 2),
major = _split$map2[0],
minor = _split$map2[1];
/* eslint-disable @typescript-eslint/camelcase */
var v3 = major >= 3;
var v2_4 = major === 2 && minor >= 4;
var v1_10 = major === 1 && minor >= 10;
return v3 || v2_4 || v1_10;
/* eslint-enable @typescript-eslint/camelcase */
}

View file

@ -1,9 +0,0 @@
export declare type AlgoliaInsightsHit = {
objectID: string;
__autocomplete_indexName: string;
__autocomplete_queryID: string;
__autocomplete_algoliaCredentials: {
appId: string;
apiKey: string;
};
};

View file

@ -1,64 +0,0 @@
import { createSearchInsightsApi } from '../createSearchInsightsApi';
import type { AlgoliaInsightsHit } from './AlgoliaInsightsHit';
export declare type AutocompleteInsightsApi = ReturnType<typeof createSearchInsightsApi>;
export declare type WithArbitraryParams<TParams extends Record<string, unknown>> = Record<string, unknown> & TParams;
export declare type InsightsParamsWithItems<TParams extends {
objectIDs: string[];
}> = Omit<TParams, 'objectIDs'> & {
items: AlgoliaInsightsHit[];
/**
* @deprecated use `items` instead
*/
objectIDs?: string[];
};
export declare type ClickedObjectIDsAfterSearchParams = {
eventName: string;
index: string;
objectIDs: string[];
positions: number[];
queryID: string;
userToken?: string;
};
export declare type ClickedObjectIDsParams = {
eventName: string;
index: string;
objectIDs: string[];
userToken?: string;
};
export declare type ClickedFiltersParams = {
eventName: string;
filters: string[];
index: string;
userToken: string;
};
export declare type ConvertedObjectIDsAfterSearchParams = {
eventName: string;
index: string;
objectIDs: string[];
queryID: string;
userToken?: string;
};
export declare type ConvertedObjectIDsParams = {
eventName: string;
index: string;
objectIDs: string[];
userToken: string;
};
export declare type ConvertedFiltersParams = {
eventName: string;
filters: string[];
index: string;
userToken: string;
};
export declare type ViewedObjectIDsParams = {
eventName: string;
index: string;
objectIDs: string[];
userToken?: string;
};
export declare type ViewedFiltersParams = {
eventName: string;
filters: string[];
index: string;
userToken: string;
};

View file

@ -1,20 +0,0 @@
import type { AutocompleteState } from '@algolia/autocomplete-shared';
import type { ClickedObjectIDsAfterSearchParams, InsightsParamsWithItems, ViewedObjectIDsParams } from './AutocompleteInsightsApi';
import type { AlgoliaInsightsHit, AutocompleteInsightsApi } from '.';
export declare type OnSelectParams = {
insights: AutocompleteInsightsApi;
insightsEvents: Array<InsightsParamsWithItems<ClickedObjectIDsAfterSearchParams & {
algoliaSource?: string[];
}>>;
item: AlgoliaInsightsHit;
state: AutocompleteState<any>;
event: any;
};
export declare type OnActiveParams = OnSelectParams;
export declare type OnItemsChangeParams = {
insights: AutocompleteInsightsApi;
insightsEvents: Array<InsightsParamsWithItems<ViewedObjectIDsParams & {
algoliaSource?: string[];
}>>;
state: AutocompleteState<any>;
};

Some files were not shown because too many files have changed in this diff Show more