nucleus-core-ts 0.9.866 → 0.9.867
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/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +11 -1
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +24 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +15 -2
- package/dist/fe/components/IntegrationsPage/store/index.js +19 -0
- package/package.json +1 -1
- package/scripts/build.ts +71 -3
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.test.js +0 -44
- package/dist/fe/components/IntegrationsPage/components/RunPlanSuggestions.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/RunPlanSuggestions.test.js +0 -103
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.test.js +0 -198
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.contract.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.contract.test.js +0 -140
- package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.test.js +0 -29
- package/dist/fe/components/IntegrationsPage/components/formSupport.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/formSupport.test.js +0 -156
- package/dist/fe/components/IntegrationsPage/components/jsonObjectField.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/jsonObjectField.test.js +0 -86
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.test.js +0 -59
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.test.js +0 -45
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.js +0 -47
- package/dist/fe/components/IntegrationsPage/components/settleOwnership.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/components/settleOwnership.test.js +0 -56
- package/dist/fe/components/IntegrationsPage/store/upsertInto.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/store/upsertInto.test.js +0 -67
- package/dist/fe/components/IntegrationsPage/text/text.test.d.ts +0 -1
- package/dist/fe/components/IntegrationsPage/text/text.test.js +0 -67
- package/dist/src/Client/Proxy/authz.test.d.ts +0 -1
- package/dist/src/Client/Proxy/authz.test.js +0 -453
- package/dist/src/Client/Proxy/httpProxy.test.d.ts +0 -1
- package/dist/src/Client/Proxy/httpProxy.test.js +0 -73
- package/dist/src/ElysiaPlugin/auditAttribution.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/authRouteClassifier.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/dbPoolConfig.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/publicRoutes.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/redactSensitiveOutput.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/resolveConfigCredential.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/accountState.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/admin/createUserAuthz.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/admin/resolveMutationSchema.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/captchaWiring.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/emailLinks/emailLinks.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/emailWiring.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/login/deviceInfo.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/magicLink/utils.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/oauth/absolutizeRedirect.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/oauth/oauthStateCookie.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/oauth/resolveSafeOAuthRedirect.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/auth/sessions/adminRoutes.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/authorization/discovery.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/chat/moderation.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/config/helpers.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/domain/hostnames.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/entity/anonymousCreate.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/entity/createScope.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/entity/filterFields.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/entity/relationAuthz.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/monitoring/index.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/monitoring/serverLogs.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/payment/marketplace/authz.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/payment/paymentAuthz.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/payment/validation.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/pubsub/daprAuth.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/pubsub/topicCap.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/adminGuard.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/basePathCollision.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/clientIp.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/csrf.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/redact.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/requestLimits.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/sensitiveColumns.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/sensitiveTables.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/systemSensitiveColumns.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/tenantBinding.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/shared/writeLock.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/storage/cdn.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/storage/helpers.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/storage/imageTransform.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/routes/storage/videoTransform.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/runtime.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/sanitizePayload.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/signNewAccessToken.test.d.ts +0 -1
- package/dist/src/ElysiaPlugin/validationError.test.d.ts +0 -1
- package/dist/src/Managers/Redis/keyPrefix.test.d.ts +0 -1
- package/dist/src/Managers/Storage/liveStorage.test.d.ts +0 -1
- package/dist/src/Managers/Storage/migrate.test.d.ts +0 -1
- package/dist/src/Managers/Storage/parseSmbUrl.test.d.ts +0 -1
- package/dist/src/Managers/Storage/storage.test.d.ts +0 -1
- package/dist/src/Services/Auth/DeviceTrust/deviceToken.test.d.ts +0 -1
- package/dist/src/Services/Auth/JWT/jwt.test.d.ts +0 -1
- package/dist/src/Services/Auth/Password/password.test.d.ts +0 -1
- package/dist/src/Services/Authorization/ClaimGuard/claimGuard.test.d.ts +0 -1
- package/dist/src/Services/Authorization/EndpointDiscovery/discovery.test.d.ts +0 -1
- package/dist/src/Services/Authorization/EndpointDiscovery/seed.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/broaderGrantWins.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/claimMatches.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/fieldRelationGrant.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/fieldWriteClaims.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/jwtScopeEmbed.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/scopeFailClosed.test.d.ts +0 -1
- package/dist/src/Services/Authorization/Middleware/scopeSelfFailClosed.test.d.ts +0 -1
- package/dist/src/Services/Authorization/SeedRunner/expandClaimPatterns.test.d.ts +0 -1
- package/dist/src/Services/Authorization/SeedRunner/scopeReconcile.test.d.ts +0 -1
- package/dist/src/Services/Backup/encryption.test.d.ts +0 -1
- package/dist/src/Services/Captcha/captchaAtomic.test.d.ts +0 -1
- package/dist/src/Services/Chat/sendMessageAtomicity.integration.test.d.ts +0 -1
- package/dist/src/Services/Integrations/bindings.test.d.ts +0 -1
- package/dist/src/Services/Integrations/collect.test.d.ts +0 -1
- package/dist/src/Services/Integrations/fetcher.test.d.ts +0 -1
- package/dist/src/Services/Integrations/lock.test.d.ts +0 -1
- package/dist/src/Services/Integrations/mapping.test.d.ts +0 -1
- package/dist/src/Services/Integrations/oauth.test.d.ts +0 -1
- package/dist/src/Services/Integrations/pagination.test.d.ts +0 -1
- package/dist/src/Services/Integrations/plan.test.d.ts +0 -1
- package/dist/src/Services/Integrations/referenceIds.test.d.ts +0 -1
- package/dist/src/Services/Integrations/revert.test.d.ts +0 -1
- package/dist/src/Services/Integrations/runner.test.d.ts +0 -1
- package/dist/src/Services/Integrations/scheduler.test.d.ts +0 -1
- package/dist/src/Services/Integrations/transfer.test.d.ts +0 -1
- package/dist/src/Services/Integrations/tree.test.d.ts +0 -1
- package/dist/src/Services/Integrations/writer.test.d.ts +0 -1
- package/dist/src/Services/Logger/ServerLogBuffer.test.d.ts +0 -1
- package/dist/src/Services/Logger/auditDedup.test.d.ts +0 -1
- package/dist/src/Services/Logger/auditGate.test.d.ts +0 -1
- package/dist/src/Services/Logger/auditRedact.test.d.ts +0 -1
- package/dist/src/Services/Logger/auditTaxonomy.test.d.ts +0 -1
- package/dist/src/Services/Logger/formatError.test.d.ts +0 -1
- package/dist/src/Services/Logger/redact.test.d.ts +0 -1
- package/dist/src/Services/Monitoring/collectors/memory.test.d.ts +0 -1
- package/dist/src/Services/OAuth/profileSync.test.d.ts +0 -1
- package/dist/src/Services/OAuth/providers/microsoft.test.d.ts +0 -1
- package/dist/src/Services/OAuth/stateStore.test.d.ts +0 -1
- package/dist/src/Services/Payment/Marketplace/marketplaceLedger.test.d.ts +0 -1
- package/dist/src/Services/Payment/Marketplace/marketplacePayout.integration.test.d.ts +0 -1
- package/dist/src/Services/RateLimiter/rateLimiter.integration.test.d.ts +0 -1
- package/dist/src/Services/RateLimiter/rateLimiter.test.d.ts +0 -1
- package/dist/src/Services/SchemaTables/resolveEntities.test.d.ts +0 -1
- package/dist/src/Services/Secrets/SecretsService.test.d.ts +0 -1
- package/dist/src/Services/Secrets/connectionTest.test.d.ts +0 -1
- package/dist/src/Services/Secrets/crypto.test.d.ts +0 -1
- package/dist/src/Services/Secrets/registry.test.d.ts +0 -1
- package/dist/src/Services/Secrets/store.test.d.ts +0 -1
- package/dist/src/Services/Tenant/helpers.test.d.ts +0 -1
- package/dist/src/Services/Tenant/isTrustedSource.test.d.ts +0 -1
- package/dist/src/Services/Tenant/schema.test.d.ts +0 -1
- package/dist/src/Services/WebAuthn/webauthnChallenge.integration.test.d.ts +0 -1
- package/dist/src/Shared/cron.test.d.ts +0 -1
- package/dist/src/Shared/cron.test.js +0 -223
- package/dist/src/Utils/cron.test.d.ts +0 -1
- package/dist/src/Utils/headerList.test.d.ts +0 -1
- package/dist/src/Utils/withDeadline.test.d.ts +0 -1
|
@@ -32,6 +32,13 @@ export function ConnectionTest({ actions, source, loading = false }) {
|
|
|
32
32
|
* the record object is replaced but its id is not, so this never re-ran and a
|
|
33
33
|
* green badge sat under an address that had never been called. Everything the
|
|
34
34
|
* test actually depends on is in the key.
|
|
35
|
+
*
|
|
36
|
+
* A stored CREDENTIAL is such a setting, and it was the one left out. Clearing
|
|
37
|
+
* a token left the green verdict standing for a connection that would now be
|
|
38
|
+
* refused; the mirror image costs more of an afternoon — a red "Not reachable"
|
|
39
|
+
* from a 401 stayed up after the right password was saved, reading as "the
|
|
40
|
+
* password did not help". The masked hints are what move when a credential
|
|
41
|
+
* does, so they are what this watches.
|
|
35
42
|
*/ useEffect(()=>{
|
|
36
43
|
clear();
|
|
37
44
|
}, [
|
|
@@ -39,7 +46,10 @@ export function ConnectionTest({ actions, source, loading = false }) {
|
|
|
39
46
|
source?.baseUrl,
|
|
40
47
|
source?.authType,
|
|
41
48
|
source?.tokenUrl,
|
|
42
|
-
source?.tlsVerify
|
|
49
|
+
source?.tlsVerify,
|
|
50
|
+
source?.authTokenHint,
|
|
51
|
+
source?.oauthClientSecretHint,
|
|
52
|
+
source?.oauthPasswordHint
|
|
43
53
|
]);
|
|
44
54
|
const handleTest = ()=>{
|
|
45
55
|
if (!source) return;
|
|
@@ -69,6 +69,30 @@ export function EndpointSample({ endpoint, actions }) {
|
|
|
69
69
|
actions.sampleEndpoint(requestedFor, params.length > 0 ? values : undefined).then((result)=>{
|
|
70
70
|
if (settleElsewhere(mine, requestedFor)) return;
|
|
71
71
|
store.setSample(result, requestedFor);
|
|
72
|
+
/**
|
|
73
|
+
* The ROW learns it has been tried, not just this card.
|
|
74
|
+
*
|
|
75
|
+
* The step strip counts "tried" off `endpoints[].sampleResponse`,
|
|
76
|
+
* because whether a call has ever been run is a fact about the call and
|
|
77
|
+
* not about whichever screen is open. The server stores it there. This
|
|
78
|
+
* panel never did, so a connection whose calls had all been sampled
|
|
79
|
+
* went on announcing "7 call(s), 0 tried" — seen exactly that way on a
|
|
80
|
+
* customer's live connection, where it read as "nobody has ever tried
|
|
81
|
+
* these", which was not true.
|
|
82
|
+
*
|
|
83
|
+
* Only a sample that actually returned records counts, which is the
|
|
84
|
+
* same rule the strip applies.
|
|
85
|
+
*/ if (result.ok && result.recordCount > 0) {
|
|
86
|
+
const row = store.endpoints.find((candidate)=>candidate.id === requestedFor);
|
|
87
|
+
if (row) {
|
|
88
|
+
store.upsertEndpoint({
|
|
89
|
+
...row,
|
|
90
|
+
sampleResponse: {
|
|
91
|
+
records: result.sample
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
72
96
|
if (!result.ok && result.error) store.setError(result.error);
|
|
73
97
|
}).catch((error)=>{
|
|
74
98
|
if (settleElsewhere(mine, requestedFor)) return;
|
|
@@ -56,9 +56,22 @@ export function EndpointsTab({ actions }) {
|
|
|
56
56
|
// selecting an id the list has not fetched yet made the whole editor
|
|
57
57
|
// collapse to "pick one" — with the form still half-filled a moment ago.
|
|
58
58
|
store.upsertEndpoint(saved);
|
|
59
|
-
// Re-selecting drops the old sample: it described the call as it was
|
|
60
|
-
// configured a moment ago, and the mapping would be built from it.
|
|
61
59
|
store.selectEndpoint(saved.id);
|
|
60
|
+
/**
|
|
61
|
+
* The sample described the call as it was BEFORE this save.
|
|
62
|
+
*
|
|
63
|
+
* Re-selecting was supposed to drop it, and does not: `selectEndpoint`
|
|
64
|
+
* returns early when the id has not changed, which on a save it never has —
|
|
65
|
+
* it is the guard that stops re-choosing the open call from throwing away a
|
|
66
|
+
* sample somebody just paid a real request for. Correct there, and the
|
|
67
|
+
* reason nothing was cleared here.
|
|
68
|
+
*
|
|
69
|
+
* So the sample sat under an edited call: change `responseRootPath` from
|
|
70
|
+
* empty to `data`, save, and the card still reports "Status 200, 0 records"
|
|
71
|
+
* with the old body keys — and the mapping's field list is built from those
|
|
72
|
+
* keys. The answer on screen belonged to a request that is no longer the one
|
|
73
|
+
* configured.
|
|
74
|
+
*/ store.setSample(null, null);
|
|
62
75
|
setReloadToken((token)=>token + 1);
|
|
63
76
|
};
|
|
64
77
|
const handleDeleted = (deletedId)=>{
|
|
@@ -93,6 +93,25 @@ export const { useStore: useIntegrationsStore } = createStore(initial, {
|
|
|
93
93
|
store.endpointsTotal = Math.max(0, store.endpointsTotal - 1);
|
|
94
94
|
store.endpoints = kept;
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* A mapping cannot outlive the call it reads from.
|
|
98
|
+
*
|
|
99
|
+
* The server deletes them — the foreign key cascades — and this list did
|
|
100
|
+
* not, so the step strip went on counting mappings that no longer existed
|
|
101
|
+
* and the IMPORT step stayed unlocked by them. Pressing it landed on an
|
|
102
|
+
* import whose mapping the server had already removed.
|
|
103
|
+
*
|
|
104
|
+
* Filtered here rather than at the call site because the store is what
|
|
105
|
+
* every one of those readings comes from; a caller that forgot would put
|
|
106
|
+
* the same ghost back.
|
|
107
|
+
*/ const survivors = store.mappings.filter((mapping)=>mapping.endpointId !== id);
|
|
108
|
+
if (survivors.length !== store.mappings.length) {
|
|
109
|
+
store.mappingsTotal = Math.max(0, store.mappingsTotal - (store.mappings.length - survivors.length));
|
|
110
|
+
store.mappings = survivors;
|
|
111
|
+
if (store.selectedMappingId && !survivors.some((m)=>m.id === store.selectedMappingId)) {
|
|
112
|
+
store.selectedMappingId = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
96
115
|
}),
|
|
97
116
|
setEndpointsSearch: (store)=>(search)=>batch(()=>{
|
|
98
117
|
store.endpointsSearch = search;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.867",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
package/scripts/build.ts
CHANGED
|
@@ -113,8 +113,18 @@ async function transpileClientModules() {
|
|
|
113
113
|
|
|
114
114
|
log.info(` Transpiling directory: ${srcDir}`)
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
/*
|
|
117
|
+
* Tests do not ship.
|
|
118
|
+
*
|
|
119
|
+
* swc transpiled the whole directory, tests included, so sixteen `.test.js`
|
|
120
|
+
* files went out in the published package — dead weight for every consumer,
|
|
121
|
+
* and a live hazard here: `bun test <path>` matches by substring, so
|
|
122
|
+
* `bun test fe/...` also collected `dist/fe/...` and ran the COMPILED
|
|
123
|
+
* copies. A test that reads a source file by relative path then failed
|
|
124
|
+
* from dist, where that path does not exist — a red run caused entirely by
|
|
125
|
+
* a stale build artefact of a test that passes.
|
|
126
|
+
*/
|
|
127
|
+
await $`bunx @swc/cli ${srcDir} -d ${config.build.outDir} --ignore ${'**/*.test.ts'} --ignore ${'**/*.test.tsx'} -C jsc.parser.syntax=typescript -C jsc.parser.tsx=true -C jsc.target=es2020 -C module.type=es6 -C jsc.transform.react.runtime=automatic`
|
|
118
128
|
.quiet()
|
|
119
129
|
.catch((e) => {
|
|
120
130
|
log.warn(`swc warning: ${e}`)
|
|
@@ -146,7 +156,7 @@ async function transpileClientModules() {
|
|
|
146
156
|
const transpileDirs = config.transpileDirs || []
|
|
147
157
|
for (const srcDir of transpileDirs) {
|
|
148
158
|
log.info(` Transpiling dependency: ${srcDir}`)
|
|
149
|
-
await $`bunx @swc/cli ${srcDir} -d ${config.build.outDir} -C jsc.parser.syntax=typescript -C jsc.parser.tsx=true -C jsc.target=es2020 -C module.type=es6 -C jsc.transform.react.runtime=automatic`
|
|
159
|
+
await $`bunx @swc/cli ${srcDir} -d ${config.build.outDir} --ignore ${'**/*.test.ts'} --ignore ${'**/*.test.tsx'} -C jsc.parser.syntax=typescript -C jsc.parser.tsx=true -C jsc.target=es2020 -C module.type=es6 -C jsc.transform.react.runtime=automatic`
|
|
150
160
|
.quiet()
|
|
151
161
|
.catch((e) => {
|
|
152
162
|
log.warn(`swc warning: ${e}`)
|
|
@@ -185,6 +195,63 @@ async function generateTypes() {
|
|
|
185
195
|
log.success('Type definitions generated (core src/ verified clean)')
|
|
186
196
|
}
|
|
187
197
|
|
|
198
|
+
/**
|
|
199
|
+
* What the package must and must not contain, checked before it can be published.
|
|
200
|
+
*
|
|
201
|
+
* Both halves of this were live mistakes on the same afternoon. Tests shipped
|
|
202
|
+
* for months — sixteen `.test.js` files in every consumer's node_modules, and
|
|
203
|
+
* a trap here too, because `bun test <path>` matches by substring and so ran
|
|
204
|
+
* the COMPILED copies out of `dist`. Then excluding them with an unquoted glob
|
|
205
|
+
* silently took `src/Shared/cron.js` with it: the build still said OK, the
|
|
206
|
+
* declarations were still there, and the missing file would have broken the
|
|
207
|
+
* CONSUMER's build rather than ours — the config file next to it warns about
|
|
208
|
+
* exactly that failure.
|
|
209
|
+
*
|
|
210
|
+
* So the shape of the package is asserted, not assumed. A build that cannot
|
|
211
|
+
* satisfy this refuses to finish, which is the only moment the cost is still
|
|
212
|
+
* ours.
|
|
213
|
+
*/
|
|
214
|
+
async function verifyPackageShape() {
|
|
215
|
+
log.info('Verifying what the package contains...')
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* Walked here rather than through `getJsFiles`, which filters out everything
|
|
219
|
+
* the obfuscator is told to skip — `fe/` among them. That is the right list
|
|
220
|
+
* for obfuscation and the wrong one for this: the sixteen test files this
|
|
221
|
+
* check exists to catch were all under `dist/fe`, and a check that cannot see
|
|
222
|
+
* them would have passed while they shipped.
|
|
223
|
+
*/
|
|
224
|
+
const shipped: string[] = []
|
|
225
|
+
const walk = async (dir: string) => {
|
|
226
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
227
|
+
const full = join(dir, entry.name)
|
|
228
|
+
if (entry.isDirectory()) await walk(full)
|
|
229
|
+
else if (entry.name.endsWith('.js')) shipped.push(full)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
await walk(config.build.outDir)
|
|
233
|
+
|
|
234
|
+
const tests = shipped.filter((file) => /\.test\.js$/.test(file))
|
|
235
|
+
if (tests.length > 0) {
|
|
236
|
+
log.error(`${tests.length} test file(s) would be published, e.g. ${tests[0]}`)
|
|
237
|
+
throw new Error('tests must not ship in the package')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Every directory shipped for the frontend to import at runtime has to have
|
|
241
|
+
// actually produced something. An empty one is a file the consumer asks for
|
|
242
|
+
// and never receives.
|
|
243
|
+
for (const srcDir of config.transpileDirs || []) {
|
|
244
|
+
const outDir = join(config.build.outDir, srcDir.replace('./', ''))
|
|
245
|
+
const produced = shipped.filter((file) => file.includes(outDir))
|
|
246
|
+
if (produced.length === 0) {
|
|
247
|
+
log.error(`${srcDir} produced no JavaScript in ${outDir}`)
|
|
248
|
+
throw new Error(`${srcDir} is declared shippable but nothing was emitted for it`)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
log.success(`Package shape verified (${shipped.length} files, no tests)`)
|
|
253
|
+
}
|
|
254
|
+
|
|
188
255
|
async function getJsFiles(dir: string): Promise<string[]> {
|
|
189
256
|
const files: string[] = []
|
|
190
257
|
|
|
@@ -346,6 +413,7 @@ async function main() {
|
|
|
346
413
|
|
|
347
414
|
log.step(7, 7, 'Verifying...')
|
|
348
415
|
await verifyBuild()
|
|
416
|
+
await verifyPackageShape()
|
|
349
417
|
|
|
350
418
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2)
|
|
351
419
|
console.log(`\n${GREEN}🎉 Build completed in ${elapsed}s${NC}\n`)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { describeRow } from './RunPlanIssues';
|
|
3
|
-
/**
|
|
4
|
-
* A preview that names the key which collided but not the values behind it
|
|
5
|
-
* cannot answer the only question worth asking: is this the same record listed
|
|
6
|
-
* twice, or two records the key cannot tell apart? The first is a skip; the
|
|
7
|
-
* second means the key is too narrow and rows are being lost.
|
|
8
|
-
*/ describe('describeRow', ()=>{
|
|
9
|
-
test('lists the mapped columns and their values, in order', ()=>{
|
|
10
|
-
expect(describeRow({
|
|
11
|
-
name: 'ÜRETİM PLANLAMA MÜDÜRLÜĞÜ',
|
|
12
|
-
slug: 'TAT-110'
|
|
13
|
-
})).toBe('name: ÜRETİM PLANLAMA MÜDÜRLÜĞÜ, slug: TAT-110');
|
|
14
|
-
});
|
|
15
|
-
test('an empty column reads as a dash, not as nothing at all', ()=>{
|
|
16
|
-
expect(describeRow({
|
|
17
|
-
name: null,
|
|
18
|
-
slug: 'TAT-110'
|
|
19
|
-
})).toBe('name: —, slug: TAT-110');
|
|
20
|
-
expect(describeRow({
|
|
21
|
-
name: undefined,
|
|
22
|
-
slug: 'X'
|
|
23
|
-
})).toBe('name: —, slug: X');
|
|
24
|
-
});
|
|
25
|
-
test('a long value is cut rather than allowed to bury the rest', ()=>{
|
|
26
|
-
const long = 'A'.repeat(200);
|
|
27
|
-
const described = describeRow({
|
|
28
|
-
description: long,
|
|
29
|
-
slug: 'X'
|
|
30
|
-
});
|
|
31
|
-
expect(described.length).toBeLessThan(120);
|
|
32
|
-
expect(described).toContain('…');
|
|
33
|
-
expect(described).toContain('slug: X');
|
|
34
|
-
});
|
|
35
|
-
test('non-string values are shown rather than dropped', ()=>{
|
|
36
|
-
expect(describeRow({
|
|
37
|
-
displayOrder: 0,
|
|
38
|
-
enabled: false
|
|
39
|
-
})).toBe('displayOrder: 0, enabled: false');
|
|
40
|
-
});
|
|
41
|
-
test('an empty row yields an empty description rather than punctuation', ()=>{
|
|
42
|
-
expect(describeRow({})).toBe('');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { missingRequiredCounts, suggestEmailTemplate } from './RunPlanSuggestions';
|
|
3
|
-
/**
|
|
4
|
-
* A preview that says "561 records are missing e-mail" has told the truth and
|
|
5
|
-
* stopped one step short of being useful. The answer is nearly always to build
|
|
6
|
-
* the value from the fields that ARE there, and the panel knows which those are.
|
|
7
|
-
*/ const plan = (issues)=>({
|
|
8
|
-
issues,
|
|
9
|
-
referenceInserts: [],
|
|
10
|
-
warnings: []
|
|
11
|
-
});
|
|
12
|
-
const issue = (message, i)=>({
|
|
13
|
-
sourceIndex: i,
|
|
14
|
-
kind: 'missing_required',
|
|
15
|
-
message
|
|
16
|
-
});
|
|
17
|
-
describe('counting what each missing field costs', ()=>{
|
|
18
|
-
it('counts the records one field kept out', ()=>{
|
|
19
|
-
const counts = missingRequiredCounts(plan([
|
|
20
|
-
issue('Required field(s) empty: email', 0),
|
|
21
|
-
issue('Required field(s) empty: email', 1)
|
|
22
|
-
]));
|
|
23
|
-
expect(counts).toEqual([
|
|
24
|
-
{
|
|
25
|
-
field: 'email',
|
|
26
|
-
records: 2
|
|
27
|
-
}
|
|
28
|
-
]);
|
|
29
|
-
});
|
|
30
|
-
it('counts each field of a record that is missing two', ()=>{
|
|
31
|
-
const counts = missingRequiredCounts(plan([
|
|
32
|
-
issue('Required field(s) empty: email, code', 0)
|
|
33
|
-
]));
|
|
34
|
-
expect(counts).toEqual([
|
|
35
|
-
{
|
|
36
|
-
field: 'email',
|
|
37
|
-
records: 1
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
field: 'code',
|
|
41
|
-
records: 1
|
|
42
|
-
}
|
|
43
|
-
]);
|
|
44
|
-
});
|
|
45
|
-
it('puts the biggest problem first', ()=>{
|
|
46
|
-
const counts = missingRequiredCounts(plan([
|
|
47
|
-
issue('Required field(s) empty: code', 0),
|
|
48
|
-
issue('Required field(s) empty: email', 1),
|
|
49
|
-
issue('Required field(s) empty: email', 2)
|
|
50
|
-
]));
|
|
51
|
-
expect(counts[0]).toEqual({
|
|
52
|
-
field: 'email',
|
|
53
|
-
records: 2
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
it('ignores issues of every other kind', ()=>{
|
|
57
|
-
const counts = missingRequiredCounts(plan([
|
|
58
|
-
{
|
|
59
|
-
sourceIndex: 0,
|
|
60
|
-
kind: 'duplicate_in_batch',
|
|
61
|
-
message: 'twice'
|
|
62
|
-
}
|
|
63
|
-
]));
|
|
64
|
-
expect(counts).toEqual([]);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe('the address it suggests building', ()=>{
|
|
68
|
-
const mapping = (sources)=>({
|
|
69
|
-
fieldMappings: sources.map((source)=>({
|
|
70
|
-
source,
|
|
71
|
-
target: source
|
|
72
|
-
}))
|
|
73
|
-
});
|
|
74
|
-
it('is built from the fields this source really sends', ()=>{
|
|
75
|
-
expect(suggestEmailTemplate(mapping([
|
|
76
|
-
'name',
|
|
77
|
-
'surname',
|
|
78
|
-
'employeeNo'
|
|
79
|
-
]))).toBe('{name}.{surname}@example.com');
|
|
80
|
-
});
|
|
81
|
-
it('recognises the other names those fields go by', ()=>{
|
|
82
|
-
expect(suggestEmailTemplate(mapping([
|
|
83
|
-
'firstName',
|
|
84
|
-
'lastName'
|
|
85
|
-
]))).toBe('{firstName}.{lastName}@example.com');
|
|
86
|
-
});
|
|
87
|
-
it('offers what it can when only one of the two is there', ()=>{
|
|
88
|
-
expect(suggestEmailTemplate(mapping([
|
|
89
|
-
'surname'
|
|
90
|
-
]))).toBe('{surname}@example.com');
|
|
91
|
-
});
|
|
92
|
-
it('suggests nothing rather than nonsense when there is no name at all', ()=>{
|
|
93
|
-
expect(suggestEmailTemplate(mapping([
|
|
94
|
-
'employeeNo',
|
|
95
|
-
'departmentCode'
|
|
96
|
-
]))).toBeNull();
|
|
97
|
-
});
|
|
98
|
-
it('ignores a source that is already a template', ()=>{
|
|
99
|
-
expect(suggestEmailTemplate(mapping([
|
|
100
|
-
'{a}-{b}'
|
|
101
|
-
]))).toBeNull();
|
|
102
|
-
});
|
|
103
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, test } from 'bun:test';
|
|
2
|
-
import { draftFromSource, draftPatchFromSource, draftToSource, emptyConnectionDraft, validateConnectionDraft } from './connectionDraft';
|
|
3
|
-
/**
|
|
4
|
-
* Found in production, creating the first connection: the form said "Empty
|
|
5
|
-
* means the server default" under the timeout, and saving with it empty
|
|
6
|
-
* answered "Missing required field: timeout_ms".
|
|
7
|
-
*
|
|
8
|
-
* `timeout_ms` is NOT NULL with a default of 30000. An explicit null does not
|
|
9
|
-
* fall back to a column default — it overrides it and violates the constraint.
|
|
10
|
-
* The distinction between "send null" and "do not send the key" is the whole
|
|
11
|
-
* bug, so it is what these assert.
|
|
12
|
-
*/ const filled = ()=>({
|
|
13
|
-
...emptyConnectionDraft,
|
|
14
|
-
name: 'TatMetal HR',
|
|
15
|
-
slug: 'tatmetal-hr',
|
|
16
|
-
baseUrl: 'https://online.tatmetal.com/hr-service'
|
|
17
|
-
});
|
|
18
|
-
describe('draftToSource — columns that are NOT NULL with a default', ()=>{
|
|
19
|
-
test('an empty timeout is omitted, not sent as null', ()=>{
|
|
20
|
-
const payload = draftToSource(filled());
|
|
21
|
-
expect('timeoutMs' in payload).toBe(false);
|
|
22
|
-
expect(payload.timeoutMs).toBeUndefined();
|
|
23
|
-
});
|
|
24
|
-
test('a timeout that was typed is sent as a number', ()=>{
|
|
25
|
-
const payload = draftToSource({
|
|
26
|
-
...filled(),
|
|
27
|
-
timeoutMs: '45000'
|
|
28
|
-
});
|
|
29
|
-
expect(payload.timeoutMs).toBe(45000);
|
|
30
|
-
});
|
|
31
|
-
test('whitespace and nonsense are treated as empty rather than as null', ()=>{
|
|
32
|
-
for (const value of [
|
|
33
|
-
' ',
|
|
34
|
-
'abc',
|
|
35
|
-
'0',
|
|
36
|
-
'-1'
|
|
37
|
-
]){
|
|
38
|
-
const payload = draftToSource({
|
|
39
|
-
...filled(),
|
|
40
|
-
timeoutMs: value
|
|
41
|
-
});
|
|
42
|
-
expect(`${value} → ${'timeoutMs' in payload}`).toBe(`${value} → false`);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
test('the other NOT NULL columns always carry a concrete value', ()=>{
|
|
46
|
-
const payload = draftToSource(filled());
|
|
47
|
-
expect(payload.authType).toBe('none');
|
|
48
|
-
expect(payload.tlsVerify).toBe(true);
|
|
49
|
-
expect(payload.enabled).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe('draftToSource — fields the chosen auth type does not use', ()=>{
|
|
53
|
-
test('a bearer connection clears the OAuth settings rather than leaving them', ()=>{
|
|
54
|
-
const payload = draftToSource({
|
|
55
|
-
...filled(),
|
|
56
|
-
authType: 'bearer',
|
|
57
|
-
tokenUrl: 'https://example.com/token',
|
|
58
|
-
oauthClientId: 'left-behind'
|
|
59
|
-
});
|
|
60
|
-
expect(payload.tokenUrl).toBeNull();
|
|
61
|
-
expect(payload.oauthClientId).toBeNull();
|
|
62
|
-
expect(payload.oauthGrantType).toBeNull();
|
|
63
|
-
});
|
|
64
|
-
test('a username is kept only for the password grant', ()=>{
|
|
65
|
-
const base = {
|
|
66
|
-
...filled(),
|
|
67
|
-
authType: 'oauth2',
|
|
68
|
-
oauthUsername: 'svc-account'
|
|
69
|
-
};
|
|
70
|
-
expect(draftToSource({
|
|
71
|
-
...base,
|
|
72
|
-
oauthGrantType: 'password'
|
|
73
|
-
}).oauthUsername).toBe('svc-account');
|
|
74
|
-
expect(draftToSource({
|
|
75
|
-
...base,
|
|
76
|
-
oauthGrantType: 'client_credentials'
|
|
77
|
-
}).oauthUsername).toBeNull();
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
describe('validateConnectionDraft', ()=>{
|
|
81
|
-
test('the three fields a connection cannot do without are required', ()=>{
|
|
82
|
-
const errors = validateConnectionDraft(emptyConnectionDraft);
|
|
83
|
-
expect(errors.name).toBeDefined();
|
|
84
|
-
expect(errors.slug).toBeDefined();
|
|
85
|
-
expect(errors.baseUrl).toBeDefined();
|
|
86
|
-
});
|
|
87
|
-
test('a filled draft passes', ()=>{
|
|
88
|
-
expect(validateConnectionDraft(filled())).toEqual({});
|
|
89
|
-
});
|
|
90
|
-
test('plain http is accepted — an in-cluster service is a normal target', ()=>{
|
|
91
|
-
const errors = validateConnectionDraft({
|
|
92
|
-
...filled(),
|
|
93
|
-
baseUrl: 'http://hr-service:8080'
|
|
94
|
-
});
|
|
95
|
-
expect(errors.baseUrl).toBeUndefined();
|
|
96
|
-
});
|
|
97
|
-
test('a non-numeric timeout is refused before it can reach the server', ()=>{
|
|
98
|
-
expect(validateConnectionDraft({
|
|
99
|
-
...filled(),
|
|
100
|
-
timeoutMs: 'soon'
|
|
101
|
-
}).timeoutMs).toBeDefined();
|
|
102
|
-
expect(validateConnectionDraft({
|
|
103
|
-
...filled(),
|
|
104
|
-
timeoutMs: ''
|
|
105
|
-
}).timeoutMs).toBeUndefined();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe('draftFromSource', ()=>{
|
|
109
|
-
test('a saved timeout comes back into the field', ()=>{
|
|
110
|
-
const draft = draftFromSource({
|
|
111
|
-
id: 'a',
|
|
112
|
-
name: 'n',
|
|
113
|
-
slug: 's',
|
|
114
|
-
baseUrl: 'https://x.test',
|
|
115
|
-
authType: 'none',
|
|
116
|
-
timeoutMs: 45000
|
|
117
|
-
});
|
|
118
|
-
expect(draft.timeoutMs).toBe('45000');
|
|
119
|
-
});
|
|
120
|
-
test('a round trip through the form does not invent a change', ()=>{
|
|
121
|
-
const source = {
|
|
122
|
-
id: 'a',
|
|
123
|
-
name: 'n',
|
|
124
|
-
slug: 's',
|
|
125
|
-
baseUrl: 'https://x.test',
|
|
126
|
-
authType: 'none',
|
|
127
|
-
tlsVerify: true,
|
|
128
|
-
enabled: true,
|
|
129
|
-
timeoutMs: 30000
|
|
130
|
-
};
|
|
131
|
-
expect(draftToSource(draftFromSource(source), 'a').timeoutMs).toBe(30000);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
/**
|
|
135
|
-
* What a save is allowed to correct in the boxes.
|
|
136
|
-
*
|
|
137
|
-
* After an edit the form showed what was TYPED, not what was stored: emptying
|
|
138
|
-
* the timeout left the box empty while the connection kept timing out at the
|
|
139
|
-
* stored value, and a trailing space in the name was trimmed on the way out so
|
|
140
|
-
* the switcher above and the box below disagreed until F5. The reply has to be
|
|
141
|
-
* read back — but only the parts of it that are actually there, because a
|
|
142
|
-
* field-scoped patch claim strips columns out, and `draftFromSource` fills a
|
|
143
|
-
* default for every column it does not find. Blanking a required box is worse
|
|
144
|
-
* than leaving it stale.
|
|
145
|
-
*/ describe('the boxes a saved record corrects', ()=>{
|
|
146
|
-
const stored = {
|
|
147
|
-
id: 's1',
|
|
148
|
-
name: 'Acme',
|
|
149
|
-
slug: 'acme',
|
|
150
|
-
baseUrl: 'https://acme.test',
|
|
151
|
-
timeoutMs: 30000
|
|
152
|
-
};
|
|
153
|
-
it('takes the server’s value for a field it answered with', ()=>{
|
|
154
|
-
// Typed "Acme " with a trailing space; stored trimmed.
|
|
155
|
-
expect(draftPatchFromSource(stored).name).toBe('Acme');
|
|
156
|
-
});
|
|
157
|
-
/** The case this exists for: the box says empty, the server says 30000. */ it('corrects a timeout the server kept when the box was emptied', ()=>{
|
|
158
|
-
expect(draftPatchFromSource(stored).timeoutMs).toBe('30000');
|
|
159
|
-
});
|
|
160
|
-
it('says nothing at all about a field the reply left out', ()=>{
|
|
161
|
-
const patch = draftPatchFromSource({
|
|
162
|
-
id: 's1',
|
|
163
|
-
name: 'Acme'
|
|
164
|
-
});
|
|
165
|
-
expect(patch).toEqual({
|
|
166
|
-
name: 'Acme'
|
|
167
|
-
});
|
|
168
|
-
expect('slug' in patch).toBe(false);
|
|
169
|
-
expect('timeoutMs' in patch).toBe(false);
|
|
170
|
-
});
|
|
171
|
-
/**
|
|
172
|
-
* The failure this shape prevents: applied over a real draft, a partial reply
|
|
173
|
-
* must correct what it mentions and leave everything else alone.
|
|
174
|
-
*/ it('leaves the rest of the draft untouched when applied over it', ()=>{
|
|
175
|
-
const draft = {
|
|
176
|
-
...emptyConnectionDraft,
|
|
177
|
-
name: 'Acme ',
|
|
178
|
-
slug: 'acme',
|
|
179
|
-
baseUrl: 'https://a.test'
|
|
180
|
-
};
|
|
181
|
-
const next = {
|
|
182
|
-
...draft,
|
|
183
|
-
...draftPatchFromSource({
|
|
184
|
-
id: 's1',
|
|
185
|
-
name: 'Acme'
|
|
186
|
-
})
|
|
187
|
-
};
|
|
188
|
-
expect(next.name).toBe('Acme');
|
|
189
|
-
expect(next.slug).toBe('acme');
|
|
190
|
-
expect(next.baseUrl).toBe('https://a.test');
|
|
191
|
-
});
|
|
192
|
-
/** null is an answer — "no timeout" — not an absence. */ it('treats a null the server returned as a value, not as silence', ()=>{
|
|
193
|
-
expect(draftPatchFromSource({
|
|
194
|
-
id: 's1',
|
|
195
|
-
timeoutMs: null
|
|
196
|
-
}).timeoutMs).toBe('');
|
|
197
|
-
});
|
|
198
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|