@quenty/pagesutils 5.20.0 → 5.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/deploy.nevermore.json +10 -0
- package/package.json +5 -3
- package/src/Shared/Proxy/PagesDatabase.lua +49 -4
- package/src/Shared/Proxy/PagesDatabase.spec.lua +106 -0
- package/src/jest.config.lua +3 -0
- package/test/default.project.json +17 -0
- package/test/scripts/Server/ServerMain.server.lua +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.22.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/pagesutils@5.21.0...@quenty/pagesutils@5.22.0) (2026-07-23)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- Add baseline player-mock and support across Nevermore for mocked players. ([567d121](https://github.com/Quenty/NevermoreEngine/commit/567d121ffc014b42391554088189a1a6296dda83))
|
|
11
|
+
|
|
12
|
+
# [5.21.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/pagesutils@5.20.0...@quenty/pagesutils@5.21.0) (2026-07-18)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package @quenty/pagesutils
|
|
15
|
+
|
|
6
16
|
# [5.20.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/pagesutils@5.19.1...@quenty/pagesutils@5.20.0) (2026-07-18)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @quenty/pagesutils
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/pagesutils",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.22.0",
|
|
4
4
|
"description": "Utilities to advance over the Roblox pages API surface",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -31,10 +31,12 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@quenty/ducktype": "5.11.0",
|
|
33
33
|
"@quenty/loader": "10.11.0",
|
|
34
|
-
"@quenty/
|
|
34
|
+
"@quenty/nevermore-test-runner": "1.5.0",
|
|
35
|
+
"@quenty/promise": "10.21.0",
|
|
36
|
+
"@quentystudios/jest-lua": "3.10.0-quenty.2"
|
|
35
37
|
},
|
|
36
38
|
"publishConfig": {
|
|
37
39
|
"access": "public"
|
|
38
40
|
},
|
|
39
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "1de37218a2bedb8e3f8614a2e09bba9eddc812da"
|
|
40
42
|
}
|
|
@@ -13,7 +13,8 @@ PagesDatabase.__index = PagesDatabase
|
|
|
13
13
|
|
|
14
14
|
export type PagesDatabase = typeof(setmetatable(
|
|
15
15
|
{} :: {
|
|
16
|
-
|
|
16
|
+
-- nil for a static database (see [PagesDatabase.fromPageData]), which has every page pre-stored
|
|
17
|
+
_pages: Pages?,
|
|
17
18
|
_lastIncrementedIndex: number,
|
|
18
19
|
_pageData: { [number]: { currentPage: any, isFinished: boolean } },
|
|
19
20
|
},
|
|
@@ -32,14 +33,56 @@ function PagesDatabase.new(pages: Pages): PagesDatabase
|
|
|
32
33
|
return self
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
--[=[
|
|
37
|
+
Constructs a database from static page data instead of a live [Pages] instance, for consumers
|
|
38
|
+
that already hold the full result set -- e.g. a test fabricating an engine pages result through
|
|
39
|
+
[PagesProxy]. Each entry in `pageData` is one page's item array; the last page reads back
|
|
40
|
+
`IsFinished = true`. Empty `pageData` mirrors an empty engine result: a single empty page that
|
|
41
|
+
is already finished.
|
|
42
|
+
|
|
43
|
+
```lua
|
|
44
|
+
local pages = PagesProxy.new(PagesDatabase.fromPageData({
|
|
45
|
+
{ "a", "b" },
|
|
46
|
+
{ "c" },
|
|
47
|
+
}))
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
@param pageData { { any } }
|
|
51
|
+
@return PagesDatabase
|
|
52
|
+
]=]
|
|
53
|
+
function PagesDatabase.fromPageData(pageData: { { any } }): PagesDatabase
|
|
54
|
+
assert(type(pageData) == "table", "Bad pageData")
|
|
55
|
+
|
|
56
|
+
local self: PagesDatabase = setmetatable({} :: any, PagesDatabase)
|
|
57
|
+
|
|
58
|
+
local pageCount = math.max(#pageData, 1)
|
|
59
|
+
|
|
60
|
+
self._lastIncrementedIndex = pageCount
|
|
61
|
+
self._pageData = {}
|
|
62
|
+
|
|
63
|
+
for pageId = 1, pageCount do
|
|
64
|
+
local page = pageData[pageId] or {}
|
|
65
|
+
assert(type(page) == "table", "Bad page")
|
|
66
|
+
|
|
67
|
+
self._pageData[pageId] = {
|
|
68
|
+
currentPage = page,
|
|
69
|
+
isFinished = pageId == pageCount,
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
return self
|
|
74
|
+
end
|
|
75
|
+
|
|
35
76
|
function PagesDatabase.isPagesDatabase(value): boolean
|
|
36
77
|
return DuckTypeUtils.isImplementation(PagesDatabase, value)
|
|
37
78
|
end
|
|
38
79
|
|
|
39
80
|
function PagesDatabase.IncrementToPageIdAsync(self: PagesDatabase, pageId: number)
|
|
40
81
|
while self._lastIncrementedIndex < pageId do
|
|
82
|
+
local pages = assert(self._pages, "Cannot advance a static PagesDatabase past its stored pages")
|
|
83
|
+
|
|
41
84
|
self._lastIncrementedIndex += 1
|
|
42
|
-
|
|
85
|
+
pages:AdvanceToNextPageAsync()
|
|
43
86
|
self:_storeState()
|
|
44
87
|
end
|
|
45
88
|
end
|
|
@@ -63,9 +106,11 @@ function PagesDatabase._getPageState(self: PagesDatabase, pageId: number)
|
|
|
63
106
|
end
|
|
64
107
|
|
|
65
108
|
function PagesDatabase._storeState(self: PagesDatabase)
|
|
109
|
+
local pages = assert(self._pages, "No pages")
|
|
110
|
+
|
|
66
111
|
self._pageData[self._lastIncrementedIndex] = {
|
|
67
|
-
currentPage =
|
|
68
|
-
isFinished =
|
|
112
|
+
currentPage = pages:GetCurrentPage(),
|
|
113
|
+
isFinished = pages.IsFinished,
|
|
69
114
|
}
|
|
70
115
|
end
|
|
71
116
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class PagesDatabase.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local PagesDatabase = require("PagesDatabase")
|
|
10
|
+
local PagesProxy = require("PagesProxy")
|
|
11
|
+
local PagesUtils = require("PagesUtils")
|
|
12
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
13
|
+
|
|
14
|
+
local describe = Jest.Globals.describe
|
|
15
|
+
local expect = Jest.Globals.expect
|
|
16
|
+
local it = Jest.Globals.it
|
|
17
|
+
|
|
18
|
+
describe("PagesDatabase.fromPageData", function()
|
|
19
|
+
it("should store each page with the last one finished", function()
|
|
20
|
+
local database = PagesDatabase.fromPageData({
|
|
21
|
+
{ "a", "b" },
|
|
22
|
+
{ "c" },
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(PagesDatabase.isPagesDatabase(database)).toBe(true)
|
|
26
|
+
expect(database:GetPage(1)).toEqual({ "a", "b" })
|
|
27
|
+
expect(database:GetIsFinished(1)).toBe(false)
|
|
28
|
+
expect(database:GetPage(2)).toEqual({ "c" })
|
|
29
|
+
expect(database:GetIsFinished(2)).toBe(true)
|
|
30
|
+
end)
|
|
31
|
+
|
|
32
|
+
it("should mirror an empty engine result as one finished empty page", function()
|
|
33
|
+
local database = PagesDatabase.fromPageData({})
|
|
34
|
+
|
|
35
|
+
expect(database:GetPage(1)).toEqual({})
|
|
36
|
+
expect(database:GetIsFinished(1)).toBe(true)
|
|
37
|
+
end)
|
|
38
|
+
|
|
39
|
+
it("should no-op advancement to already-stored pages", function()
|
|
40
|
+
local database = PagesDatabase.fromPageData({ { "a" }, { "b" } })
|
|
41
|
+
|
|
42
|
+
database:IncrementToPageIdAsync(2)
|
|
43
|
+
|
|
44
|
+
expect(database:GetPage(2)).toEqual({ "b" })
|
|
45
|
+
end)
|
|
46
|
+
|
|
47
|
+
it("should error on advancement past the stored pages", function()
|
|
48
|
+
local database = PagesDatabase.fromPageData({ { "a" } })
|
|
49
|
+
|
|
50
|
+
expect(function()
|
|
51
|
+
database:IncrementToPageIdAsync(2)
|
|
52
|
+
end).toThrow()
|
|
53
|
+
end)
|
|
54
|
+
end)
|
|
55
|
+
|
|
56
|
+
describe("PagesProxy over a static database", function()
|
|
57
|
+
it("should duck-type as a pages instance and iterate every page", function()
|
|
58
|
+
local proxy = PagesProxy.new(PagesDatabase.fromPageData({
|
|
59
|
+
{ "a", "b" },
|
|
60
|
+
{ "c" },
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
expect(PagesProxy.isPagesProxy(proxy)).toBe(true)
|
|
64
|
+
expect(proxy:GetCurrentPage()).toEqual({ "a", "b" })
|
|
65
|
+
expect((proxy :: any).IsFinished).toBe(false)
|
|
66
|
+
|
|
67
|
+
proxy:AdvanceToNextPageAsync()
|
|
68
|
+
|
|
69
|
+
expect(proxy:GetCurrentPage()).toEqual({ "c" })
|
|
70
|
+
expect((proxy :: any).IsFinished).toBe(true)
|
|
71
|
+
expect(function()
|
|
72
|
+
proxy:AdvanceToNextPageAsync()
|
|
73
|
+
end).toThrow()
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
it("should clone without sharing advancement state", function()
|
|
77
|
+
local proxy = PagesProxy.new(PagesDatabase.fromPageData({ { "a" }, { "b" } }))
|
|
78
|
+
local clone = proxy:Clone()
|
|
79
|
+
|
|
80
|
+
clone:AdvanceToNextPageAsync()
|
|
81
|
+
|
|
82
|
+
expect(proxy:GetCurrentPage()).toEqual({ "a" })
|
|
83
|
+
expect(clone:GetCurrentPage()).toEqual({ "b" })
|
|
84
|
+
end)
|
|
85
|
+
end)
|
|
86
|
+
|
|
87
|
+
describe("PagesUtils.promiseAdvanceToNextPage", function()
|
|
88
|
+
it("should resolve the next page's content for a proxy", function()
|
|
89
|
+
local proxy = PagesProxy.new(PagesDatabase.fromPageData({ { "a" }, { "b" } }))
|
|
90
|
+
|
|
91
|
+
local promise = PagesUtils.promiseAdvanceToNextPage(proxy :: any)
|
|
92
|
+
local outcome, value = PromiseTestUtils.awaitOutcome(promise)
|
|
93
|
+
|
|
94
|
+
expect(outcome).toBe("resolved")
|
|
95
|
+
expect(value).toEqual({ "b" })
|
|
96
|
+
end)
|
|
97
|
+
|
|
98
|
+
it("should reject when the proxy is already finished", function()
|
|
99
|
+
local proxy = PagesProxy.new(PagesDatabase.fromPageData({ { "a" } }))
|
|
100
|
+
|
|
101
|
+
local promise = PagesUtils.promiseAdvanceToNextPage(proxy :: any)
|
|
102
|
+
local outcome = PromiseTestUtils.awaitOutcome(promise)
|
|
103
|
+
|
|
104
|
+
expect(outcome).toBe("rejected")
|
|
105
|
+
end)
|
|
106
|
+
end)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "PagesUtilsTest",
|
|
3
|
+
"tree": {
|
|
4
|
+
"$className": "DataModel",
|
|
5
|
+
"ServerScriptService": {
|
|
6
|
+
"$properties": {
|
|
7
|
+
"LoadStringEnabled": true
|
|
8
|
+
},
|
|
9
|
+
"pagesutils": {
|
|
10
|
+
"$path": ".."
|
|
11
|
+
},
|
|
12
|
+
"Script": {
|
|
13
|
+
"$path": "scripts/Server"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
--!nonstrict
|
|
2
|
+
local ServerScriptService = game:GetService("ServerScriptService")
|
|
3
|
+
|
|
4
|
+
local root = ServerScriptService.pagesutils
|
|
5
|
+
local loader = root:FindFirstChild("LoaderUtils", true).Parent
|
|
6
|
+
local require = require(loader).bootstrapGame(root)
|
|
7
|
+
|
|
8
|
+
local NevermoreTestRunnerUtils = require("NevermoreTestRunnerUtils")
|
|
9
|
+
|
|
10
|
+
if NevermoreTestRunnerUtils.runTestsIfNeededAsync(root) then
|
|
11
|
+
return
|
|
12
|
+
end
|