@pithy-sh/core 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,10 +11,16 @@ import { z } from "zod";
11
11
  * the seam they already share, so the shape is stated once instead of being duplicated (and drifting) at
12
12
  * both ends.
13
13
  *
14
- * **The file holds a live credential.** A signed session cookie for a local database is a way in for
15
- * anyone who can read it, which is why it is written under `logs/` — gitignored by the starter template,
16
- * so it can never be committed — and why nothing here is ever put in a `PithyError` (the payload reaches
17
- * logs verbatim).
14
+ * **The file holds a live credential.** A signed claim for a local database is a way in for anyone who
15
+ * can read it, which is why it is written under `logs/` — gitignored by the starter template, so it can
16
+ * never be committed — and why nothing here is ever put in a `PithyError` (the payload reaches logs
17
+ * verbatim).
18
+ *
19
+ * **It used to hold a session cookie, and now it holds a claim — `#572`.** The difference is what
20
+ * survives: a cookie names a row in `pithy_auth_sessions`, so signing out of the app revoked it and the
21
+ * dev login 404'd until somebody reseeded. A claim names a *user*, signed with the session secret, and
22
+ * the route exchanges it for a fresh session on every open. Sign-out then revokes the session it should
23
+ * revoke and leaves the way back in alone.
18
24
  */
19
25
  /** The directory seed artifacts are written to, relative to the project root. Gitignored, so transient. */
20
26
  const SEED_ARTIFACT_DIR = "logs";
@@ -34,21 +40,22 @@ const DEV_LOGIN_PATH = `${SEED_ARTIFACT_DIR}/${DEV_LOGIN_FILE}`;
34
40
  * anything the kit serves that is not part of a capability's public surface lives under this prefix, so
35
41
  * a route added here can never collide with an application path someone already shipped.
36
42
  *
37
- * Registered **only** in a `dev` composition, and never under CI. It mints an authenticated session with
38
- * no credential presented, which is the whole risk of the feature and the reason its gates live at
39
- * registration rather than inside the handler.
43
+ * Registered **only** in a `dev` composition, and never under CI. It mints an authenticated session for
44
+ * whoever presents a claim this project's own secret signed, which is the whole risk of the feature and
45
+ * the reason its gates live at registration rather than inside the handler.
40
46
  */
41
47
  const DEV_LOGIN_ROUTE = "/__pithy/dev-login";
48
+ /** The query parameter {@link DEV_LOGIN_ROUTE} takes the claim in. Spelled once, read by both ends. */
49
+ const DEV_LOGIN_CLAIM_PARAM = "t";
42
50
  /**
43
51
  * A seeded dev login — everything a browser needs to be signed in as a seeded user, and nothing else.
44
52
  * `z.input` is the JSON on disk (dates as ISO-8601 strings); `z.output` is the app shape.
45
53
  */
46
54
  const DevLogin = z.object({
47
- email: z.string().describe("The seeded user this cookie signs in as. Shown on the `pithy dev` ready banner."),
48
- userId: z.string().describe("The seeded user's id, so a tool can correlate the cookie with the seeded rows."),
49
- cookieName: z.string().describe("The cookie name the auth capability's session is read from, e.g. `better-auth.session_token`."),
50
- cookieValue: z.string().describe("The signed, URI-encoded cookie value. A live credential for the local database — never logged, never committed, never in an error payload."),
51
- expiresAt: JsonDate.describe("When the seeded session expires. ISO-8601 text on disk; a `Date` in app code.")
52
- }).describe("A seeded dev login: the signed session cookie for one seeded user, written to `logs/dev-login.json`.");
55
+ email: z.string().describe("The seeded user this claim signs in as. Shown on the `pithy dev` ready banner."),
56
+ userId: z.string().describe("The seeded user's id, so a tool can correlate the login with the seeded rows."),
57
+ claim: z.string().describe("The signed, URI-encoded claim naming the user to sign in as. A live credential for the local database — never logged, never committed, never in an error payload. **Not a session:** it names a user and the route mints a session against it, so revoking a session leaves it working."),
58
+ expiresAt: JsonDate.describe("When the claim stops being accepted. ISO-8601 text on disk; a `Date` in app code.")
59
+ }).describe("A seeded dev login: the signed claim for one seeded user, written to `logs/dev-login.json`.");
53
60
  //#endregion
54
- export { DEV_LOGIN_FILE, DEV_LOGIN_PATH, DEV_LOGIN_ROUTE, DevLogin, SEED_ARTIFACT_DIR };
61
+ export { DEV_LOGIN_CLAIM_PARAM, DEV_LOGIN_FILE, DEV_LOGIN_PATH, DEV_LOGIN_ROUTE, DevLogin, SEED_ARTIFACT_DIR };
@@ -3,5 +3,5 @@
3
3
  /** This package's npm name — the join key against a release feed. */
4
4
  export declare const PACKAGE_NAME = "@pithy-sh/core";
5
5
  /** This package's version, stamped from its own package.json at generation time. */
6
- export declare const PACKAGE_VERSION = "0.5.0";
6
+ export declare const PACKAGE_VERSION = "0.6.2";
7
7
  //# sourceMappingURL=version.generated.d.ts.map
@@ -4,6 +4,6 @@
4
4
  /** This package's npm name — the join key against a release feed. */
5
5
  const PACKAGE_NAME = "@pithy-sh/core";
6
6
  /** This package's version, stamped from its own package.json at generation time. */
