@tollwyn/baukasten 2.0.1 → 3.0.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/README.md +137 -5
- package/dist/check.d.ts +0 -2
- package/dist/check.js +167 -8
- package/dist/eslint.d.ts +2 -0
- package/dist/eslint.js +64 -0
- package/dist/gate.d.ts +32 -0
- package/dist/gate.js +49 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/knip.d.ts +14 -0
- package/dist/knip.js +25 -0
- package/dist/product.d.ts +4 -3
- package/dist/product.js +1 -1
- package/dist/server/adapter/migrate.d.ts +2 -1
- package/dist/server/billing.d.ts +3 -2
- package/dist/server/catalog.d.ts +1 -7
- package/dist/server/catalog.js +1 -1
- package/dist/server/config.d.ts +1 -1
- package/dist/server/core.d.ts +2 -2
- package/dist/server/endpoints.d.ts +0 -8
- package/dist/server/endpoints.js +1 -1
- package/dist/server/index.d.ts +3 -10
- package/dist/server/index.js +1 -2
- package/dist/server/messages.d.ts +1 -1
- package/dist/server/pages.d.ts +0 -3
- package/dist/server/pages.js +1 -1
- package/dist/server/ports.d.ts +3 -2
- package/dist/shopify/hook.d.ts +2 -1
- package/dist/shopify/index.d.ts +2 -7
- package/dist/shopify/index.js +0 -1
- package/dist/shopify/pages.d.ts +0 -2
- package/dist/shopify/pages.js +1 -1
- package/dist/shopify/ports.d.ts +2 -1
- package/dist/shopify/shops.d.ts +2 -5
- package/dist/shopify/shops.js +2 -2
- package/dist/testing/browser.d.ts +0 -2
- package/dist/testing/browser.js +1 -1
- package/dist/testing/index.d.ts +5 -5
- package/dist/testing/index.js +2 -2
- package/dist/testing/shopify.d.ts +2 -1
- package/dist/testing/smtp.d.ts +2 -3
- package/dist/testing/smtp.js +1 -1
- package/dist/testing/stripe.d.ts +5 -5
- package/dist/ui/Linked.svelte.d.ts +2 -2
- package/dist/ui/index.d.ts +0 -1
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A product is a SvelteKit 3 app with `@sveltejs/adapter-node` 6 and Svelte 5, tes
|
|
|
12
12
|
```sh
|
|
13
13
|
npm install --save-exact @tollwyn/baukasten@<version>
|
|
14
14
|
npm install --save-dev @sveltejs/adapter-node@6 @playwright/test smtp-server
|
|
15
|
+
npm install --save-dev eslint@10 knip@6 typescript-eslint@8 eslint-plugin-svelte@3 @eslint-community/eslint-plugin-eslint-comments@4 @eslint/js@10 globals@17
|
|
15
16
|
npx --no-install baukasten-stubs
|
|
16
17
|
# fill in what the product owns (below)
|
|
17
18
|
npx --no-install baukasten-check
|
|
@@ -57,6 +58,8 @@ them: `baukasten.config.ts`, `src/app.css`, `src/lib/server/baukasten.ts` and
|
|
|
57
58
|
previous version must run against the new schema). Every table that holds data of an account
|
|
58
59
|
points at it with `REFERENCES baukasten.accounts (id) ON DELETE CASCADE`. It reads the tables of
|
|
59
60
|
the baukasten only through its functions and never changes them.
|
|
61
|
+
- `eslint.config.js`, `knip.config.ts`, `tsconfig.json` and the script `gate` of `package.json`, as
|
|
62
|
+
«The gate» below says.
|
|
60
63
|
- `.env.example`, naming every variable without a value (`envExampleProblems` of `/testing`).
|
|
61
64
|
- `playwright.config.ts` with `testDir: 'e2e'` and `TEST_DATABASE_URL`, a Postgres server on which the
|
|
62
65
|
tests create their databases. It sets the variables the configuration of the product requires in
|
|
@@ -98,15 +101,79 @@ it. `muster/` shows all of it with Tailwind CSS 4, Skeleton and a font from font
|
|
|
98
101
|
`src/app.css`, which Vite bundles (a small font inlined as `data:`, which the policy allows), and
|
|
99
102
|
pictures in `static/` or the product's code. Nothing from another server: the policy refuses it.
|
|
100
103
|
|
|
104
|
+
## The gate
|
|
105
|
+
|
|
106
|
+
`npm run gate` is the gate of the product, which its workflow runs. What lints in it is the
|
|
107
|
+
baukasten's, and `baukasten-check` holds it:
|
|
108
|
+
|
|
109
|
+
- `eslint.config.js` spreads the config of `@tollwyn/baukasten/eslint` first and adds the product's
|
|
110
|
+
own rules on top:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import baukasten from '@tollwyn/baukasten/eslint';
|
|
114
|
+
|
|
115
|
+
export default [...baukasten];
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The config lints every file with types: the type-checked rules of typescript-eslint
|
|
119
|
+
(`recommendedTypeChecked`), Svelte's recommended rules for the components, and a reason after
|
|
120
|
+
`--` in every comment that turns a rule off, as in `@ts-expect-error`. A component, a universal
|
|
121
|
+
load function, hook or matcher and the service worker import nothing of `/server`, `/shopify` or
|
|
122
|
+
`/testing` but types. It sets no rule of formatting, so Prettier needs no config beside it. The
|
|
123
|
+
product's config turns no rule off, and never names `@typescript-eslint/no-floating-promises`,
|
|
124
|
+
`@typescript-eslint/no-misused-promises`, `@typescript-eslint/ban-ts-comment`,
|
|
125
|
+
`@eslint-community/eslint-comments/require-description` or `disableTypeChecked`: a promise nobody
|
|
126
|
+
awaits loses its error, `@ts-nocheck` and `@ts-ignore` take code out of the type check, and a rule
|
|
127
|
+
turned off without its reason hides why. Where a rule must stay off at one place, the comment
|
|
128
|
+
there says why. Imports of its own a product restricts with ESLint's `no-restricted-imports`:
|
|
129
|
+
`@typescript-eslint/no-restricted-imports` would replace the config's for the same files.
|
|
130
|
+
|
|
131
|
+
- `knip.config.ts` spreads the preset of `@tollwyn/baukasten/knip`, which knows the routes, the
|
|
132
|
+
stubs, the connection test and what the baukasten loads, and adds what is the product's own:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import baukasten from '@tollwyn/baukasten/knip';
|
|
136
|
+
import type { KnipConfig } from 'knip';
|
|
137
|
+
|
|
138
|
+
export default { ...baukasten } satisfies KnipConfig;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
It sets no `rules`, `include` or `exclude`, and no other configuration of knip lies beside it
|
|
142
|
+
(`knip.json` and the like, or `knip` in `package.json`): knip finds unused files, exports and
|
|
143
|
+
dependencies. The preset reads the configuration of Playwright without running it; what that
|
|
144
|
+
loads besides its tests (`globalSetup`, a `webServer`, a reporter of its own) the product adds to
|
|
145
|
+
`playwright.entry`.
|
|
146
|
+
|
|
147
|
+
- The script `gate` runs `svelte-check --tsconfig ./tsconfig.json --fail-on-warnings` (without
|
|
148
|
+
`--tsconfig`, svelte-check reports nothing of the TypeScript files),
|
|
149
|
+
`eslint . --config eslint.config.js --max-warnings 0` (with `--config`, an `eslint.config.js` in a
|
|
150
|
+
folder below takes no files from it) and `knip`, each exactly so, after `svelte-kit sync`, which
|
|
151
|
+
writes the types all three read: in itself or in a script it runs with `npm run`, among commands
|
|
152
|
+
joined by `&&` and nothing else, since `;`, `||`, `|` or `&` could let a failure pass. The
|
|
153
|
+
`compilerOptions` that `vite.config.ts` passes to `sveltekit()` set no `warningFilter`:
|
|
154
|
+
svelte-check compiles with them and would not see the warnings it filters. One warning at its
|
|
155
|
+
place a `svelte-ignore` comment silences.
|
|
156
|
+
|
|
157
|
+
- `tsconfig.json` sets `strict` and `noUncheckedIndexedAccess` to `true` in its own
|
|
158
|
+
`compilerOptions` and turns off no part of `strict`. Its `include` names `src`, `e2e` and the
|
|
159
|
+
TypeScript in the root (`*.ts`), and not the JavaScript in the root: the config lints that through
|
|
160
|
+
a project of its own, with the same options, and typescript-eslint refuses a file that is in both.
|
|
161
|
+
|
|
162
|
+
ESLint, knip and the plugins the config imports are dev dependencies of the product, installed with
|
|
163
|
+
the third command above, and `baukasten-check` holds their versions. They are neither dependencies
|
|
164
|
+
nor peers of the baukasten: npm puts every dependency and every peer of a dependency into the image
|
|
165
|
+
of the product, and these have no business there.
|
|
166
|
+
|
|
101
167
|
## The check and the connection test
|
|
102
168
|
|
|
103
169
|
`baukasten-check` runs in the root of the product, before its gate, against the installed version:
|
|
104
170
|
the stubs are there and unchanged, `deploy/auslieferung.json` names the webhook and the rights of
|
|
105
|
-
the baukasten, SvelteKit and Svelte fit its peer ranges
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
`baukasten
|
|
109
|
-
|
|
171
|
+
the baukasten, SvelteKit and Svelte fit its peer ranges, adapter-node is version 6 and ESLint, knip
|
|
172
|
+
and the plugins of the config are in their versions, `overrides` in `package.json` replaces nothing
|
|
173
|
+
the baukasten depends on and its `imports` map `#lib/*`, no migration of the product names the
|
|
174
|
+
schema `baukasten` beyond its references to `baukasten.accounts`, the configuration and
|
|
175
|
+
`SVELTEKIT_OPTIONS` are in place, and the gate is as above. Exit 0 when the product fits; else exit
|
|
176
|
+
1 with one line per problem. It reads these files and runs none of them.
|
|
110
177
|
|
|
111
178
|
`e2e/baukasten.spec.ts` runs the connection test in the gate of the product, against the built
|
|
112
179
|
product with the doubles of Stripe and mail: sign-up with a referral tag, mail and confirmation,
|
|
@@ -138,6 +205,7 @@ connection test among them.
|
|
|
138
205
|
```sh
|
|
139
206
|
npm install --save-exact @tollwyn/baukasten@<version>
|
|
140
207
|
npm install --save-dev @sveltejs/adapter-node@6 @playwright/test smtp-server
|
|
208
|
+
npm install --save-dev eslint@10 knip@6 typescript-eslint@8 eslint-plugin-svelte@3 @eslint-community/eslint-plugin-eslint-comments@4 @eslint/js@10 globals@17
|
|
141
209
|
npx --no-install baukasten-stubs shopify
|
|
142
210
|
# fill in what the app owns (below)
|
|
143
211
|
npx --no-install baukasten-check
|
|
@@ -197,6 +265,7 @@ missing, it writes the files the app owns as a starting point: `baukasten.config
|
|
|
197
265
|
holds data of a store points at it with `REFERENCES baukasten.shops (domain) ON DELETE CASCADE`:
|
|
198
266
|
so `shop/redact` deletes them with the store.
|
|
199
267
|
- `.env.example` (`envExampleProblems` of `/testing`) and `playwright.config.ts`, as in a web app.
|
|
268
|
+
- The gate, as a web app's («The gate»).
|
|
200
269
|
- The image: as a web app's, with `shopify.app.toml` in its root as well.
|
|
201
270
|
- `deploy/auslieferung.json`: version 2, `art: shopify`, `name`, `subdomain` (without `shopify` or
|
|
202
271
|
`example`, which the review refuses in an address), the migration
|
|
@@ -264,6 +333,69 @@ the baukasten do:
|
|
|
264
333
|
was reached from the logs, and tells the stores concerned by email without undue delay, as the
|
|
265
334
|
privacy text promises.
|
|
266
335
|
|
|
336
|
+
# From 2.x to 3.0
|
|
337
|
+
|
|
338
|
+
3.0 holds the gate of a product: `baukasten-check` fails until the product lints with the config and
|
|
339
|
+
the preset of the baukasten, and its entries export less (below). Its pages, mails, legal texts,
|
|
340
|
+
variables, migrations and stubs stay as they were, and nothing changes when the product runs. A
|
|
341
|
+
product on 2.x keeps running as it is; it takes 3.0 in one change, a web app and a Shopify app
|
|
342
|
+
alike. A product on 1.x goes to 2.0 first («From 1.x to 2.0»).
|
|
343
|
+
|
|
344
|
+
1. Two commands, with a version from 3.0.0 on:
|
|
345
|
+
|
|
346
|
+
```sh
|
|
347
|
+
npm install --save-exact @tollwyn/baukasten@<version>
|
|
348
|
+
npm install --save-dev eslint@10 knip@6 typescript-eslint@8 eslint-plugin-svelte@3 @eslint-community/eslint-plugin-eslint-comments@4 @eslint/js@10 globals@17
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
2. `eslint.config.js` and `knip.config.ts` as «The gate» shows them, with the product's own rules and
|
|
352
|
+
entries on top. What the old config set that the baukasten's sets too goes: the block that
|
|
353
|
+
lints the JavaScript of the root with `disableTypeChecked`, and every rule it turned off. A
|
|
354
|
+
`knip.json` or `knip.jsonc` goes too, its entries and ignores into `knip.config.ts`, where the
|
|
355
|
+
preset does not already name them. The packages only the old configs imported go with them
|
|
356
|
+
(`eslint-config-prettier` and the like): knip names those it finds unused.
|
|
357
|
+
3. The script `gate` runs `svelte-check --tsconfig ./tsconfig.json --fail-on-warnings`,
|
|
358
|
+
`eslint . --config eslint.config.js --max-warnings 0` and `knip`, `tsconfig.json` sets `strict`
|
|
359
|
+
and `noUncheckedIndexedAccess`, and its `include` names `src`, `e2e` and `*.ts`, and no
|
|
360
|
+
JavaScript of the root.
|
|
361
|
+
4. Where the product imports a name the entries no longer export (below), the import goes, and
|
|
362
|
+
svelte-check names each. A type it takes from what uses it, like
|
|
363
|
+
`Parameters<NonNullable<ShopifyHooks['webhook']>>[1]` for the delivery of a webhook. For
|
|
364
|
+
`HYDRATED` it calls `waitForHydration`, for `WEBAPP_VARIABLES` and `SHOPIFY_VARIABLES` it takes
|
|
365
|
+
`BAUKASTEN_VARIABLES` or the standard variables; `LIMITS`, `StripeFailed` and `stripeSignature`
|
|
366
|
+
stay inside the baukasten, which applies the limits, logs a failure of Stripe and signs the
|
|
367
|
+
webhooks of its double itself.
|
|
368
|
+
5. `npx --no-install baukasten-check`, then the gate of the product. What the stricter rules find,
|
|
369
|
+
the product fixes at its cause; where a rule must stay off at one place, the comment says why
|
|
370
|
+
after `--`.
|
|
371
|
+
|
|
372
|
+
The entries export what a sample, this README, a stub or a product imports, the counterpart in
|
|
373
|
+
`/server` or `/shopify` of such a name, and `ApiError`, which `kit.api.respond` turns into its
|
|
374
|
+
answer. They no longer export:
|
|
375
|
+
|
|
376
|
+
- `@tollwyn/baukasten`: `SHOPIFY_VARIABLES`, `WEBAPP_VARIABLES` and the types `Brand`, `Catalog`,
|
|
377
|
+
`CatalogProduct`, `Linked`, `NavigationLink`, `Product`, `ProductEnv`, `PurchasePrice`,
|
|
378
|
+
`ShopifyApp`, `ShopifyPlan`, `ShopifyPrivacy`, `ShopifyStandardVariable`, `StandardVariable`,
|
|
379
|
+
`SubscriptionPrice` and `Texts`.
|
|
380
|
+
- `/server`: `LIMITS`, `StripeFailed` and the types `Account`, `ApiKey`, `CheckoutPage`, `Config`,
|
|
381
|
+
`Limits`, `MailConfig`, `NodeHandler`, `OpenApiDocument`, `Operator`, `Order`, `Pages`,
|
|
382
|
+
`ProductNumbers`, `Purchase`, `Register`, `Rule`, `Run`, `RunOptions`, `Stripe`, `Subscription`,
|
|
383
|
+
`SubscriptionChange` and `SubscriptionStatus`.
|
|
384
|
+
- `/shopify`: `LIMITS` and the types `CustomerRequest`, `Delivery`, `Limits`, `NodeHandler`, `Plan`,
|
|
385
|
+
`Rule`, `Run`, `RunOptions`, `ShopifyConfig` and `ShopSession`.
|
|
386
|
+
- `/testing`: `HYDRATED`, `stripeSignature` and the types `AppProcess`, `DoubleEndpoint`,
|
|
387
|
+
`DoublePrice`, `DoubleSession`, `DoubleShop`, `DoubleSubscription`, `PageLike` and
|
|
388
|
+
`ReceivedMail`.
|
|
389
|
+
- `/ui`: the types of the data of its pages, `AccountData`, `AccountForm`, `ConfirmData`,
|
|
390
|
+
`ConfirmForm`, `ForgotForm`, `FrameData`, `LegalBlock`, `LegalData`, `LegalInline`, `LoginData`,
|
|
391
|
+
`LoginForm`, `LogoutData`, `ResetData`, `ResetForm`, `SignupData`, `SignupForm`, `VerifyData` and
|
|
392
|
+
`VerifyForm`.
|
|
393
|
+
|
|
394
|
+
What changes in the package: the entries `/eslint` and `/knip`, and `baukasten-check` reads
|
|
395
|
+
`eslint.config.js`, `knip.config.ts`, `tsconfig.json`, `vite.config.ts` for filtered warnings and
|
|
396
|
+
the scripts of `package.json`, and wants ESLint 10, knip 6 and the plugins of the config in the
|
|
397
|
+
versions it names.
|
|
398
|
+
|
|
267
399
|
# From 1.x to 2.0
|
|
268
400
|
|
|
269
401
|
2.0 moves the baukasten to SvelteKit 3 and adapter-node 6, and to version 23 of the SDK of Stripe.
|
package/dist/check.d.ts
CHANGED
|
@@ -12,8 +12,6 @@ export interface ProductFiles {
|
|
|
12
12
|
/** The names in a folder, as the migration reads them; none where there is no folder. */
|
|
13
13
|
names(folder: string): readonly string[];
|
|
14
14
|
}
|
|
15
|
-
/** The migration command of a Shopify app in its delivery manifest. */
|
|
16
|
-
export declare const SHOPIFY_MIGRATION: string[];
|
|
17
15
|
/** Whether `version` lies in the caret range `range` from 1.0.0 on; a pre-release lies in none. */
|
|
18
16
|
export declare function fits(version: string, range: string): boolean;
|
|
19
17
|
/** What keeps the product from fitting the installed baukasten; empty when it fits. */
|
package/dist/check.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { GATE_SCRIPT, HELD_RULES, KNIP_COMMAND, LINT_COMMAND, LINT_PLUGINS, LINT_TOOLS, TYPECHECK_COMMAND } from './gate.js';
|
|
2
3
|
import { KEY_RIGHTS, WEBHOOK_EVENTS, WEBHOOK_PATH } from './server/stripe.js';
|
|
3
4
|
import { APP_TOML, appTomlProblems } from './shopify/toml.js';
|
|
4
5
|
import { SHOPIFY_STUBS, STUBS } from './stubs.js';
|
|
5
6
|
const ANGABE_PATH = 'deploy/auslieferung.json';
|
|
6
7
|
const STUBS_COMMAND = 'npx --no-install baukasten-stubs';
|
|
8
|
+
const ESLINT_CONFIG = 'eslint.config.js';
|
|
9
|
+
const ESLINT_ENTRY = '@tollwyn/baukasten/eslint';
|
|
10
|
+
const ESLINT_USE = `import baukasten from '${ESLINT_ENTRY}', then export default [...baukasten, …]`;
|
|
11
|
+
const KNIP_CONFIG = 'knip.config.ts';
|
|
12
|
+
const KNIP_ENTRY = '@tollwyn/baukasten/knip';
|
|
13
|
+
const KNIP_USE = `import baukasten from '${KNIP_ENTRY}', then export default { ...baukasten, … }`;
|
|
7
14
|
const ANGABE = z.looseObject({
|
|
8
15
|
version: z.literal(2, 'must be 2, the version that names the art of the product'),
|
|
9
16
|
art: z.literal('webapp', 'must be webapp'),
|
|
@@ -20,7 +27,7 @@ const ANGABE = z.looseObject({
|
|
|
20
27
|
})
|
|
21
28
|
});
|
|
22
29
|
/** The migration command of a Shopify app in its delivery manifest. */
|
|
23
|
-
|
|
30
|
+
const SHOPIFY_MIGRATION = [
|
|
24
31
|
'node',
|
|
25
32
|
'node_modules/@tollwyn/baukasten/dist/bin/migrate.js',
|
|
26
33
|
'shopify'
|
|
@@ -36,17 +43,25 @@ const REFUSED_IN_ADDRESS = ['shopify', 'example'];
|
|
|
36
43
|
const ART = z.looseObject({ art: z.literal('shopify') });
|
|
37
44
|
const PACKAGE = z.looseObject({
|
|
38
45
|
overrides: z.record(z.string(), z.unknown()).optional(),
|
|
39
|
-
imports: z.record(z.string(), z.unknown()).optional()
|
|
46
|
+
imports: z.record(z.string(), z.unknown()).optional(),
|
|
47
|
+
scripts: z.record(z.string(), z.string()).optional(),
|
|
48
|
+
knip: z.unknown().optional()
|
|
40
49
|
});
|
|
41
50
|
/** The alias the stubs import the product's src/lib by, as SvelteKit sets it up. */
|
|
42
51
|
const LIB_IMPORT = { key: '#lib/*', target: './src/lib/*' };
|
|
43
52
|
const PEER = z.looseObject({ version: z.string() });
|
|
44
53
|
/**
|
|
45
|
-
* What the product builds with, checked like a peer: adapter-node 5 cannot build SvelteKit
|
|
46
|
-
* handler the stub server.js wraps, with its event at shutdown, is that of one major
|
|
47
|
-
*
|
|
54
|
+
* What the product builds and lints with, checked like a peer: adapter-node 5 cannot build SvelteKit
|
|
55
|
+
* 3, and the handler the stub server.js wraps, with its event at shutdown, is that of one major; the
|
|
56
|
+
* config of /eslint and the preset of /knip are written for one major of each tool and plugin. None
|
|
57
|
+
* is a peer, since only the build and the gate need them, and npm keeps a peer of a dependency in the
|
|
58
|
+
* image.
|
|
48
59
|
*/
|
|
49
|
-
const BUILT_WITH = {
|
|
60
|
+
const BUILT_WITH = {
|
|
61
|
+
'@sveltejs/adapter-node': '^6.0.0',
|
|
62
|
+
...LINT_TOOLS,
|
|
63
|
+
...LINT_PLUGINS
|
|
64
|
+
};
|
|
50
65
|
/**
|
|
51
66
|
* A reference to an account of a web app or a shop of a Shopify app: the one way a migration of the
|
|
52
67
|
* product names the schema baukasten.
|
|
@@ -217,7 +232,7 @@ function packageProblems(files, baukasten) {
|
|
|
217
232
|
const parsed = json(text);
|
|
218
233
|
const manifest = parsed.kind === 'json' ? PACKAGE.safeParse(parsed.value) : null;
|
|
219
234
|
if (manifest === null || !manifest.success) {
|
|
220
|
-
return ['package.json is no JSON object, or its overrides or
|
|
235
|
+
return ['package.json is no JSON object, or its overrides, imports or scripts are no object'];
|
|
221
236
|
}
|
|
222
237
|
const own = new Set([
|
|
223
238
|
baukasten.name,
|
|
@@ -229,8 +244,135 @@ function packageProblems(files, baukasten) {
|
|
|
229
244
|
if (manifest.data.imports?.[LIB_IMPORT.key] !== LIB_IMPORT.target) {
|
|
230
245
|
problems.push(`package.json does not map imports "${LIB_IMPORT.key}" to "${LIB_IMPORT.target}", which the stubs import from`);
|
|
231
246
|
}
|
|
247
|
+
if (manifest.data.knip !== undefined) {
|
|
248
|
+
problems.push(`package.json sets knip, which knip adds to ${KNIP_CONFIG}: move it into ${KNIP_CONFIG}`);
|
|
249
|
+
}
|
|
250
|
+
return [...problems, ...gateProblems(manifest.data.scripts ?? {})];
|
|
251
|
+
}
|
|
252
|
+
/** What `npm run gate` must run, read from the scripts, which the workflow of the product runs. */
|
|
253
|
+
function gateProblems(scripts) {
|
|
254
|
+
const commands = gateCommands(scripts, GATE_SCRIPT, new Set());
|
|
255
|
+
return [LINT_COMMAND, KNIP_COMMAND, TYPECHECK_COMMAND]
|
|
256
|
+
.filter((command) => !commands.includes(command))
|
|
257
|
+
.map((command) => `npm run ${GATE_SCRIPT} does not run ${command}: the script ${GATE_SCRIPT} runs it, or a script it runs with npm run, among commands joined by && and nothing else`);
|
|
258
|
+
}
|
|
259
|
+
/** The script a command runs, where running it with npm is all the command does. */
|
|
260
|
+
function scriptRun(command) {
|
|
261
|
+
return command === 'npm test' ? 'test' : /^npm run (\S+)$/.exec(command)?.[1];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* The commands a script runs, in it and in the scripts it runs with npm, each with its words joined
|
|
265
|
+
* by one space. Only a script of commands joined by && counts: any other operator could let a failure
|
|
266
|
+
* pass, wherever it stands (`a && b || true` is `(a && b) || true`).
|
|
267
|
+
*/
|
|
268
|
+
function gateCommands(scripts, name, seen) {
|
|
269
|
+
const script = scripts[name];
|
|
270
|
+
if (script === undefined || seen.has(name) || /[;&|\n]/.test(script.replaceAll('&&', ''))) {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
seen.add(name);
|
|
274
|
+
return script.split('&&').flatMap((part) => {
|
|
275
|
+
const command = part.trim().split(/\s+/).join(' ');
|
|
276
|
+
const run = scriptRun(command);
|
|
277
|
+
return run === undefined ? [command] : gateCommands(scripts, run, seen);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/** Strings as they are, comments gone: what JavaScript, TypeScript or tsconfig.json says. */
|
|
281
|
+
const STRINGS_OR_COMMENTS = /'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`|\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
|
|
282
|
+
const withoutComments = (text) => text.replace(STRINGS_OR_COMMENTS, (match) => (match.startsWith('/') ? '' : match));
|
|
283
|
+
/** Whether the code imports the default export of `entry` and spreads it whole, not a part of it. */
|
|
284
|
+
function spreads(code, entry) {
|
|
285
|
+
const name = new RegExp(String.raw `\bimport\s+([\w$]+)\s+from\s*['"]${entry}['"]`).exec(code)?.[1];
|
|
286
|
+
return (name !== undefined &&
|
|
287
|
+
new RegExp(String.raw `\.\.\.\s*${name.replaceAll('$', '\\$')}\s*[,)\]}]`).test(code));
|
|
288
|
+
}
|
|
289
|
+
/** A rule turned off: the severity `'off'`, or `0` for a rule by its name in quotes. */
|
|
290
|
+
const TURNED_OFF = /(['"])off\1|['"][^'"\n]+['"]\s*:\s*\[?\s*0\s*[,\]}]/;
|
|
291
|
+
/**
|
|
292
|
+
* Read from the text of eslint.config.js only: it spreads the config of the baukasten, turns no rule
|
|
293
|
+
* off, and names neither a rule it holds, whose options could weaken it, nor the config of
|
|
294
|
+
* typescript-eslint that turns off the rules needing types.
|
|
295
|
+
*/
|
|
296
|
+
function eslintProblems(files) {
|
|
297
|
+
const text = files.read(ESLINT_CONFIG);
|
|
298
|
+
if (text === undefined)
|
|
299
|
+
return [`${ESLINT_CONFIG} is missing: ${ESLINT_USE}`];
|
|
300
|
+
const code = withoutComments(text);
|
|
301
|
+
const problems = spreads(code, ESLINT_ENTRY)
|
|
302
|
+
? []
|
|
303
|
+
: [`${ESLINT_CONFIG} does not spread the config of ${ESLINT_ENTRY}: ${ESLINT_USE}`];
|
|
304
|
+
for (const rule of HELD_RULES.filter((r) => code.includes(r))) {
|
|
305
|
+
problems.push(`${ESLINT_CONFIG} names ${rule}, which the config of ${ESLINT_ENTRY} holds: a product adds rules on top and changes none of these`);
|
|
306
|
+
}
|
|
307
|
+
if (TURNED_OFF.test(code)) {
|
|
308
|
+
problems.push(`${ESLINT_CONFIG} turns a rule off: a product adds rules on top and turns none off; where one must stay off at a place, the comment there says why after --`);
|
|
309
|
+
}
|
|
310
|
+
if (/\bdisableTypeChecked\b/.test(code)) {
|
|
311
|
+
problems.push(`${ESLINT_CONFIG} names disableTypeChecked, which turns off the rules of ${ESLINT_ENTRY} that need types: its config gives every file types, the JavaScript in the root too`);
|
|
312
|
+
}
|
|
232
313
|
return problems;
|
|
233
314
|
}
|
|
315
|
+
/** Where knip looks for its configuration before knip.config.ts, in its order. */
|
|
316
|
+
const KNIP_FIRST = ['knip.json', 'knip.jsonc', '.knip.json', '.knip.jsonc', 'knip.ts', 'knip.js'];
|
|
317
|
+
/** What turns off a kind of issue of knip in its configuration. */
|
|
318
|
+
const KNIP_SWITCHES = ['rules', 'include', 'exclude'];
|
|
319
|
+
/** Read from the text of knip.config.ts only, as from eslint.config.js. */
|
|
320
|
+
function knipProblems(files) {
|
|
321
|
+
const text = files.read(KNIP_CONFIG);
|
|
322
|
+
const problems = [];
|
|
323
|
+
if (text === undefined) {
|
|
324
|
+
problems.push(`${KNIP_CONFIG} is missing: ${KNIP_USE}`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const code = withoutComments(text);
|
|
328
|
+
if (!spreads(code, KNIP_ENTRY)) {
|
|
329
|
+
problems.push(`${KNIP_CONFIG} does not spread the preset of ${KNIP_ENTRY}: ${KNIP_USE}`);
|
|
330
|
+
}
|
|
331
|
+
for (const key of KNIP_SWITCHES.filter((k) => new RegExp(String.raw `(?:^|[\s{,])['"]?${k}['"]?\s*:`).test(code))) {
|
|
332
|
+
problems.push(`${KNIP_CONFIG} sets ${key}, which switches off what the preset of ${KNIP_ENTRY} finds, unused files, exports and dependencies: remove it`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
for (const first of KNIP_FIRST.filter((f) => files.read(f) !== undefined)) {
|
|
336
|
+
problems.push(`${first} takes the place of ${KNIP_CONFIG} for knip: remove it`);
|
|
337
|
+
}
|
|
338
|
+
return problems;
|
|
339
|
+
}
|
|
340
|
+
const TSCONFIG = z.looseObject({ compilerOptions: z.record(z.string(), z.unknown()).optional() });
|
|
341
|
+
/** What `strict` turns on, each of which a product could turn off again beside it. */
|
|
342
|
+
const STRICT_FAMILY = [
|
|
343
|
+
'alwaysStrict',
|
|
344
|
+
'noImplicitAny',
|
|
345
|
+
'noImplicitThis',
|
|
346
|
+
'strictBindCallApply',
|
|
347
|
+
'strictBuiltinIteratorReturn',
|
|
348
|
+
'strictFunctionTypes',
|
|
349
|
+
'strictNullChecks',
|
|
350
|
+
'strictPropertyInitialization',
|
|
351
|
+
'useUnknownInCatchVariables'
|
|
352
|
+
];
|
|
353
|
+
/**
|
|
354
|
+
* Read from tsconfig.json itself, not from what it extends: the product sets both there, where its
|
|
355
|
+
* reader sees them.
|
|
356
|
+
*/
|
|
357
|
+
function tsconfigProblems(files) {
|
|
358
|
+
const text = files.read('tsconfig.json');
|
|
359
|
+
if (text === undefined)
|
|
360
|
+
return ['tsconfig.json is missing'];
|
|
361
|
+
// tsconfig.json may end an object or array with a comma, which JSON does not take.
|
|
362
|
+
const plain = withoutComments(text).replace(/"(?:[^"\\]|\\.)*"|,(?=\s*[}\]])/g, (match) => match === ',' ? '' : match);
|
|
363
|
+
const parsed = json(plain);
|
|
364
|
+
const tsconfig = parsed.kind === 'json' ? TSCONFIG.safeParse(parsed.value) : null;
|
|
365
|
+
if (tsconfig === null || !tsconfig.success) {
|
|
366
|
+
return ['tsconfig.json is no JSON object, or its compilerOptions are no object'];
|
|
367
|
+
}
|
|
368
|
+
const options = tsconfig.data.compilerOptions ?? {};
|
|
369
|
+
return [
|
|
370
|
+
...['strict', 'noUncheckedIndexedAccess']
|
|
371
|
+
.filter((option) => options[option] !== true)
|
|
372
|
+
.map((option) => `tsconfig.json does not set compilerOptions.${option} to true`),
|
|
373
|
+
...STRICT_FAMILY.filter((option) => options[option] === false).map((option) => `tsconfig.json sets compilerOptions.${option} to false, which strict turns on`)
|
|
374
|
+
];
|
|
375
|
+
}
|
|
234
376
|
function migrationProblems(files, art) {
|
|
235
377
|
// Like the migration: every entry read, a folder failing loudly, then the .sql files.
|
|
236
378
|
return files
|
|
@@ -280,6 +422,19 @@ function configurationProblems(files, art) {
|
|
|
280
422
|
}
|
|
281
423
|
return problems;
|
|
282
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* svelte-check compiles the components with the compilerOptions the product passes to sveltekit() in
|
|
427
|
+
* vite.config.ts (SvelteKit 3 refuses a svelte.config.*), and a warningFilter among them hides the
|
|
428
|
+
* warnings of Svelte before --fail-on-warnings sees them.
|
|
429
|
+
*/
|
|
430
|
+
function svelteWarningProblems(files) {
|
|
431
|
+
const vite = files.read('vite.config.ts');
|
|
432
|
+
return vite !== undefined && /\bwarningFilter\b/.test(withoutComments(vite))
|
|
433
|
+
? [
|
|
434
|
+
'vite.config.ts sets warningFilter, which hides warnings of Svelte from svelte-check --fail-on-warnings: fix what they say, or silence one at its place with a svelte-ignore comment'
|
|
435
|
+
]
|
|
436
|
+
: [];
|
|
437
|
+
}
|
|
283
438
|
/** What keeps the product from fitting the installed baukasten; empty when it fits. */
|
|
284
439
|
export function productProblems(files, baukasten) {
|
|
285
440
|
const art = artOf(files);
|
|
@@ -289,6 +444,10 @@ export function productProblems(files, baukasten) {
|
|
|
289
444
|
...versionProblems(files, baukasten),
|
|
290
445
|
...packageProblems(files, baukasten),
|
|
291
446
|
...migrationProblems(files, art),
|
|
292
|
-
...configurationProblems(files, art)
|
|
447
|
+
...configurationProblems(files, art),
|
|
448
|
+
...svelteWarningProblems(files),
|
|
449
|
+
...eslintProblems(files),
|
|
450
|
+
...knipProblems(files),
|
|
451
|
+
...tsconfigProblems(files)
|
|
293
452
|
];
|
|
294
453
|
}
|
package/dist/eslint.d.ts
ADDED
package/dist/eslint.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// The ESLint config of every product, which its eslint.config.js spreads first and adds its own rules
|
|
2
|
+
// to (README, «The gate»). The plugins come from the dev dependencies of the product (gate.ts says
|
|
3
|
+
// why); baukasten-check holds their versions.
|
|
4
|
+
import js from '@eslint/js';
|
|
5
|
+
import comments from '@eslint-community/eslint-plugin-eslint-comments/configs';
|
|
6
|
+
import { defineConfig } from 'eslint/config';
|
|
7
|
+
import svelte from 'eslint-plugin-svelte';
|
|
8
|
+
import globals from 'globals';
|
|
9
|
+
import tseslint from 'typescript-eslint';
|
|
10
|
+
import { HELD_RULES } from './gate.js';
|
|
11
|
+
/**
|
|
12
|
+
* What reaches the browser: the components, the load functions, hooks and matchers that run on both
|
|
13
|
+
* sides, and the service worker. A file of the server, a test or src/lib/server/ is none of them.
|
|
14
|
+
*/
|
|
15
|
+
const BROWSER_FILES = [
|
|
16
|
+
'**/*.svelte',
|
|
17
|
+
'**/*.svelte.{ts,js}',
|
|
18
|
+
'src/routes/**/+{page,layout}.{ts,js}',
|
|
19
|
+
'src/hooks.{ts,js}',
|
|
20
|
+
'src/hooks.client.{ts,js}',
|
|
21
|
+
'src/params/*.{ts,js}',
|
|
22
|
+
'src/service-worker.{ts,js}',
|
|
23
|
+
'src/service-worker/**'
|
|
24
|
+
];
|
|
25
|
+
/** The entries of the package for the server, which fail the client build (entries.test.ts). */
|
|
26
|
+
const SERVER_ENTRIES = ['server', 'shopify', 'testing'].map((entry) => ({
|
|
27
|
+
name: `@tollwyn/baukasten/${entry}`,
|
|
28
|
+
message: 'it runs on the server only: import it in a +page.server.ts, +layout.server.ts, +server.ts, src/hooks.server.ts or src/lib/server/',
|
|
29
|
+
allowTypeImports: true
|
|
30
|
+
}));
|
|
31
|
+
export default defineConfig({ ignores: ['.svelte-kit/', 'build/', 'coverage/', 'test-results/', 'playwright-report/'] }, js.configs.recommended, tseslint.configs.recommendedTypeChecked, svelte.configs.recommended, comments.recommended, {
|
|
32
|
+
languageOptions: {
|
|
33
|
+
parserOptions: {
|
|
34
|
+
// The stub server.js and the configuration files in the root lie in no tsconfig: they get
|
|
35
|
+
// the options of the product's own, so that every rule, the product's too, has types.
|
|
36
|
+
projectService: {
|
|
37
|
+
allowDefaultProject: ['*.js', '*.mjs', '*.cjs'],
|
|
38
|
+
defaultProject: 'tsconfig.json'
|
|
39
|
+
},
|
|
40
|
+
extraFileExtensions: ['.svelte']
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
rules: Object.fromEntries(HELD_RULES.map((rule) => [rule, 'error']))
|
|
44
|
+
},
|
|
45
|
+
// A product runs at the root of its own host without a base path, so its links are plain paths and
|
|
46
|
+
// need no resolve(); they carry the referral tag through useLink of /ui.
|
|
47
|
+
{
|
|
48
|
+
files: ['**/*.svelte', '**/*.svelte.{ts,js}'],
|
|
49
|
+
languageOptions: {
|
|
50
|
+
parserOptions: { parser: tseslint.parser, svelteFeatures: { runes: true } }
|
|
51
|
+
},
|
|
52
|
+
rules: { 'svelte/no-navigation-without-resolve': 'off' }
|
|
53
|
+
},
|
|
54
|
+
// The globals no-undef knows: components run in the browser, and any other JavaScript, the stub
|
|
55
|
+
// server.js and the configuration files among it, in Node or there. TypeScript knows those of its
|
|
56
|
+
// own files, and typescript-eslint turns no-undef off for them.
|
|
57
|
+
{ files: ['**/*.svelte'], languageOptions: { globals: globals.browser } }, {
|
|
58
|
+
files: ['**/*.{js,mjs,cjs}'],
|
|
59
|
+
languageOptions: { globals: { ...globals.node, ...globals.browser } }
|
|
60
|
+
}, {
|
|
61
|
+
files: BROWSER_FILES,
|
|
62
|
+
ignores: ['**/*.test.ts', '**/*.spec.ts'],
|
|
63
|
+
rules: { '@typescript-eslint/no-restricted-imports': ['error', { paths: SERVER_ENTRIES }] }
|
|
64
|
+
});
|
package/dist/gate.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** The script the workflow of the product runs as its gate. */
|
|
2
|
+
export declare const GATE_SCRIPT = "gate";
|
|
3
|
+
/**
|
|
4
|
+
* Lint, each warning failing it; a product writes it exactly so, with no flag that weakens it. With
|
|
5
|
+
* --config, ESLint looks for no config from the folder of each file, where one in a folder below
|
|
6
|
+
* would take the place of the product's.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LINT_COMMAND = "eslint . --config eslint.config.js --max-warnings 0";
|
|
9
|
+
/** knip with knip.config.ts, the only configuration it reads in a product. */
|
|
10
|
+
export declare const KNIP_COMMAND = "knip";
|
|
11
|
+
/**
|
|
12
|
+
* The type check, each warning failing it; without --tsconfig, svelte-check reports nothing of the
|
|
13
|
+
* TypeScript files.
|
|
14
|
+
*/
|
|
15
|
+
export declare const TYPECHECK_COMMAND = "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings";
|
|
16
|
+
/**
|
|
17
|
+
* Rules of the config of /eslint that a product's eslint.config.js never names, and so never turns
|
|
18
|
+
* off or weakens by an option: a promise nobody awaits loses its error, a disable without its reason
|
|
19
|
+
* hides why, and `@ts-nocheck` or `@ts-ignore` take a file or a line out of the type check.
|
|
20
|
+
*/
|
|
21
|
+
export declare const HELD_RULES: readonly ["@typescript-eslint/no-floating-promises", "@typescript-eslint/no-misused-promises", "@typescript-eslint/ban-ts-comment", "@eslint-community/eslint-comments/require-description"];
|
|
22
|
+
/**
|
|
23
|
+
* What the config of /eslint imports, with the ranges it is written for. The product installs them
|
|
24
|
+
* as dev dependencies and resolves them for the package: as dependencies or peers of the package,
|
|
25
|
+
* npm would put them into the image of every product.
|
|
26
|
+
*/
|
|
27
|
+
export declare const LINT_PLUGINS: Readonly<Record<string, string>>;
|
|
28
|
+
/**
|
|
29
|
+
* The tools the config of /eslint and the preset of /knip are written for, from the dev dependencies
|
|
30
|
+
* of the product, with their ranges.
|
|
31
|
+
*/
|
|
32
|
+
export declare const LINT_TOOLS: Readonly<Record<string, string>>;
|
package/dist/gate.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// What the baukasten holds of the gate of a product: the commands its `npm run gate` runs, the rules
|
|
2
|
+
// of the config of /eslint a product never changes, and what the product lints with. Pure, so that
|
|
3
|
+
// baukasten-check reads it without loading ESLint.
|
|
4
|
+
/** The script the workflow of the product runs as its gate. */
|
|
5
|
+
export const GATE_SCRIPT = 'gate';
|
|
6
|
+
/**
|
|
7
|
+
* Lint, each warning failing it; a product writes it exactly so, with no flag that weakens it. With
|
|
8
|
+
* --config, ESLint looks for no config from the folder of each file, where one in a folder below
|
|
9
|
+
* would take the place of the product's.
|
|
10
|
+
*/
|
|
11
|
+
export const LINT_COMMAND = 'eslint . --config eslint.config.js --max-warnings 0';
|
|
12
|
+
/** knip with knip.config.ts, the only configuration it reads in a product. */
|
|
13
|
+
export const KNIP_COMMAND = 'knip';
|
|
14
|
+
/**
|
|
15
|
+
* The type check, each warning failing it; without --tsconfig, svelte-check reports nothing of the
|
|
16
|
+
* TypeScript files.
|
|
17
|
+
*/
|
|
18
|
+
export const TYPECHECK_COMMAND = 'svelte-check --tsconfig ./tsconfig.json --fail-on-warnings';
|
|
19
|
+
/**
|
|
20
|
+
* Rules of the config of /eslint that a product's eslint.config.js never names, and so never turns
|
|
21
|
+
* off or weakens by an option: a promise nobody awaits loses its error, a disable without its reason
|
|
22
|
+
* hides why, and `@ts-nocheck` or `@ts-ignore` take a file or a line out of the type check.
|
|
23
|
+
*/
|
|
24
|
+
export const HELD_RULES = [
|
|
25
|
+
'@typescript-eslint/no-floating-promises',
|
|
26
|
+
'@typescript-eslint/no-misused-promises',
|
|
27
|
+
'@typescript-eslint/ban-ts-comment',
|
|
28
|
+
'@eslint-community/eslint-comments/require-description'
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* What the config of /eslint imports, with the ranges it is written for. The product installs them
|
|
32
|
+
* as dev dependencies and resolves them for the package: as dependencies or peers of the package,
|
|
33
|
+
* npm would put them into the image of every product.
|
|
34
|
+
*/
|
|
35
|
+
export const LINT_PLUGINS = {
|
|
36
|
+
'@eslint/js': '^10.0.1',
|
|
37
|
+
'@eslint-community/eslint-plugin-eslint-comments': '^4.8.1',
|
|
38
|
+
'eslint-plugin-svelte': '^3.23.1',
|
|
39
|
+
globals: '^17.13.0',
|
|
40
|
+
'typescript-eslint': '^8.71.2'
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The tools the config of /eslint and the preset of /knip are written for, from the dev dependencies
|
|
44
|
+
* of the product, with their ranges.
|
|
45
|
+
*/
|
|
46
|
+
export const LINT_TOOLS = {
|
|
47
|
+
eslint: '^10.12.0',
|
|
48
|
+
knip: '^6.41.0'
|
|
49
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { z } from 'zod';
|
|
2
|
-
export { defineProduct, LANGUAGES, type
|
|
3
|
-
export { defineShopifyApp
|
|
2
|
+
export { defineProduct, LANGUAGES, type Language, type ProductLegal } from './product.js';
|
|
3
|
+
export { defineShopifyApp } from './shopify/app.js';
|
|
4
4
|
export { SHOPIFY_SVELTEKIT_OPTIONS } from './shopify/sveltekit.js';
|
|
5
5
|
export { SVELTEKIT_OPTIONS } from './sveltekit.js';
|
|
6
|
-
export { TEXTS
|
|
7
|
-
export { BAUKASTEN_VARIABLES, SHOPIFY_STANDARD_VARIABLES,
|
|
6
|
+
export { TEXTS } from './texts.js';
|
|
7
|
+
export { BAUKASTEN_VARIABLES, SHOPIFY_STANDARD_VARIABLES, STANDARD_VARIABLES } from './variables.js';
|
package/dist/index.js
CHANGED
|
@@ -6,4 +6,4 @@ export { defineShopifyApp } from './shopify/app.js';
|
|
|
6
6
|
export { SHOPIFY_SVELTEKIT_OPTIONS } from './shopify/sveltekit.js';
|
|
7
7
|
export { SVELTEKIT_OPTIONS } from './sveltekit.js';
|
|
8
8
|
export { TEXTS } from './texts.js';
|
|
9
|
-
export { BAUKASTEN_VARIABLES, SHOPIFY_STANDARD_VARIABLES,
|
|
9
|
+
export { BAUKASTEN_VARIABLES, SHOPIFY_STANDARD_VARIABLES, STANDARD_VARIABLES } from './variables.js';
|
package/dist/knip.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The part of the configuration of knip that the preset sets. */
|
|
2
|
+
export interface KnipPreset {
|
|
3
|
+
entry: string[];
|
|
4
|
+
sveltekit: {
|
|
5
|
+
entry: string[];
|
|
6
|
+
};
|
|
7
|
+
playwright: {
|
|
8
|
+
config: string[];
|
|
9
|
+
entry: string[];
|
|
10
|
+
};
|
|
11
|
+
ignoreDependencies: string[];
|
|
12
|
+
}
|
|
13
|
+
declare const preset: KnipPreset;
|
|
14
|
+
export default preset;
|
package/dist/knip.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The knip preset of every product, which its knip.config.ts spreads and adds to (README, «The
|
|
2
|
+
// gate»). knip finds the routes, the hooks of server and client and the matchers through its plugin
|
|
3
|
+
// for SvelteKit, the stub server.js as the file Node starts, and the tests through Vitest; this names
|
|
4
|
+
// what only the baukasten knows, and what the plugin misses.
|
|
5
|
+
import { LINT_PLUGINS } from './gate.js';
|
|
6
|
+
const preset = {
|
|
7
|
+
// The baukasten loads it, in the migration command and the connection test.
|
|
8
|
+
entry: ['baukasten.config.ts'],
|
|
9
|
+
// SvelteKit loads the universal hooks, which the plugin does not name. Here, not under entry, a
|
|
10
|
+
// product without them gets no hint that the pattern finds nothing; the plugin still reads
|
|
11
|
+
// vite.config.ts for the rest.
|
|
12
|
+
sveltekit: { entry: ['src/hooks.{js,ts}'] },
|
|
13
|
+
// Read, not run: the configuration of Playwright may need the test database to load. Its tests lie
|
|
14
|
+
// in e2e/, the connection test of the baukasten among them, as Playwright finds them there. What
|
|
15
|
+
// else the configuration loads (globalSetup, a webServer, a reporter of its own) the product adds
|
|
16
|
+
// here itself.
|
|
17
|
+
playwright: {
|
|
18
|
+
config: [],
|
|
19
|
+
entry: ['playwright.config.ts', 'e2e/**/*.@(spec|test).?(c|m)[jt]s?(x)']
|
|
20
|
+
},
|
|
21
|
+
// What the baukasten imports from the product: the plugins of its ESLint config, and the server of
|
|
22
|
+
// the SMTP double.
|
|
23
|
+
ignoreDependencies: [...Object.keys(LINT_PLUGINS), 'smtp-server']
|
|
24
|
+
};
|
|
25
|
+
export default preset;
|
package/dist/product.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface NavigationLink {
|
|
|
9
9
|
label: string;
|
|
10
10
|
}
|
|
11
11
|
/** The grounds on which a product may process personal data, as the privacy policy names them. */
|
|
12
|
-
|
|
12
|
+
declare const LEGAL_BASES: readonly ["contract", "legitimateInterest"];
|
|
13
13
|
export type LegalBasis = (typeof LEGAL_BASES)[number];
|
|
14
14
|
/**
|
|
15
15
|
* What covers personal data that go to a recipient: a country with adequate protection (only
|
|
@@ -77,14 +77,14 @@ export interface ProductLegal {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
/** A price of a subscription: an amount in the smallest unit of the currency, per month or year. */
|
|
80
|
-
|
|
80
|
+
interface SubscriptionPrice {
|
|
81
81
|
/** The price at Stripe has this lookup key; a new amount becomes a new price under the same key. */
|
|
82
82
|
lookupKey: string;
|
|
83
83
|
amount: number;
|
|
84
84
|
interval: 'month' | 'year';
|
|
85
85
|
}
|
|
86
86
|
/** A price paid once, in the smallest unit of the currency. */
|
|
87
|
-
|
|
87
|
+
interface PurchasePrice {
|
|
88
88
|
lookupKey: string;
|
|
89
89
|
amount: number;
|
|
90
90
|
}
|
|
@@ -159,3 +159,4 @@ export interface Brand {
|
|
|
159
159
|
export declare function isProduct(value: unknown): value is Product;
|
|
160
160
|
/** Declares the configuration of the product; anything wrong with it stops the product loudly. */
|
|
161
161
|
export declare function defineProduct<const E extends ProductEnv>(product: Product<E>): Product<E>;
|
|
162
|
+
export {};
|
package/dist/product.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { BAUKASTEN_VARIABLES } from './variables.js';
|
|
3
3
|
export const LANGUAGES = ['en', 'de'];
|
|
4
4
|
/** The grounds on which a product may process personal data, as the privacy policy names them. */
|
|
5
|
-
|
|
5
|
+
const LEGAL_BASES = ['contract', 'legitimateInterest'];
|
|
6
6
|
/**
|
|
7
7
|
* What covers personal data that go to a recipient: a country with adequate protection (only
|
|
8
8
|
* Switzerland, the EEA and the countries in Annex 1 of the Swiss Data Protection Ordinance, DSV), the
|
|
@@ -8,7 +8,7 @@ export interface MigrationSources {
|
|
|
8
8
|
/** The folder of the product's migrations; a product without one has none. */
|
|
9
9
|
product: string;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
type ByLayer = Record<Layer, string[]>;
|
|
12
12
|
/** What a run applied, and what the database had ahead of this version. */
|
|
13
13
|
export interface MigrateResult {
|
|
14
14
|
applied: ByLayer;
|
|
@@ -19,3 +19,4 @@ export declare function migrationSources(root: string, art: Art): MigrationSourc
|
|
|
19
19
|
/** What the database still needs of this version and what forbids migrating it, for /health/deep. */
|
|
20
20
|
export declare function migrationStatus(db: Queryable, sources: MigrationSources): Promise<MigrationStatus>;
|
|
21
21
|
export declare function migrate(databaseUrl: string, sources: MigrationSources): Promise<MigrateResult>;
|
|
22
|
+
export {};
|
package/dist/server/billing.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare class StripeFailed extends Error {
|
|
|
11
11
|
constructor(kind: string, message: string, options?: ErrorOptions);
|
|
12
12
|
}
|
|
13
13
|
/** A subscription as the product gets it: its product and the lookup key of its price. */
|
|
14
|
-
|
|
14
|
+
interface Subscription {
|
|
15
15
|
id: string;
|
|
16
16
|
status: SubscriptionStatus;
|
|
17
17
|
product: string;
|
|
@@ -57,7 +57,7 @@ export type CheckoutPage = {
|
|
|
57
57
|
} | {
|
|
58
58
|
kind: 'subscribed';
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
type Received = {
|
|
61
61
|
kind: 'invalid';
|
|
62
62
|
} | {
|
|
63
63
|
kind: 'applied';
|
|
@@ -92,3 +92,4 @@ export declare function createBilling(options: {
|
|
|
92
92
|
/** The sentence by the box for the terms in Checkout, in Markdown, with their link. */
|
|
93
93
|
termsMessage: (termsUrl: string) => string;
|
|
94
94
|
}): Billing;
|
|
95
|
+
export {};
|
package/dist/server/catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Catalog } from '../product.js';
|
|
2
|
-
import type { NewPrice,
|
|
2
|
+
import type { NewPrice, StripeApi, StripePrice } from './ports.js';
|
|
3
3
|
/** The name and the address of the product, which the customer portal shows. */
|
|
4
4
|
export interface Shop {
|
|
5
5
|
name: string;
|
|
@@ -43,12 +43,6 @@ export interface CatalogAction {
|
|
|
43
43
|
export declare function pricesOf(catalog: Catalog): NewPrice[];
|
|
44
44
|
/** Whether the price at Stripe charges what the catalog says. */
|
|
45
45
|
export declare function priceFits(found: StripePrice, wanted: NewPrice): boolean;
|
|
46
|
-
/**
|
|
47
|
-
* The default configuration of the customer portal names the product and links its own pages: an
|
|
48
|
-
* account copied from another one brings that one's along. `ids` holds the price at Stripe of each
|
|
49
|
-
* lookup key that has one; null while a price the portal offers has none yet.
|
|
50
|
-
*/
|
|
51
|
-
export declare function portalConfiguration(catalog: Catalog, shop: Shop, ids: ReadonlyMap<string, string>): PortalConfiguration | null;
|
|
52
46
|
/**
|
|
53
47
|
* Whether `found` holds every value of `wanted`. Lists hold the same items in any order: Stripe
|
|
54
48
|
* need not keep the order it was given.
|
package/dist/server/catalog.js
CHANGED
|
@@ -44,7 +44,7 @@ export function priceFits(found, wanted) {
|
|
|
44
44
|
* account copied from another one brings that one's along. `ids` holds the price at Stripe of each
|
|
45
45
|
* lookup key that has one; null while a price the portal offers has none yet.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
function portalConfiguration(catalog, shop, ids) {
|
|
48
48
|
const subscriptions = catalog.products.flatMap((product) => product.kind === 'subscription' ? [product] : []);
|
|
49
49
|
const prices = subscriptions.reduce((count, product) => count + product.prices.length, 0);
|
|
50
50
|
const allowed = [
|
package/dist/server/config.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type Mail = {
|
|
|
18
18
|
};
|
|
19
19
|
/** The mode of Stripe, by the prefix of the key: test until the delivery service switches to live. */
|
|
20
20
|
export type StripeMode = 'test' | 'live';
|
|
21
|
-
|
|
21
|
+
type Stripe = {
|
|
22
22
|
kind: 'stripe';
|
|
23
23
|
secretKey: string;
|
|
24
24
|
webhookSecret: string;
|
package/dist/server/core.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface CoreConfig {
|
|
|
19
19
|
missing: readonly string[];
|
|
20
20
|
}
|
|
21
21
|
/** Pool, configuration, log, mail and limits of what runs, for the hooks of the product. */
|
|
22
|
-
|
|
22
|
+
interface CoreContext<C> {
|
|
23
23
|
config: C;
|
|
24
24
|
db: Db;
|
|
25
25
|
log: Log;
|
|
@@ -27,7 +27,7 @@ export interface CoreContext<C> {
|
|
|
27
27
|
limits: Limits;
|
|
28
28
|
}
|
|
29
29
|
/** The hooks of the product the core calls; the art calls the others. */
|
|
30
|
-
|
|
30
|
+
interface CoreHooks<C> {
|
|
31
31
|
checks?: Record<string, (context: CoreContext<C>) => Promise<void>>;
|
|
32
32
|
start?: (context: CoreContext<C>) => Promise<void> | void;
|
|
33
33
|
stop?: (context: CoreContext<C>) => Promise<void> | void;
|
|
@@ -31,14 +31,6 @@ export declare function signupBody(m: Messages, terms: string): z.ZodObject<{
|
|
|
31
31
|
acting_for_business: z.ZodLiteral<true>;
|
|
32
32
|
via: z.ZodOptional<z.ZodString>;
|
|
33
33
|
}, z.core.$strip>;
|
|
34
|
-
/**
|
|
35
|
-
* The body of a Checkout over the API: a price of the catalog by its lookup key, and a quantity,
|
|
36
|
-
* which the price decides on (checkout below).
|
|
37
|
-
*/
|
|
38
|
-
export declare function checkoutBody(m: Messages): z.ZodObject<{
|
|
39
|
-
price: z.ZodString;
|
|
40
|
-
quantity: z.ZodOptional<z.ZodUnknown>;
|
|
41
|
-
}, z.core.$strip>;
|
|
42
34
|
/** The answer of a path of the baukasten's API, or null for any other path. */
|
|
43
35
|
export declare function serveApi(s: Services, event: RequestEvent): Promise<Response> | null;
|
|
44
36
|
export {};
|
package/dist/server/endpoints.js
CHANGED
|
@@ -84,7 +84,7 @@ async function signup(s, event) {
|
|
|
84
84
|
* The body of a Checkout over the API: a price of the catalog by its lookup key, and a quantity,
|
|
85
85
|
* which the price decides on (checkout below).
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
function checkoutBody(m) {
|
|
88
88
|
return z.object({ price: z.string(m.api.price), quantity: z.unknown().optional() }, m.api.notAnObject);
|
|
89
89
|
}
|
|
90
90
|
function invalid(m, path, message) {
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
export type { Account, ApiKey } from './accounts.js';
|
|
2
1
|
export { log } from './adapter/log.js';
|
|
3
2
|
export { ApiError } from './api.js';
|
|
4
|
-
export { StripeFailed, type CheckoutPage, type Order, type Purchase, type Subscription, type SubscriptionChange } from './billing.js';
|
|
5
|
-
export type { Config, Mail as MailConfig, Operator, Register, Stripe } from './config.js';
|
|
6
3
|
export { CheckFailed } from './health.js';
|
|
7
4
|
export { baukasten, type Context, type Hooks, type Kit } from './hook.js';
|
|
8
|
-
export { LIMITS, type Limits, type Rule } from './limits.js';
|
|
9
5
|
export type { Log } from './log.js';
|
|
10
6
|
export { MailFailed } from './mail.js';
|
|
11
|
-
export type {
|
|
12
|
-
export
|
|
13
|
-
export
|
|
14
|
-
export { periodic, type Run, type RunOptions } from './runs.js';
|
|
15
|
-
export type { ProductNumbers } from './sales.js';
|
|
16
|
-
export { start, type NodeHandler } from './start.js';
|
|
7
|
+
export type { Db, Mail, Mailer, Queryable } from './ports.js';
|
|
8
|
+
export { periodic } from './runs.js';
|
|
9
|
+
export { start } from './start.js';
|
|
17
10
|
export { KEY_RIGHTS, WEBHOOK_EVENTS, WEBHOOK_PATH } from './stripe.js';
|
|
18
11
|
export { requireAccount, tooMany } from './web.js';
|
package/dist/server/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
// The server entry of the baukasten; nothing here may reach the browser.
|
|
1
2
|
export { log } from './adapter/log.js';
|
|
2
3
|
export { ApiError } from './api.js';
|
|
3
|
-
export { StripeFailed } from './billing.js';
|
|
4
4
|
export { CheckFailed } from './health.js';
|
|
5
5
|
export { baukasten } from './hook.js';
|
|
6
|
-
export { LIMITS } from './limits.js';
|
|
7
6
|
export { MailFailed } from './mail.js';
|
|
8
7
|
export { periodic } from './runs.js';
|
|
9
8
|
export { start } from './start.js';
|
package/dist/server/pages.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { type RequestEvent } from '@sveltejs/kit';
|
|
2
|
-
import type { Brand } from '../product.js';
|
|
3
2
|
import type { AccountData, ConfirmData, FrameData, LegalData, LoginData, LogoutData, ResetData, SignupData, VerifyData } from '../ui/data.js';
|
|
4
3
|
import { type Services } from './services.js';
|
|
5
4
|
type LoadEvent = {
|
|
6
5
|
locals: App.Locals;
|
|
7
6
|
url: URL;
|
|
8
7
|
};
|
|
9
|
-
/** The marks of the product as the frame shows them: the picture of a shared link at its whole address. */
|
|
10
|
-
export declare function frameBrand(brand: Brand | undefined, origin: string): FrameData['brand'];
|
|
11
8
|
export type Pages = ReturnType<typeof createPages>;
|
|
12
9
|
export declare function createPages(services: () => Services): {
|
|
13
10
|
layout: {
|
package/dist/server/pages.js
CHANGED
|
@@ -9,7 +9,7 @@ import { sendLink, signUp } from './services.js';
|
|
|
9
9
|
import { parseVia } from './via.js';
|
|
10
10
|
import { clearSessionCookie, requireAccount, safeNext, SESSION_COOKIE, setSessionCookie, tooMany } from './web.js';
|
|
11
11
|
/** The marks of the product as the frame shows them: the picture of a shared link at its whole address. */
|
|
12
|
-
|
|
12
|
+
function frameBrand(brand, origin) {
|
|
13
13
|
if (brand === undefined)
|
|
14
14
|
return null;
|
|
15
15
|
return {
|
package/dist/server/ports.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface Mailer {
|
|
|
47
47
|
check(): Promise<void>;
|
|
48
48
|
}
|
|
49
49
|
/** A product at Stripe as the catalog compares it. */
|
|
50
|
-
|
|
50
|
+
interface StripeProduct {
|
|
51
51
|
id: string;
|
|
52
52
|
name: string;
|
|
53
53
|
active: boolean;
|
|
@@ -127,7 +127,7 @@ export interface PortalConfiguration {
|
|
|
127
127
|
}
|
|
128
128
|
export type SubscriptionStatus = 'incomplete' | 'incomplete_expired' | 'trialing' | 'active' | 'past_due' | 'canceled' | 'unpaid' | 'paused';
|
|
129
129
|
/** A subscription at Stripe with its one item. */
|
|
130
|
-
|
|
130
|
+
interface StripeSubscription {
|
|
131
131
|
id: string;
|
|
132
132
|
customerId: string;
|
|
133
133
|
status: SubscriptionStatus;
|
|
@@ -278,3 +278,4 @@ export interface StripeApi {
|
|
|
278
278
|
kind: 'invalid';
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
|
+
export {};
|
package/dist/shopify/hook.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { type ShopifyConfig } from './config.js';
|
|
|
9
9
|
import { type Plan } from './shops.js';
|
|
10
10
|
import { type WebhookHooks } from './webhooks.js';
|
|
11
11
|
/** The shop of a request that got in. */
|
|
12
|
-
|
|
12
|
+
interface ShopSession {
|
|
13
13
|
/** Its permanent domain, like example.myshopify.com. */
|
|
14
14
|
domain: string;
|
|
15
15
|
plan: Exclude<Plan, {
|
|
@@ -58,3 +58,4 @@ export interface ShopifyKit<E extends ProductEnv = ProductEnv> {
|
|
|
58
58
|
admin(shop: string): AdminApi;
|
|
59
59
|
}
|
|
60
60
|
export declare function shopifyBaukasten<E extends ProductEnv>(app: ShopifyApp<E>, hooks?: ShopifyHooks<E>): ShopifyKit<E>;
|
|
61
|
+
export {};
|
package/dist/shopify/index.d.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import { type NodeHandler } from '../server/start.js';
|
|
2
2
|
export { log } from '../server/adapter/log.js';
|
|
3
3
|
export { CheckFailed } from '../server/health.js';
|
|
4
|
-
export { LIMITS, type Limits, type Rule } from '../server/limits.js';
|
|
5
4
|
export type { Log } from '../server/log.js';
|
|
6
5
|
export { MailFailed } from '../server/mail.js';
|
|
7
6
|
export type { Db, Mail, Mailer, Queryable } from '../server/ports.js';
|
|
8
|
-
export { periodic
|
|
9
|
-
export type { NodeHandler } from '../server/start.js';
|
|
7
|
+
export { periodic } from '../server/runs.js';
|
|
10
8
|
export { AdminRefused, ShopGone, type AdminApi } from './admin.js';
|
|
11
|
-
export type { ShopifyConfig } from './config.js';
|
|
12
9
|
export { SHOPIFY_API_VERSION, SHOPIFY_WEBHOOK_PATH } from './constants.js';
|
|
13
|
-
export { shopifyBaukasten, type ShopifyContext, type ShopifyHooks, type ShopifyKit
|
|
14
|
-
export type { Plan } from './shops.js';
|
|
15
|
-
export type { CustomerRequest, Delivery } from './webhooks.js';
|
|
10
|
+
export { shopifyBaukasten, type ShopifyContext, type ShopifyHooks, type ShopifyKit } from './hook.js';
|
|
16
11
|
/** The server of a Shopify app, called by its stub server.js with the handler of adapter-node. */
|
|
17
12
|
export declare function start(load: () => Promise<{
|
|
18
13
|
handler: NodeHandler;
|
package/dist/shopify/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { serve } from '../server/start.js';
|
|
|
3
3
|
import { SHOPIFY_SECURITY_HEADERS } from './headers.js';
|
|
4
4
|
export { log } from '../server/adapter/log.js';
|
|
5
5
|
export { CheckFailed } from '../server/health.js';
|
|
6
|
-
export { LIMITS } from '../server/limits.js';
|
|
7
6
|
export { MailFailed } from '../server/mail.js';
|
|
8
7
|
export { periodic } from '../server/runs.js';
|
|
9
8
|
export { AdminRefused, ShopGone } from './admin.js';
|
package/dist/shopify/pages.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { Language } from '../product.js';
|
|
2
2
|
import type { LegalData } from '../ui/data.js';
|
|
3
3
|
import type { ShopifyTexts } from './texts.js';
|
|
4
|
-
/** Text into HTML, in an element or a quoted attribute. */
|
|
5
|
-
export declare function escapeHtml(text: string): string;
|
|
6
4
|
/** frame-ancestors for the admin of `shop`, or for none outside it. */
|
|
7
5
|
export declare function frameAncestors(shop: string | null): string;
|
|
8
6
|
/**
|
package/dist/shopify/pages.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { APP_BRIDGE_URL, SHOPIFY_ADMIN, SHOPIFY_CDN, SHOPIFY_SCRIPTS } from './constants.js';
|
|
2
2
|
/** Text into HTML, in an element or a quoted attribute. */
|
|
3
|
-
|
|
3
|
+
function escapeHtml(text) {
|
|
4
4
|
return text
|
|
5
5
|
.replaceAll('&', '&')
|
|
6
6
|
.replaceAll('<', '<')
|
package/dist/shopify/ports.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface ShopTokens {
|
|
|
11
11
|
* Why Shopify gave no tokens: `invalid` for an ID token or refresh token it no longer takes (the shop
|
|
12
12
|
* must open the app again), `failed` for anything else, which a later try may get past.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
type TokenRefusal = {
|
|
15
15
|
kind: 'invalid';
|
|
16
16
|
} | {
|
|
17
17
|
kind: 'failed';
|
|
@@ -75,3 +75,4 @@ export interface ShopifyApi {
|
|
|
75
75
|
/** Whether the Partner API takes the token: a query without data of a shop, for /health/deep. */
|
|
76
76
|
partnerCheck(): Promise<void>;
|
|
77
77
|
}
|
|
78
|
+
export {};
|
package/dist/shopify/shops.d.ts
CHANGED
|
@@ -3,10 +3,6 @@ import type { Db, Queryable } from '../server/ports.js';
|
|
|
3
3
|
import type { ShopifyPlan } from './app.js';
|
|
4
4
|
import type { ShopifyApi } from './ports.js';
|
|
5
5
|
import { type SealKey } from './seal.js';
|
|
6
|
-
/** A token is refreshed when it ends within this time, so that no request runs into its end. */
|
|
7
|
-
export declare const REFRESH_BEFORE_MS: number;
|
|
8
|
-
/** The plan of a shop is asked again after this time, and at once after the plan page. */
|
|
9
|
-
export declare const PLAN_FRESH_MS: number;
|
|
10
6
|
export type Plan = {
|
|
11
7
|
kind: 'none';
|
|
12
8
|
} | {
|
|
@@ -15,7 +11,7 @@ export type Plan = {
|
|
|
15
11
|
/** Until when it runs as a trial; null without one. */
|
|
16
12
|
trialEndsAt: Date | null;
|
|
17
13
|
};
|
|
18
|
-
|
|
14
|
+
type Entry = {
|
|
19
15
|
kind: 'in';
|
|
20
16
|
}
|
|
21
17
|
/** Shopify no longer takes the ID token: App Bridge fetches a new one. */
|
|
@@ -74,3 +70,4 @@ export declare function createShops(deps: {
|
|
|
74
70
|
/** 12 random bytes for each sealed token. */
|
|
75
71
|
nonce: () => Uint8Array;
|
|
76
72
|
}): Shops;
|
|
73
|
+
export {};
|
package/dist/shopify/shops.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { scopesCover } from './scopes.js';
|
|
2
2
|
import { open, seal } from './seal.js';
|
|
3
3
|
/** A token is refreshed when it ends within this time, so that no request runs into its end. */
|
|
4
|
-
|
|
4
|
+
const REFRESH_BEFORE_MS = 5 * 60_000;
|
|
5
5
|
/** The plan of a shop is asked again after this time, and at once after the plan page. */
|
|
6
|
-
|
|
6
|
+
const PLAN_FRESH_MS = 10 * 60_000;
|
|
7
7
|
export function createShops(deps) {
|
|
8
8
|
const { db, api, key, log, now } = deps;
|
|
9
9
|
/** Where a token belongs: a sealed one opens only there. */
|
package/dist/testing/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** What the frame sets once the page is hydrated. */
|
|
2
|
-
|
|
2
|
+
const HYDRATED = 'html[data-hydrated]';
|
|
3
3
|
/** Waits until the page is interactive: filled before, a field can lose what was typed. */
|
|
4
4
|
export async function waitForHydration(page) {
|
|
5
5
|
await page.waitForSelector(HYDRATED, { state: 'attached' });
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { freePort, spawnNode, startApp, type
|
|
1
|
+
export { freePort, spawnNode, startApp, type LogLine, type RunningApp } from './app.js';
|
|
2
2
|
export { connectionTest } from './connection.js';
|
|
3
|
-
export {
|
|
3
|
+
export { newCaller, open, waitForHydration } from './browser.js';
|
|
4
4
|
export { createTestDatabase, type TestDatabase } from './database.js';
|
|
5
5
|
export { envExampleProblems } from './env-example.js';
|
|
6
6
|
export { shopifyConnectionTest } from './shopify-connection.js';
|
|
7
|
-
export { startShopify, type AdminAnswer, type
|
|
8
|
-
export { linkIn, startSmtp, type
|
|
9
|
-
export { startStripe,
|
|
7
|
+
export { startShopify, type AdminAnswer, type ShopifyDouble } from './shopify.js';
|
|
8
|
+
export { linkIn, startSmtp, type SmtpDouble } from './smtp.js';
|
|
9
|
+
export { startStripe, type StripeDouble } from './stripe.js';
|
package/dist/testing/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { freePort, spawnNode, startApp } from './app.js';
|
|
2
2
|
export { connectionTest } from './connection.js';
|
|
3
|
-
export {
|
|
3
|
+
export { newCaller, open, waitForHydration } from './browser.js';
|
|
4
4
|
export { createTestDatabase } from './database.js';
|
|
5
5
|
export { envExampleProblems } from './env-example.js';
|
|
6
6
|
export { shopifyConnectionTest } from './shopify-connection.js';
|
|
7
7
|
export { startShopify } from './shopify.js';
|
|
8
8
|
export { linkIn, startSmtp } from './smtp.js';
|
|
9
|
-
export { startStripe
|
|
9
|
+
export { startStripe } from './stripe.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface DoubleShop {
|
|
2
2
|
domain: string;
|
|
3
3
|
/** gid://shopify/Shop/… */
|
|
4
4
|
id: string;
|
|
@@ -69,3 +69,4 @@ export interface ShopifyDouble {
|
|
|
69
69
|
export declare function startShopify(options: {
|
|
70
70
|
scopes: string;
|
|
71
71
|
}): Promise<ShopifyDouble>;
|
|
72
|
+
export {};
|
package/dist/testing/smtp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface ReceivedMail {
|
|
2
2
|
/** The recipients of the envelope, in small letters. */
|
|
3
3
|
to: string[];
|
|
4
4
|
/** The From header, decoded: name and address. */
|
|
@@ -26,8 +26,6 @@ export interface SmtpDouble {
|
|
|
26
26
|
accept(to: string): void;
|
|
27
27
|
stop(): Promise<void>;
|
|
28
28
|
}
|
|
29
|
-
/** Words like `=?UTF-8?Q?Gr=C3=BCezi?=` in a header; the space between two words goes with them. */
|
|
30
|
-
export declare function decodeWords(value: string): string;
|
|
31
29
|
/** The headers and the text of a plain-text message as SMTP carries it. */
|
|
32
30
|
export declare function parseMessage(raw: Buffer): {
|
|
33
31
|
headers: Map<string, string>;
|
|
@@ -36,3 +34,4 @@ export declare function parseMessage(raw: Buffer): {
|
|
|
36
34
|
/** The first link in a text that starts with `prefix`. */
|
|
37
35
|
export declare function linkIn(text: string, prefix: string): string;
|
|
38
36
|
export declare function startSmtp(): Promise<SmtpDouble>;
|
|
37
|
+
export {};
|
package/dist/testing/smtp.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// carries it.
|
|
6
6
|
import { EventEmitter } from 'node:events';
|
|
7
7
|
/** Words like `=?UTF-8?Q?Gr=C3=BCezi?=` in a header; the space between two words goes with them. */
|
|
8
|
-
|
|
8
|
+
function decodeWords(value) {
|
|
9
9
|
return value
|
|
10
10
|
.replace(/(=\?[^?]+\?[QqBb]\?[^?]*\?=)\s+(?==\?)/g, '$1')
|
|
11
11
|
.replace(/=\?([^?]+)\?([QqBb])\?([^?]*)\?=/g, (_, charset, kind, text) => {
|
package/dist/testing/stripe.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** A value of the JSON the double answers. */
|
|
2
|
-
|
|
2
|
+
type Json = string | number | boolean | null | Json[] | {
|
|
3
3
|
[key: string]: Json;
|
|
4
4
|
};
|
|
5
5
|
type JsonObject = {
|
|
6
6
|
[key: string]: Json;
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
interface DoublePrice {
|
|
9
9
|
id: string;
|
|
10
10
|
object: 'price';
|
|
11
11
|
product: string;
|
|
@@ -19,7 +19,7 @@ export interface DoublePrice {
|
|
|
19
19
|
interval_count: number;
|
|
20
20
|
} | null;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
interface DoubleSubscription {
|
|
23
23
|
id: string;
|
|
24
24
|
object: 'subscription';
|
|
25
25
|
customer: string;
|
|
@@ -35,7 +35,7 @@ export interface DoubleSubscription {
|
|
|
35
35
|
}[];
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
interface DoubleSession {
|
|
39
39
|
id: string;
|
|
40
40
|
object: 'checkout.session';
|
|
41
41
|
mode: string;
|
|
@@ -59,7 +59,7 @@ export interface DoubleSession {
|
|
|
59
59
|
/** The whole request, as the API got it. */
|
|
60
60
|
request: JsonObject;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
interface DoubleEndpoint {
|
|
63
63
|
id: string;
|
|
64
64
|
object: 'webhook_endpoint';
|
|
65
65
|
url: string;
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -12,4 +12,3 @@ export { default as LogoutPage } from './LogoutPage.svelte';
|
|
|
12
12
|
export { default as ResetPasswordPage } from './ResetPasswordPage.svelte';
|
|
13
13
|
export { default as SignupPage } from './SignupPage.svelte';
|
|
14
14
|
export { default as VerifyEmailPage } from './VerifyEmailPage.svelte';
|
|
15
|
-
export type * from './data.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tollwyn/baukasten",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "What every product built on this kit shares: configuration, log and server, database with two-layer migrations, health, accounts with sessions and API keys, limits, mail, payment through Stripe, the numbers for sales, the frame and pages, the stubs that hang them into a product and the check of a product, and the tools to test one, its connection test among them; and for a Shopify app its installation with tokens, the frame of the admin, its webhooks, its plan through the Partner API and its privacy text.",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "What every product built on this kit shares: configuration, log and server, database with two-layer migrations, health, accounts with sessions and API keys, limits, mail, payment through Stripe, the numbers for sales, the frame and pages, the stubs that hang them into a product and the check of a product, the ESLint config and knip preset of its gate, and the tools to test one, its connection test among them; and for a Shopify app its installation with tokens, the frame of the admin, its webhooks, its plan through the Partner API and its privacy text.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
"types": "./dist/ui/index.d.ts",
|
|
36
36
|
"svelte": "./dist/ui/index.js",
|
|
37
37
|
"default": "./dist/ui/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./eslint": {
|
|
40
|
+
"types": "./dist/eslint.d.ts",
|
|
41
|
+
"default": "./dist/eslint.js"
|
|
42
|
+
},
|
|
43
|
+
"./knip": {
|
|
44
|
+
"types": "./dist/knip.d.ts",
|
|
45
|
+
"default": "./dist/knip.js"
|
|
38
46
|
}
|
|
39
47
|
},
|
|
40
48
|
"bin": {
|
|
@@ -52,7 +60,7 @@
|
|
|
52
60
|
"test:db": "node ../scripts/test-db.mjs mit vitest run --config vitest.db.config.ts",
|
|
53
61
|
"test:e2e": "npm run muster && npm --prefix muster run test:e2e && npm --prefix muster-shopify run test:e2e",
|
|
54
62
|
"typecheck": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings && npm --prefix muster run check && npm --prefix muster-shopify run check",
|
|
55
|
-
"lint": "eslint . --max-warnings 0 && prettier --check . && npm --prefix muster run lint && npm --prefix muster-shopify run lint",
|
|
63
|
+
"lint": "eslint . --max-warnings 0 && knip && prettier --check . && npm --prefix muster run lint && npm --prefix muster-shopify run lint",
|
|
56
64
|
"check:version": "node scripts/npm-version.ts"
|
|
57
65
|
},
|
|
58
66
|
"peerDependencies": {
|
|
@@ -80,6 +88,7 @@
|
|
|
80
88
|
"eslint-config-prettier": "10.1.8",
|
|
81
89
|
"eslint-plugin-svelte": "3.23.1",
|
|
82
90
|
"globals": "17.13.0",
|
|
91
|
+
"knip": "6.41.0",
|
|
83
92
|
"prettier": "3.9.10",
|
|
84
93
|
"prettier-plugin-svelte": "4.1.1",
|
|
85
94
|
"smtp-server": "3.19.24",
|