@nixxie-cms/core 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +2 -2
  2. package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.cjs.js +4 -4
  3. package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.esm.js +4 -4
  4. package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.cjs.js +2 -2
  5. package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.esm.js +2 -2
  6. package/context/dist/nixxie-cms-core-context.cjs.js +2 -2
  7. package/context/dist/nixxie-cms-core-context.esm.js +2 -2
  8. package/dist/{CreateItemDialog-33335548.esm.js → CreateItemDialog-7008b050.esm.js} +1 -1
  9. package/dist/{CreateItemDialog-56cf59b7.cjs.js → CreateItemDialog-a0cab315.cjs.js} +1 -1
  10. package/dist/{PageContainer-7db73317.esm.js → PageContainer-5ae731cc.esm.js} +25 -18
  11. package/dist/{PageContainer-27c27f10.cjs.js → PageContainer-abd7159f.cjs.js} +25 -18
  12. package/dist/{admin-meta-graphql-6f7f5331.esm.js → admin-meta-graphql-0e6e606e.esm.js} +1 -1
  13. package/dist/{admin-meta-graphql-c8f926e9.cjs.js → admin-meta-graphql-306c224a.cjs.js} +1 -1
  14. package/dist/{context-3132c3ed.esm.js → context-af9957ed.esm.js} +2 -2
  15. package/dist/{context-e7a45152.cjs.js → context-b5204629.cjs.js} +2 -2
  16. package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
  17. package/dist/declarations/src/admin-ui/components/PageContainer.d.ts.map +1 -1
  18. package/dist/declarations/src/helpers.d.ts.map +1 -1
  19. package/dist/declarations/src/index.d.ts +1 -0
  20. package/dist/declarations/src/index.d.ts.map +1 -1
  21. package/dist/declarations/src/internal-unstable/admin-ui/id-field-view.d.ts.map +1 -0
  22. package/dist/declarations/src/internal-unstable/admin-ui/pages/App/index.d.ts.map +1 -0
  23. package/dist/declarations/src/internal-unstable/admin-ui/pages/CreateItemPage/index.d.ts.map +1 -0
  24. package/dist/declarations/src/internal-unstable/admin-ui/pages/HomePage/index.d.ts.map +1 -0
  25. package/dist/declarations/src/internal-unstable/admin-ui/pages/ItemPage/index.d.ts.map +1 -0
  26. package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -0
  27. package/dist/declarations/src/internal-unstable/admin-ui/pages/NoAccessPage/index.d.ts.map +1 -0
  28. package/dist/declarations/src/internal-unstable/artifacts.d.ts.map +1 -0
  29. package/dist/declarations/src/lib/core/initialise-lists.d.ts +1 -1
  30. package/dist/declarations/src/schema.d.ts.map +1 -1
  31. package/dist/declarations/src/types/config/index.d.ts +60 -1
  32. package/dist/declarations/src/types/config/index.d.ts.map +1 -1
  33. package/dist/declarations/src/types/config/lists.d.ts +4 -4
  34. package/dist/declarations/src/types/context.d.ts +150 -0
  35. package/dist/declarations/src/types/context.d.ts.map +1 -1
  36. package/dist/declarations/src/types/next-fields.d.ts +1 -1
  37. package/dist/{express-e9ed9a7d.cjs.js → express-455ae20c.cjs.js} +1 -1
  38. package/dist/{express-6743b918.esm.js → express-7559ca2d.esm.js} +1 -1
  39. package/dist/{index-ac01583b.cjs.js → index-89635494.cjs.js} +4 -4
  40. package/dist/{index-24b78415.esm.js → index-baa799e0.esm.js} +4 -4
  41. package/dist/nixxie-cms-core.cjs.js +104 -77
  42. package/dist/nixxie-cms-core.esm.js +104 -77
  43. package/dist/{non-null-graphql-5315718c.esm.js → non-null-graphql-a84ed64d.esm.js} +1 -1
  44. package/dist/{non-null-graphql-17b83ddc.cjs.js → non-null-graphql-add6bb3d.cjs.js} +1 -1
  45. package/dist/{resolve-hooks-66fe8a8e.cjs.js → resolve-hooks-165a9ce2.cjs.js} +1 -1
  46. package/dist/{resolve-hooks-17aafd37.esm.js → resolve-hooks-6813a045.esm.js} +2 -2
  47. package/dist/{system-dfec2f0a.esm.js → system-03e49e4f.esm.js} +8 -4
  48. package/dist/{system-48c5f6df.cjs.js → system-a321642d.cjs.js} +8 -4
  49. package/dist/{useFilter-0b5a1ee6.esm.js → useFilter-9b6db1f9.esm.js} +1 -1
  50. package/dist/{useFilter-1a4e6900.cjs.js → useFilter-acc9d413.cjs.js} +1 -1
  51. package/fields/dist/nixxie-cms-core-fields.cjs.js +16 -16
  52. package/fields/dist/nixxie-cms-core-fields.esm.js +17 -17
  53. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +3 -3
  54. package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +3 -3
  55. package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.cjs.js +1 -1
  56. package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.esm.js +1 -1
  57. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +3 -3
  58. package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +3 -3
  59. package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.cjs.js +4 -4
  60. package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.esm.js +4 -4
  61. package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.cjs.js +1 -1
  62. package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.esm.js +1 -1
  63. package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.cjs.js +1 -1
  64. package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.esm.js +1 -1
  65. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.d.ts +2 -0
  66. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.js +244 -0
  67. package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.esm.js +235 -0
  68. package/internal-unstable/admin-ui/id-field-view/package.json +4 -0
  69. package/internal-unstable/admin-ui/next-config/package.json +4 -0
  70. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.d.ts +2 -0
  71. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.js +59 -0
  72. package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.esm.js +55 -0
  73. package/internal-unstable/admin-ui/pages/App/package.json +4 -0
  74. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.d.ts +2 -0
  75. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.js +116 -0
  76. package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.esm.js +112 -0
  77. package/internal-unstable/admin-ui/pages/CreateItemPage/package.json +4 -0
  78. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.d.ts +2 -0
  79. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.js +336 -0
  80. package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.esm.js +332 -0
  81. package/internal-unstable/admin-ui/pages/HomePage/package.json +4 -0
  82. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.d.ts +2 -0
  83. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.js +463 -0
  84. package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.esm.js +455 -0
  85. package/internal-unstable/admin-ui/pages/ItemPage/package.json +4 -0
  86. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.d.ts +2 -0
  87. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.js +1195 -0
  88. package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.esm.js +1187 -0
  89. package/internal-unstable/admin-ui/pages/ListPage/package.json +4 -0
  90. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.d.ts +2 -0
  91. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.js +40 -0
  92. package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.esm.js +35 -0
  93. package/internal-unstable/admin-ui/pages/NoAccessPage/package.json +4 -0
  94. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.d.ts +2 -0
  95. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +51 -0
  96. package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +38 -0
  97. package/internal-unstable/artifacts/package.json +4 -0
  98. package/package.json +44 -44
  99. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +44 -15
  100. package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +44 -15
  101. package/scripts/dist/nixxie-cms-core-scripts.cjs.js +3 -3
  102. package/scripts/dist/nixxie-cms-core-scripts.esm.js +3 -3
  103. package/src/admin-ui/admin-meta-graphql.ts +168 -168
  104. package/src/admin-ui/components/CommandPalette.tsx +433 -431
  105. package/src/admin-ui/components/Navigation.tsx +389 -385
  106. package/src/admin-ui/components/PageContainer.tsx +311 -310
  107. package/src/admin-ui/components/WelcomeDialog.tsx +1 -1
  108. package/src/admin-ui/context.tsx +338 -338
  109. package/src/admin-ui/templates/app.ts +60 -60
  110. package/src/admin-ui/templates/create-item.ts +5 -5
  111. package/src/admin-ui/templates/home.ts +2 -2
  112. package/src/admin-ui/templates/item.tsx +5 -5
  113. package/src/admin-ui/templates/list.tsx +5 -5
  114. package/src/admin-ui/templates/next-config.ts +29 -0
  115. package/src/admin-ui/templates/no-access.ts +7 -7
  116. package/src/fields/types/bigInt/index.ts +181 -181
  117. package/src/fields/types/bytes/index.ts +275 -275
  118. package/src/fields/types/calendarDay/index.ts +194 -194
  119. package/src/fields/types/checkbox/index.ts +76 -76
  120. package/src/fields/types/decimal/index.ts +182 -182
  121. package/src/fields/types/file/index.ts +168 -168
  122. package/src/fields/types/float/index.ts +133 -133
  123. package/src/fields/types/image/index.ts +244 -244
  124. package/src/fields/types/integer/index.ts +156 -156
  125. package/src/fields/types/json/index.ts +77 -77
  126. package/src/fields/types/multiselect/index.ts +212 -212
  127. package/src/fields/types/password/index.ts +241 -241
  128. package/src/fields/types/relationship/index.ts +381 -381
  129. package/src/fields/types/relationship/views/RelationshipTable.tsx +190 -190
  130. package/src/fields/types/select/index.ts +226 -226
  131. package/src/fields/types/text/index.ts +207 -207
  132. package/src/fields/types/timestamp/index.ts +116 -116
  133. package/src/fields/types/virtual/index.ts +108 -108
  134. package/src/helpers.ts +342 -316
  135. package/src/index.ts +4 -0
  136. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.tsx +167 -167
  137. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.tsx +22 -22
  138. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.tsx +71 -71
  139. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.tsx +333 -333
  140. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/common.tsx +358 -358
  141. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.tsx +483 -483
  142. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/FilterAdd.tsx +221 -221
  143. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/PaginationControls.tsx +170 -170
  144. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/Tag.tsx +72 -72
  145. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.tsx +1006 -1006
  146. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.tsx +24 -24
  147. package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.ts +5 -5
  148. package/src/lib/context/createContext.ts +165 -161
  149. package/src/lib/core/initialise-lists.ts +1097 -1097
  150. package/src/lib/id-field.ts +214 -214
  151. package/src/lib/telemetry.ts +342 -342
  152. package/src/schema.ts +237 -233
  153. package/src/scripts/telemetry.ts +1 -1
  154. package/src/types/config/index.ts +400 -333
  155. package/src/types/config/lists.ts +4 -4
  156. package/src/types/context.ts +700 -530
  157. package/src/types/next-fields.ts +499 -499
  158. package/src/types/telemetry.ts +51 -51
  159. package/tests/telemetry.test.ts +361 -361
  160. package/CHANGELOG.md +0 -3158
  161. package/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/package.json +0 -4
  162. package/___internal-do-not-use-will-break-in-patch/admin-ui/next-config/package.json +0 -4
  163. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/package.json +0 -4
  164. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/package.json +0 -4
  165. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/package.json +0 -4
  166. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/package.json +0 -4
  167. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/package.json +0 -4
  168. package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/package.json +0 -4
  169. package/___internal-do-not-use-will-break-in-patch/artifacts/package.json +0 -4
  170. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.d.ts.map +0 -1
  171. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.d.ts.map +0 -1
  172. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.d.ts.map +0 -1
  173. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.d.ts.map +0 -1
  174. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.d.ts.map +0 -1
  175. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.d.ts.map +0 -1
  176. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/index.d.ts.map +0 -1
  177. package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/artifacts.d.ts.map +0 -1
  178. /package/dist/{common-1a350e11.cjs.js → common-5933f758.cjs.js} +0 -0
  179. /package/dist/{common-29fc82e6.esm.js → common-ea5c441a.esm.js} +0 -0
  180. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.d.ts +0 -0
  181. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.d.ts +0 -0
  182. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.d.ts +0 -0
  183. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.d.ts +0 -0
  184. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.d.ts +0 -0
  185. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.d.ts +0 -0
  186. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.d.ts +0 -0
  187. /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.d.ts +0 -0