7
- const PACKAGE_VERSION = "0.5.0";
7
+ const PACKAGE_VERSION = "0.6.2";
8
8
  //#endregion
9
9
  export { PACKAGE_NAME, PACKAGE_VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pithy-sh/core",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -133,8 +133,14 @@ export interface ProvisionScope {
133
133
  * is created, judged to be the project's, kept out of the teardown record, and orphaned.
134
134
  */
135
135
  readonly honorsGlobal: boolean;
136
- /** The script name a Worker deploys under in this scope. */
137
- worker(worker: string): string;
136
+ /**
137
+ * The script name a Worker deploys under in this scope.
138
+ *
139
+ * `declared` is the name that scope's stanza already carries, when it carries one. A scope may honor it
140
+ * or compose its own; see {@link environmentScope} and {@link featureScope}, which answer differently
141
+ * and say why. Passing it is how a caller asks the scope rather than deciding for itself.
142
+ */
143
+ worker(worker: string, declared?: string): string;
138
144
  /**
139
145
  * This scope's CF Secrets Store entry name for a declared secret.
140
146
  *
@@ -228,13 +234,25 @@ export function bindingResourceName(
228
234
  * every name below, so an illegal one must fail before anything is created rather than at the fourth
229
235
  * getter that happened to use it.
230
236
  *
231
- * **A Worker's script name is wrangler's own, `<script>-<env>`, deliberately.** It is the one name here
232
- * that is not `<project>-<env>-<thing>`, because `wrangler deploy --env staging` has always appended the
233
- * environment to the top-level `name` — so every project that ever deployed is already at that address.
234
- * A Worker script is a `refuse`-policy namespace precisely because renaming one orphans the deployment
235
- * and every `service` binding pointing at it; composing a "more correct" name here would do exactly
236
- * that, silently, on the next deploy. The name is written out rather than left implicit so a service
237
- * binding and a deploy agree on one string that is in the file.
237
+ * **A declared name wins; the fallback is wrangler's own, `<script>-<env>`.**
238
+ *
239
+ * This reverses the rule that stood here until #580, and the reversal is worth reading rather than
240
+ * assuming. The old rule *recomputed* `<script>-<env>` on every provision and wrote it over whatever the
241
+ * stanza said, on the argument that wrangler has always appended the environment to the top-level `name`,
242
+ * so every project that ever deployed is already at that address.
243
+ *
244
+ * That argument is still true about the **fallback**, and the fallback is unchanged: a stanza that names
245
+ * nothing gets `<script>-<env>` and every existing adopter deploys exactly where they always did. What was
246
+ * wrong was recomputing over a name somebody wrote down. Since #580 the starter template writes one —
247
+ * `<project>-<env>-<worker>`, the shape every other resource here takes — so recompute would have made the
248
+ * template's stamp pointless, and worse: it would have silently renamed the Worker of every project that
249
+ * had taken the new shape, on their next `pithy provision`. A Worker script is a `refuse`-policy namespace
250
+ * precisely because renaming one orphans the deployment, its routes, and every `service` binding pointing
251
+ * at it. Doing that as a side effect of provisioning is the failure the old rule was written to prevent,
252
+ * arriving from the other direction.
253
+ *
254
+ * So the name is read, not composed, when the file already holds one — and written out when it does not,
255
+ * so a service binding and a deploy still agree on one string that is in the file.
238
256
  */
239
257
  export function environmentScope(project: string, environment: string): ProvisionScope {
240
258
  assertValidEnvironment(environment);
@@ -247,7 +265,7 @@ export function environmentScope(project: string, environment: string): Provisio
247
265
  honorsGlobal: true,
248
266
  resource: (binding, kind, naming) =>
249
267
  bindingResourceName(project, binding, kind, naming, (thing) => names[KIND_NAMER[kind]](thing)),
250
- worker: (worker) => `${worker}-${environment}`,
268
+ worker: (worker, declared) => declared ?? `${worker}-${environment}`,
251
269
  secretEntry: (secret, secretScope) =>
252
270
  secretEntryName(project, secret, secretScope, () => names.secretEntry(secret)),
253
271
  };
@@ -272,6 +290,12 @@ export function environmentScope(project: string, environment: string): Provisio
272
290
  * `resource` is ignored for the same reason it is moot: a feature's `<thing>` segment is the binding plus
273
291
  * the kind, and the whole name is recomputed on teardown rather than looked up, so nothing outside the
274
292
  * feature reads the string.
293
+ *
294
+ * **And a declared Worker name is ignored here too — the other half of the #580 asymmetry.** A declared
295
+ * environment's script name is the adopter's, written in a tracked file, and provisioning reads it. A
296
+ * feature's is the kit's: it is composed from the branch, written into a generated config nobody commits,
297
+ * and **recomputed on teardown**. Honoring a declared one would leave `pithy feature destroy` computing a
298
+ * name the deployment does not answer to, which is a Worker nothing deletes.
275
299
  */
276
300
  export function featureScope(identity: FeatureIdentity): ProvisionScope {
277
301
  return {
@@ -13,4 +13,4 @@
13
13
  export const PACKAGE_NAME = "@pithy-sh/core";
14
14
 
15
15
  /** This package's version, stamped from its own package.json at generation time. */
16
- export const PACKAGE_VERSION = "0.6.0";
16
+ export const PACKAGE_VERSION = "0.6.2";