@@ -1,361 +1,361 @@
1
- import https from 'node:https'
2
- import Conf from 'conf'
3
-
4
- import path from 'path'
5
- import type { InitialisedList } from '../src/lib/core/initialise-lists'
6
- import { runTelemetry, disableTelemetry } from '../src/lib/telemetry'
7
-
8
- const mockProjectRoot = path.resolve(__dirname, '..', '..', '..')
9
- const mockProjectDir = path.join(mockProjectRoot, './tests/test-projects/basic')
10
- const mockPackageVersions = {
11
- '@nixxie-cms/core': '14.1.0',
12
- '@nixxie-cms/auth': '9.0.1',
13
- '@nixxie-cms/fields-document': '18.0.2',
14
- '@nixxie-cms/cloudinary': '0.0.1',
15
- }
16
-
17
- jest.mock(
18
- '@nixxie-cms/core/package.json',
19
- () => {
20
- return { version: mockPackageVersions['@nixxie-cms/core'] }
21
- },
22
- { virtual: true }
23
- )
24
- jest.mock(
25
- '@nixxie-cms/auth/package.json',
26
- () => {
27
- return { version: mockPackageVersions['@nixxie-cms/auth'] }
28
- },
29
- { virtual: true }
30
- )
31
- jest.mock(
32
- '@nixxie-cms/fields-document/package.json',
33
- () => {
34
- return { version: mockPackageVersions['@nixxie-cms/fields-document'] }
35
- },
36
- { virtual: true }
37
- )
38
- jest.mock(
39
- '@nixxie-cms/cloudinary/package.json',
40
- () => {
41
- return { version: mockPackageVersions['@nixxie-cms/cloudinary'] }
42
- },
43
- { virtual: true }
44
- )
45
-
46
- let mockTelemetryConfig: any = undefined
47
-
48
- jest.mock('conf', () => {
49
- const getMockTelemetryConfig = jest.fn(() => {
50
- if (mockTelemetryConfig === 'THROW') throw new Error('JSON.parse error')
51
- return mockTelemetryConfig
52
- })
53
-
54
- return function Conf() {
55
- return {
56
- get: getMockTelemetryConfig,
57
- set: (key: string, newState: any) => {
58
- if (key !== 'telemetry') throw new Error(`Unexpected conf key ${key}`)
59
- mockTelemetryConfig = newState
60
- },
61
- delete: () => {
62
- mockTelemetryConfig = undefined
63
- },
64
- }
65
- }
66
- })
67
-
68
- jest.mock('node:https', () => {
69
- const once = jest.fn()
70
- const end = jest.fn()
71
- const request = jest.fn().mockImplementation((_, __, f) => {
72
- setTimeout(() => f(), 100)
73
- return { once, end }
74
- })
75
- // added for reach by toHaveBeenCalledWith
76
- ;(request as any).once = once
77
- ;(request as any).end = end
78
- return { request }
79
- })
80
-
81
- jest.mock('node:os', () => {
82
- return {
83
- ...jest.requireActual('node:os'),
84
- platform: () => 'nixxie-os',
85
- }
86
- })
87
-
88
- // required as CI is set for tests
89
- jest.mock('ci-info', () => {
90
- return { isCI: false }
91
- })
92
-
93
- const lists: Record<string, InitialisedList> = {
94
- Thing: {
95
- fields: {
96
- // @ts-expect-error
97
- id: {
98
- __ksTelemetryFieldTypeName: 'id',
99
- },
100
- // @ts-expect-error
101
- name: {
102
- __ksTelemetryFieldTypeName: 'id',
103
- },
104
- // @ts-expect-error
105
- thing: {
106
- __ksTelemetryFieldTypeName: 'id',
107
- },
108
- },
109
- },
110
- Stuff: {
111
- fields: {
112
- // @ts-expect-error
113
- id: {
114
- __ksTelemetryFieldTypeName: 'id',
115
- },
116
- // @ts-expect-error
117
- name: {
118
- __ksTelemetryFieldTypeName: 'id',
119
- },
120
- },
121
- },
122
- }
123
-
124
- describe('Telemetry tests', () => {
125
- beforeEach(() => {
126
- jest.clearAllMocks()
127
- mockTelemetryConfig = undefined // reset state
128
- })
129
-
130
- const today = new Date().toJSON().slice(0, 10)
131
- const mockYesterday = '2023-01-01'
132
- const mockTelemetryConfigInitialised = {
133
- informedAt: `${mockYesterday}T01:11:11.111Z`,
134
- device: { lastSentDate: mockYesterday },
135
- projects: {
136
- [mockProjectDir]: {
137
- lastSentDate: mockYesterday,
138
- },
139
- },
140
- }
141
-
142
- function expectDidSend(lastSentDate: string | null) {
143
- expect(https.request).toHaveBeenCalledWith(
144
- `https://telemetry.nixxiecms.com/3/project`,
145
- {
146
- method: 'POST',
147
- headers: {
148
- 'Content-Type': 'application/json',
149
- },
150
- },
151
- expect.any(Function)
152
- )
153
- expect((https.request as any).end).toHaveBeenCalledWith(
154
- JSON.stringify({
155
- lastSentDate,
156
- packages: mockPackageVersions,
157
- database: 'sqlite',
158
- lists: 2,
159
- fields: {
160
- unknown: 0,
161
- id: 5,
162
- },
163
- })
164
- )
165
-
166
- expect(https.request).toHaveBeenCalledWith(
167
- `https://telemetry.nixxiecms.com/3/device`,
168
- {
169
- method: 'POST',
170
- headers: {
171
- 'Content-Type': 'application/json',
172
- },
173
- },
174
- expect.any(Function)
175
- )
176
- expect((https.request as any).end).toHaveBeenCalledWith(
177
- JSON.stringify({
178
- lastSentDate,
179
- os: 'nixxie-os',
180
- node: process.versions.node.split('.')[0],
181
- })
182
- )
183
- }
184
-
185
- test('Telemetry writes out an empty configuration, and sends nothing on first run', async () => {
186
- await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
187
-
188
- expect(new Conf().get).toHaveBeenCalledTimes(1)
189
- expect(https.request).toHaveBeenCalledTimes(0)
190
- expect(mockTelemetryConfig).toStrictEqual({
191
- informedAt: expect.stringMatching(new RegExp(`^${today}`)),
192
- device: { lastSentDate: null },
193
- projects: {},
194
- })
195
- })
196
-
197
- test('Telemetry is sent after inform', async () => {
198
- await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
199
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send
200
-
201
- expectDidSend(null)
202
- expect(new Conf().get).toHaveBeenCalledTimes(2)
203
- expect(https.request).toHaveBeenCalledTimes(2) // would be 4 if sent twice
204
- expect(mockTelemetryConfig).toStrictEqual({
205
- informedAt: expect.stringMatching(new RegExp(`^${today}`)),
206
- device: { lastSentDate: today },
207
- projects: {
208
- [mockProjectDir]: { lastSentDate: today },
209
- },
210
- })
211
- })
212
-
213
- test('Telemetry is not sent twice in one day', async () => {
214
- await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
215
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send
216
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send, same day
217
-
218
- expectDidSend(null)
219
- expect(new Conf().get).toHaveBeenCalledTimes(3)
220
- expect(https.request).toHaveBeenCalledTimes(2) // would be 4 if sent twice
221
- })
222
-
223
- test('Telemetry sends a lastSentDate on the next run, a different day', async () => {
224
- mockTelemetryConfig = mockTelemetryConfigInitialised
225
-
226
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send, different day
227
-
228
- expectDidSend(mockYesterday)
229
- expect(new Conf().get).toHaveBeenCalledTimes(1)
230
- expect(https.request).toHaveBeenCalledTimes(2)
231
- expect(mockTelemetryConfig).toStrictEqual({
232
- informedAt: expect.stringMatching(new RegExp(`^${mockYesterday}`)),
233
- device: { lastSentDate: today },
234
- projects: {
235
- [mockProjectDir]: { lastSentDate: today },
236
- },
237
- })
238
- })
239
-
240
- test(`Telemetry is reset when using "nixxie telemetry disable"`, () => {
241
- disableTelemetry()
242
-
243
- expect(mockTelemetryConfig).toBe(false)
244
- })
245
-
246
- test(`Telemetry is not sent if telemetry configuration is disabled`, async () => {
247
- mockTelemetryConfig = false
248
-
249
- await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
250
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send
251
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send, same day
252
-
253
- expect(new Conf().get).toHaveBeenCalledTimes(3)
254
- expect(https.request).toHaveBeenCalledTimes(0)
255
- expect(mockTelemetryConfig).toBe(false)
256
- })
257
-
258
- test(`Telemetry is unchanged if configuration is malformed`, async () => {
259
- mockTelemetryConfig = 'THROW'
260
-
261
- await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
262
- await runTelemetry(mockProjectDir, lists, 'sqlite') // send
263
-
264
- expect(new Conf().get).toHaveBeenCalledTimes(2)
265
- expect(https.request).toHaveBeenCalledTimes(0)
266
- expect(mockTelemetryConfig).toStrictEqual('THROW') // nothing changes
267
- })
268
-
269
- // easy opt-out tests
270
- for (const [key, value] of Object.entries({
271
- NODE_ENV: 'production',
272
- NIXXIE_TELEMETRY_DISABLED: '1',
273
- })) {
274
- describe(`when process.env.${key} is set to ${value}`, () => {
275
- const envBefore = process.env[key]
276
-
277
- beforeEach(() => {
278
- process.env[key] = value
279
- })
280
-
281
- afterEach(() => {
282
- process.env[key] = envBefore
283
- })
284
-
285
- test(`when telemetry initialised, we do nothing`, async () => {
286
- mockTelemetryConfig = mockTelemetryConfigInitialised
287
-
288
- await runTelemetry(mockProjectDir, lists, 'sqlite') // try send again
289
-
290
- expect(new Conf().get).toHaveBeenCalledTimes(1)
291
- expect(https.request).toHaveBeenCalledTimes(0)
292
- expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
293
- })
294
-
295
- test(`when telemetry uninitialised, we do nothing`, async () => {
296
- expect(mockTelemetryConfig).toBe(undefined)
297
-
298
- await runTelemetry(mockProjectDir, lists, 'sqlite') // try inform
299
- await runTelemetry(mockProjectDir, lists, 'sqlite') // try send
300
-
301
- expect(new Conf().get).toHaveBeenCalledTimes(2)
302
- expect(https.request).toHaveBeenCalledTimes(0)
303
- expect(mockTelemetryConfig).toBe(undefined) // unchanged
304
- })
305
- })
306
- }
307
-
308
- describe('when something throws internally', () => {
309
- let runTelemetryThrows: any
310
- beforeEach(() => {
311
- // this is a nightmare, don't touch it
312
- jest.resetAllMocks()
313
- jest.resetModules()
314
- runTelemetryThrows = require('../src/lib/telemetry').runTelemetry
315
- })
316
-
317
- test(`nothing actually throws`, async () => {
318
- mockTelemetryConfig = mockTelemetryConfigInitialised
319
-
320
- await runTelemetryThrows(mockProjectDir, lists, 'sqlite') // send
321
-
322
- // expect(new Conf().get).toHaveBeenCalledTimes(1) // nightmare
323
- expect(https.request).toHaveBeenCalledTimes(0)
324
- expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
325
- })
326
- })
327
-
328
- describe('when running in CI', () => {
329
- let runTelemetryCI: any
330
- beforeEach(() => {
331
- // this is a nightmare, don't touch it
332
- jest.resetAllMocks()
333
- jest.resetModules()
334
- jest.mock('ci-info', () => {
335
- return { isCI: true }
336
- })
337
- runTelemetryCI = require('../src/lib/telemetry').runTelemetry
338
- })
339
-
340
- test(`when initialised, nothing is sent`, async () => {
341
- mockTelemetryConfig = mockTelemetryConfigInitialised
342
-
343
- await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try send again
344
-
345
- expect(new Conf().get).toHaveBeenCalledTimes(0)
346
- expect(https.request).toHaveBeenCalledTimes(0)
347
- expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
348
- })
349
-
350
- test(`if not initialised, we do nothing`, async () => {
351
- mockTelemetryConfig = undefined
352
-
353
- await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try inform
354
- await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try send
355
-
356
- expect(new Conf().get).toHaveBeenCalledTimes(0)
357
- expect(https.request).toHaveBeenCalledTimes(0)
358
- expect(mockTelemetryConfig).toBe(undefined) // nothing changed
359
- })
360
- })
361
- })
1
+ import https from 'node:https'
2
+ import Conf from 'conf'
3
+
4
+ import path from 'path'
5
+ import type { InitialisedList } from '../src/lib/core/initialise-lists'
6
+ import { runTelemetry, disableTelemetry } from '../src/lib/telemetry'
7
+
8
+ const mockProjectRoot = path.resolve(__dirname, '..', '..', '..')
9
+ const mockProjectDir = path.join(mockProjectRoot, './tests/test-projects/basic')
10
+ const mockPackageVersions = {
11
+ '@nixxie-cms/core': '14.1.0',
12
+ '@nixxie-cms/auth': '9.0.1',
13
+ '@nixxie-cms/fields-document': '18.0.2',
14
+ '@nixxie-cms/cloudinary': '0.0.1',
15
+ }
16
+
17
+ jest.mock(
18
+ '@nixxie-cms/core/package.json',
19
+ () => {
20
+ return { version: mockPackageVersions['@nixxie-cms/core'] }
21
+ },
22
+ { virtual: true }
23
+ )
24
+ jest.mock(
25
+ '@nixxie-cms/auth/package.json',
26
+ () => {
27
+ return { version: mockPackageVersions['@nixxie-cms/auth'] }
28
+ },
29
+ { virtual: true }
30
+ )
31
+ jest.mock(
32
+ '@nixxie-cms/fields-document/package.json',
33
+ () => {
34
+ return { version: mockPackageVersions['@nixxie-cms/fields-document'] }
35
+ },
36
+ { virtual: true }
37
+ )
38
+ jest.mock(
39
+ '@nixxie-cms/cloudinary/package.json',
40
+ () => {
41
+ return { version: mockPackageVersions['@nixxie-cms/cloudinary'] }
42
+ },
43
+ { virtual: true }
44
+ )
45
+
46
+ let mockTelemetryConfig: any = undefined
47
+
48
+ jest.mock('conf', () => {
49
+ const getMockTelemetryConfig = jest.fn(() => {
50
+ if (mockTelemetryConfig === 'THROW') throw new Error('JSON.parse error')
51
+ return mockTelemetryConfig
52
+ })
53
+
54
+ return function Conf() {
55
+ return {
56
+ get: getMockTelemetryConfig,
57
+ set: (key: string, newState: any) => {
58
+ if (key !== 'telemetry') throw new Error(`Unexpected conf key ${key}`)
59
+ mockTelemetryConfig = newState
60
+ },
61
+ delete: () => {
62
+ mockTelemetryConfig = undefined
63
+ },
64
+ }
65
+ }
66
+ })
67
+
68
+ jest.mock('node:https', () => {
69
+ const once = jest.fn()
70
+ const end = jest.fn()
71
+ const request = jest.fn().mockImplementation((_, __, f) => {
72
+ setTimeout(() => f(), 100)
73
+ return { once, end }
74
+ })
75
+ // added for reach by toHaveBeenCalledWith
76
+ ;(request as any).once = once
77
+ ;(request as any).end = end
78
+ return { request }
79
+ })
80
+
81
+ jest.mock('node:os', () => {
82
+ return {
83
+ ...jest.requireActual('node:os'),
84
+ platform: () => 'nixxie-os',
85
+ }
86
+ })
87
+
88
+ // required as CI is set for tests
89
+ jest.mock('ci-info', () => {
90
+ return { isCI: false }
91
+ })
92
+
93
+ const lists: Record<string, InitialisedList> = {
94
+ Thing: {
95
+ fields: {
96
+ // @ts-expect-error
97
+ id: {
98
+ __nxTelemetryFieldTypeName: 'id',
99
+ },
100
+ // @ts-expect-error
101
+ name: {
102
+ __nxTelemetryFieldTypeName: 'id',
103
+ },
104
+ // @ts-expect-error
105
+ thing: {
106
+ __nxTelemetryFieldTypeName: 'id',
107
+ },
108
+ },
109
+ },
110
+ Stuff: {
111
+ fields: {
112
+ // @ts-expect-error
113
+ id: {
114
+ __nxTelemetryFieldTypeName: 'id',
115
+ },
116
+ // @ts-expect-error
117
+ name: {
118
+ __nxTelemetryFieldTypeName: 'id',
119
+ },
120
+ },
121
+ },
122
+ }
123
+
124
+ describe('Telemetry tests', () => {
125
+ beforeEach(() => {
126
+ jest.clearAllMocks()
127
+ mockTelemetryConfig = undefined // reset state
128
+ })
129
+
130
+ const today = new Date().toJSON().slice(0, 10)
131
+ const mockYesterday = '2023-01-01'
132
+ const mockTelemetryConfigInitialised = {
133
+ informedAt: `${mockYesterday}T01:11:11.111Z`,
134
+ device: { lastSentDate: mockYesterday },
135
+ projects: {
136
+ [mockProjectDir]: {
137
+ lastSentDate: mockYesterday,
138
+ },
139
+ },
140
+ }
141
+
142
+ function expectDidSend(lastSentDate: string | null) {
143
+ expect(https.request).toHaveBeenCalledWith(
144
+ `https://telemetry.nixxieinternational.com/3/project`,
145
+ {
146
+ method: 'POST',
147
+ headers: {
148
+ 'Content-Type': 'application/json',
149
+ },
150
+ },
151
+ expect.any(Function)
152
+ )
153
+ expect((https.request as any).end).toHaveBeenCalledWith(
154
+ JSON.stringify({
155
+ lastSentDate,
156
+ packages: mockPackageVersions,
157
+ database: 'sqlite',
158
+ lists: 2,
159
+ fields: {
160
+ unknown: 0,
161
+ id: 5,
162
+ },
163
+ })
164
+ )
165
+
166
+ expect(https.request).toHaveBeenCalledWith(
167
+ `https://telemetry.nixxieinternational.com/3/device`,
168
+ {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ },
173
+ },
174
+ expect.any(Function)
175
+ )
176
+ expect((https.request as any).end).toHaveBeenCalledWith(
177
+ JSON.stringify({
178
+ lastSentDate,
179
+ os: 'nixxie-os',
180
+ node: process.versions.node.split('.')[0],
181
+ })
182
+ )
183
+ }
184
+
185
+ test('Telemetry writes out an empty configuration, and sends nothing on first run', async () => {
186
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
187
+
188
+ expect(new Conf().get).toHaveBeenCalledTimes(1)
189
+ expect(https.request).toHaveBeenCalledTimes(0)
190
+ expect(mockTelemetryConfig).toStrictEqual({
191
+ informedAt: expect.stringMatching(new RegExp(`^${today}`)),
192
+ device: { lastSentDate: null },
193
+ projects: {},
194
+ })
195
+ })
196
+
197
+ test('Telemetry is sent after inform', async () => {
198
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
199
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send
200
+
201
+ expectDidSend(null)
202
+ expect(new Conf().get).toHaveBeenCalledTimes(2)
203
+ expect(https.request).toHaveBeenCalledTimes(2) // would be 4 if sent twice
204
+ expect(mockTelemetryConfig).toStrictEqual({
205
+ informedAt: expect.stringMatching(new RegExp(`^${today}`)),
206
+ device: { lastSentDate: today },
207
+ projects: {
208
+ [mockProjectDir]: { lastSentDate: today },
209
+ },
210
+ })
211
+ })
212
+
213
+ test('Telemetry is not sent twice in one day', async () => {
214
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
215
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send
216
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send, same day
217
+
218
+ expectDidSend(null)
219
+ expect(new Conf().get).toHaveBeenCalledTimes(3)
220
+ expect(https.request).toHaveBeenCalledTimes(2) // would be 4 if sent twice
221
+ })
222
+
223
+ test('Telemetry sends a lastSentDate on the next run, a different day', async () => {
224
+ mockTelemetryConfig = mockTelemetryConfigInitialised
225
+
226
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send, different day
227
+
228
+ expectDidSend(mockYesterday)
229
+ expect(new Conf().get).toHaveBeenCalledTimes(1)
230
+ expect(https.request).toHaveBeenCalledTimes(2)
231
+ expect(mockTelemetryConfig).toStrictEqual({
232
+ informedAt: expect.stringMatching(new RegExp(`^${mockYesterday}`)),
233
+ device: { lastSentDate: today },
234
+ projects: {
235
+ [mockProjectDir]: { lastSentDate: today },
236
+ },
237
+ })
238
+ })
239
+
240
+ test(`Telemetry is reset when using "nixxie telemetry disable"`, () => {
241
+ disableTelemetry()
242
+
243
+ expect(mockTelemetryConfig).toBe(false)
244
+ })
245
+
246
+ test(`Telemetry is not sent if telemetry configuration is disabled`, async () => {
247
+ mockTelemetryConfig = false
248
+
249
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
250
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send
251
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send, same day
252
+
253
+ expect(new Conf().get).toHaveBeenCalledTimes(3)
254
+ expect(https.request).toHaveBeenCalledTimes(0)
255
+ expect(mockTelemetryConfig).toBe(false)
256
+ })
257
+
258
+ test(`Telemetry is unchanged if configuration is malformed`, async () => {
259
+ mockTelemetryConfig = 'THROW'
260
+
261
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // inform
262
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // send
263
+
264
+ expect(new Conf().get).toHaveBeenCalledTimes(2)
265
+ expect(https.request).toHaveBeenCalledTimes(0)
266
+ expect(mockTelemetryConfig).toStrictEqual('THROW') // nothing changes
267
+ })
268
+
269
+ // easy opt-out tests
270
+ for (const [key, value] of Object.entries({
271
+ NODE_ENV: 'production',
272
+ NIXXIE_TELEMETRY_DISABLED: '1',
273
+ })) {
274
+ describe(`when process.env.${key} is set to ${value}`, () => {
275
+ const envBefore = process.env[key]
276
+
277
+ beforeEach(() => {
278
+ process.env[key] = value
279
+ })
280
+
281
+ afterEach(() => {
282
+ process.env[key] = envBefore
283
+ })
284
+
285
+ test(`when telemetry initialised, we do nothing`, async () => {
286
+ mockTelemetryConfig = mockTelemetryConfigInitialised
287
+
288
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // try send again
289
+
290
+ expect(new Conf().get).toHaveBeenCalledTimes(1)
291
+ expect(https.request).toHaveBeenCalledTimes(0)
292
+ expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
293
+ })
294
+
295
+ test(`when telemetry uninitialised, we do nothing`, async () => {
296
+ expect(mockTelemetryConfig).toBe(undefined)
297
+
298
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // try inform
299
+ await runTelemetry(mockProjectDir, lists, 'sqlite') // try send
300
+
301
+ expect(new Conf().get).toHaveBeenCalledTimes(2)
302
+ expect(https.request).toHaveBeenCalledTimes(0)
303
+ expect(mockTelemetryConfig).toBe(undefined) // unchanged
304
+ })
305
+ })
306
+ }
307
+
308
+ describe('when something throws internally', () => {
309
+ let runTelemetryThrows: any
310
+ beforeEach(() => {
311
+ // this is a nightmare, don't touch it
312
+ jest.resetAllMocks()
313
+ jest.resetModules()
314
+ runTelemetryThrows = require('../src/lib/telemetry').runTelemetry
315
+ })
316
+
317
+ test(`nothing actually throws`, async () => {
318
+ mockTelemetryConfig = mockTelemetryConfigInitialised
319
+
320
+ await runTelemetryThrows(mockProjectDir, lists, 'sqlite') // send
321
+
322
+ // expect(new Conf().get).toHaveBeenCalledTimes(1) // nightmare
323
+ expect(https.request).toHaveBeenCalledTimes(0)
324
+ expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
325
+ })
326
+ })
327
+
328
+ describe('when running in CI', () => {
329
+ let runTelemetryCI: any
330
+ beforeEach(() => {
331
+ // this is a nightmare, don't touch it
332
+ jest.resetAllMocks()
333
+ jest.resetModules()
334
+ jest.mock('ci-info', () => {
335
+ return { isCI: true }
336
+ })
337
+ runTelemetryCI = require('../src/lib/telemetry').runTelemetry
338
+ })
339
+
340
+ test(`when initialised, nothing is sent`, async () => {
341
+ mockTelemetryConfig = mockTelemetryConfigInitialised
342
+
343
+ await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try send again
344
+
345
+ expect(new Conf().get).toHaveBeenCalledTimes(0)
346
+ expect(https.request).toHaveBeenCalledTimes(0)
347
+ expect(mockTelemetryConfig).toBe(mockTelemetryConfigInitialised) // unchanged
348
+ })
349
+
350
+ test(`if not initialised, we do nothing`, async () => {
351
+ mockTelemetryConfig = undefined
352
+
353
+ await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try inform
354
+ await runTelemetryCI(mockProjectDir, lists, 'sqlite') // try send
355
+
356
+ expect(new Conf().get).toHaveBeenCalledTimes(0)
357
+ expect(https.request).toHaveBeenCalledTimes(0)
358
+ expect(mockTelemetryConfig).toBe(undefined) // nothing changed
359
+ })
360
+ })
361
+ })