@samebase/alchemy-convex 0.13.0 → 0.14.1

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 CHANGED
@@ -185,16 +185,26 @@ Every run needs the team token in `CONVEX_ACCESS_TOKEN`, or the Convex CLI login
185
185
  team access token names its team, so `Convex.state()` needs no `team` with one; a `team` slug that
186
186
  is set must be the slug of that team, checked against its projects, and a token of another team
187
187
  fails the run. The Convex CLI login needs the slug or the id. The first run of a team creates
188
- the project `samebase` and pushes the store functions to its
188
+ the project `samebase` and pushes the store app of this package to its
189
189
  production deployment with the Convex CLI of the app; a later version of this package pushes a
190
- newer store when it finds an older one. A store that is newer than the package fails the run with
191
- both versions, so the repositories of a team upgrade the package together. Each run makes a
190
+ newer store app when it finds an older one. A store that is newer than the package fails the run
191
+ with both versions, so the repositories of a team upgrade the package together. Each run makes a
192
192
  deploy key of that deployment that can only run the store's internal functions, and deletes it at
193
193
  the end. Rows are in the tables `resources` and `outputs` of the component `alchemyState` in the
194
194
  dashboard, under the stack name.
195
195
  The store has no lock: run one deploy of a stage at a time, such as with a CI concurrency group.
196
196
  The store and its readers (Samebase) are released and upgraded together.
197
197
 
198
+ The base app of a team can serve the store in the `samebase` project, next to its own components.
199
+ It mounts the component from `@samebase/alchemy-convex/state/convex.config`. It exports the
200
+ store functions of `@samebase/alchemy-convex/state/functions`:
201
+ `stateFunctions(components.alchemyState, "base")` and `readerFunctions(components.alchemyState)`.
202
+ It exports its auth config from `@samebase/alchemy-convex/state/auth.config`. Then
203
+ `state:host` answers `"base"`, and `Convex.state()` never pushes over that app: a base
204
+ that is older than the package fails the run with `OutdatedBase`, and the base app upgrades its
205
+ `@samebase/alchemy-convex`. See
206
+ [the design note](docs/design/convex-state.md#the-stores-functions-are-exported-and-a-base-is-never-pushed-over-0140).
207
+
198
208
  Store version 3 supports native subscriptions. A trusted server checks repository access, then
199
209
  calls internal `reader:authorize({ repository, subject })` with a deployment credential. It gives
200
210
  the returned token to a separate `ConvexClient` connected to the state deployment. The client
package/lib/index.d.ts CHANGED
@@ -458,12 +458,26 @@ export declare const withRunRecord: (output: unknown, record: RunRecord) => obje
458
458
  //#region src/State.d.ts
459
459
  /** The name of the state project in each team. */
460
460
  export declare const STORE_PROJECT_NAME = "samebase";
461
+ declare const OutdatedBase_base: Schema.Class<OutdatedBase, Schema.TaggedStruct<"OutdatedBase", {
462
+ readonly team: Schema.Union<readonly [Schema.String, Schema.Number]>;
463
+ readonly deployment: Schema.String;
464
+ readonly version: Schema.Number;
465
+ }>, import("effect/Cause").YieldableError>;
466
+ /**
467
+ * The base app of the team serves an older store. Convex.state() never
468
+ * pushes its store app over a base, because the push replaces every function
469
+ * of the deployment and so erases the base app.
470
+ */
471
+ export declare class OutdatedBase extends OutdatedBase_base {
472
+ get message(): string;
473
+ }
461
474
  /**
462
475
  * Alchemy state in the `samebase` project of `team`, with the team token
463
476
  * in CONVEX_ACCESS_TOKEN or the Convex CLI login. A team access token names
464
477
  * its team, so `team` is optional with one; the Convex CLI login needs the
465
478
  * slug or the id. The first use of the run finds or creates the project and
466
- * pushes the store when it is missing or older. `alchemy plan` on another
479
+ * pushes the store app when the store is missing or an older seed. An older
480
+ * base fails the run with {@link OutdatedBase}. `alchemy plan` on another
467
481
  * stack never touches Convex.
468
482
  */
469
483
  export declare const state: (options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/Credentials.ts","../src/ManagementApi.ts","../src/Providers.ts","../src/Push.ts","../src/Deployment.ts","../src/Component.ts","../src/Project.ts","../src/RunRecord.ts","../src/State.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/Credentials.ts","../src/ManagementApi.ts","../src/Providers.ts","../src/Push.ts","../src/Deployment.ts","../src/Component.ts","../src/Project.ts","../src/RunRecord.ts","../src/State.ts"],"mappings":";;;;;;;;;;;;;;;;UA6BiB;;WAEN,aAAa,SAAS;;WAEtB;;UAGM;;WAEN,aAAa,OAAO,OAAO;;WAE3B,gBAAgB,OAAO;;WAEvB,WAAW,OAAO,OAAO,SAAS;;;qBAGhC,oBAAoB;;qBAKpB;;qBAGA;;;;qBAOA,yBAAyB;;;;;;;;;qBAiCzB,kBAAiB,MAAM,MAAM;;qBA0B7B,aAAc;WAChB,cAAc,SAAS;WACvB,YAAY,SAAS;MAC5B,MAAM,MAAM;;;KC3GJ,mBAAmB,kBAAkB;KACrC,mBAAmB,kBAAkB;;UAGhC,YAAY;WAClB,OAAO;WACP;WACA,UAAU;;;;;;;;;;;;;qBAQR,uBAAuB;UAuBnB;;WAEN,UAAU,GACjB,mBACA,MAAM,QAAQ,qBAAqB,QAAQ,YAAY,QACpD,OAAO,OAAO,GAAG;;WAEb,cACP,mBACA,MAAM,QAAQ,qBAAqB,QAAQ,0BACxC,OAAO,aAAa;;WAEhB,oBAAoB,GAC3B,mBACA,6BACA,WAAW,SAAS,kBACpB,MAAM,QAAQ,qBAAqB,QAAQ,YAAY,QACpD,OAAO,OAAO,GAAG;;;;;;;;WAQb,gBACP,sCACG,OAAO,eAAe;;;;;;;;WAQlB,oBAAoB,mBAAmB,OAAO;;;;;WAK9C,YAAY,OAAO,SAAS,qBAAqB;;;qBAM/C,sBAAsB;;;;qBC1FtB,kBAAkB;;;;;;qBAOlB,YAAa,cAAa,MAAM,MAAM,iBAA2B,MAAA,MAAA,8CAAA,mCAAA,oDAAA;;;;KC+BlE,oBAAoB,SAAS;;;;;;KAO7B;;WAIG;;;;;;;;WAQA;;;;;;;WAOA;;;;;;qBAkCF,0BAA0B;MAIxB;;;;;;;qBAMF,0BAA0B;MAOxB;;;;;;;;;qBAMF,uBAAuB;MAMrB;;;;;;;qBAQF,2BAA2B;MAIzB;;;;;;;qBAMF,oCAAoC;MAIlC;;;;;;qBAMF,kCAAkC;MAIhC;;;qBAuCF,oBACX;EACG;EAAa;;;;UC3HR;;WAEC;aAAoB;;;;;;;;WAOpB;;;;;;WAMA,SAAS;;WAET,MAAM,SAAS,eAAe;;KAG7B,mBACP;;WAEU;MAEV;;WAEU;;;;;WAKA;;;UAIL;WACC;WACA,MAAM;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA;;UAGM,6BAA6B;;WAEnC;;WAEA;;YAGC,aAAa,8BAEvB,iBACA,sBACA,kBACA;;KAQU,kBAAkB,+BAA8B,KACxD,UAAU,kBACR,WAAW;;;;;qBAWJ,wBAAwB;MAGtB;;;;;;;;;;;;qBAeF,cAAU,YACV,OAAS,oBAAe,OAAA,OAAA,mBAAA;;;;;;EAYvB,MAAA,YAAM;IAAc;IAAgB;QAAgB,OAAA,OAAA;;;;;;;;;;qBAMrD,iCAAiC;MAU/B;;;;;;qBAMF,qCAAqC;MAInC;;;;;;;;;qBAMF,4BAA4B;MAS1B;;;;;UC1OE;WACN;WACA;WACA;;;;;;;;;qBAUE,gBAAiB;EAC5B,YAAY;EACZ;EACA,WAAW;MACZ,OAAA,yBAAA;;;;KCWW,mBAAmB;KAEnB;;WAGG;;WAEA;;WAEA,mBAAmB;;WAEnB;;;WAIA;;WAEA;;UAGE;WACN;WACA;WACA;WACA;WACA;WACA;;YAGC,UAAU,2BAA2B,cAAc,0BAA0B;qBAC5E,2BAAO,cAAA;;;;;;;qBAGP,0BAA0B;MAIxB;;;;;;;;;;;;qBASF,gCAAgC;MAS9B;;;;;;;;qBAOF,yBAAyB;MAKvB;;;;;;qBAMF,+BAA+B;MAI7B;;;;;;;qBAMF,wBAAwB;MAItB;;;;;qBC5GF;;UAMI;WACN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;;WAEA;;;;;qBAGE,uBAAuB;MAGrB;;;UAME;WACN;WACA,KAAK,SAAS;;;;;;;;qBAuIZ,YAAa,OAAO,mBAAiB,OAAO,OAAO,WAAW;;;;;;;qBAgB9D,gBAAiB,iBAAiB,QAAQ;;;;qBCrH1C;;;;;;;;;;;qBAwLA,qBAAqB;MAKnB;;;;;;;;;;;qBAqOF,QACX;WAAoB;MACnB,MAAM,MAAM,cAAc,oBAAoB"}
package/lib/index.js CHANGED
@@ -1660,6 +1660,26 @@ const remoteOf = (api, record) => {
1660
1660
  /** The version that the store answers, or undefined when the deployment has no `state:version`. */
1661
1661
  const storeVersion = (api) => api.call("state:version", {}).pipe(Effect.flatMap((reply) => reply.status === "error" && MISSING_FUNCTION.test(reply.errorMessage) ? Effect.succeed(void 0) : valueOf(reply).pipe(Effect.flatMap(decode(Schema.Number, "store version")))));
1662
1662
  /**
1663
+ * The app that serves the store. A store without `state:host` is a seed: it
1664
+ * was pushed before 0.14.0, and only the store app of this package served
1665
+ * those stores.
1666
+ */
1667
+ const storeHost = (api) => api.call("state:host", {}).pipe(Effect.flatMap((reply) => reply.status === "error" && MISSING_FUNCTION.test(reply.errorMessage) ? Effect.succeed("seed") : valueOf(reply).pipe(Effect.flatMap(decode(Schema.Literals(["seed", "base"]), "store host")))));
1668
+ /**
1669
+ * The base app of the team serves an older store. Convex.state() never
1670
+ * pushes its store app over a base, because the push replaces every function
1671
+ * of the deployment and so erases the base app.
1672
+ */
1673
+ var OutdatedBase = class extends Schema.TaggedError()("OutdatedBase", {
1674
+ team: Schema.Union([Schema.String, Schema.Number]),
1675
+ deployment: Schema.String,
1676
+ version: Schema.Number
1677
+ }) {
1678
+ get message() {
1679
+ return `The base of team ${this.team} (${this.deployment}) answers store version ${this.version}, and this package needs store version 4. Convex.state does not push over a base. Upgrade the dependency @samebase/alchemy-convex of the base app to the version of this package, and push the base app.`;
1680
+ }
1681
+ };
1682
+ /**
1663
1683
  * The functions directory of the store app, `src/store/convex` of this
1664
1684
  * package. This file is `src/State.ts` in a checkout and a chunk in `lib/`
1665
1685
  * in the installed package, which ships `src` too, so the directory is
@@ -1716,8 +1736,8 @@ const createStoreProject = (api, teamId, team) => Effect.gen(function* () {
1716
1736
  });
1717
1737
  /**
1718
1738
  * The StateService over the `samebase` project of the team: found or
1719
- * created, its store pushed when it is missing or older, and read and
1720
- * written with the keys of this run.
1739
+ * created, its store app pushed when the store is missing or an older seed,
1740
+ * and read and written with the keys of this run.
1721
1741
  */
1722
1742
  const makeConvexState = (options) => Effect.gen(function* () {
1723
1743
  const { api } = options;
@@ -1737,15 +1757,21 @@ const makeConvexState = (options) => Effect.gen(function* () {
1737
1757
  current = next;
1738
1758
  return next.call(path, args);
1739
1759
  })))) };
1760
+ const needsPush = (found) => found === void 0 ? Effect.succeed(true) : found < 4 ? storeHost(functions).pipe(Effect.map((host) => host === "seed")) : Effect.succeed(false);
1740
1761
  let version = yield* storeVersion(functions);
1741
- if (version === void 0 || version < 4) {
1762
+ if (yield* needsPush(version)) {
1742
1763
  if (version === void 0 && deployment.lastDeployTime) return yield* failure(`project ${STORE_PROJECT_NAME} (${project.id}) of team ${team} has code that is not the state store, and Convex.state does not push over it. Rename that project in the Convex dashboard.`);
1743
1764
  version = yield* storeVersion(functions);
1744
- if (version === void 0 || version < 4) {
1765
+ if (yield* needsPush(version)) {
1745
1766
  yield* pushStore(options, deployment);
1746
1767
  version = yield* storeVersion(functions);
1747
1768
  }
1748
1769
  }
1770
+ if (version !== void 0 && version < 4 && (yield* storeHost(functions)) === "base") return yield* new OutdatedBase({
1771
+ team,
1772
+ deployment: deployment.name,
1773
+ version
1774
+ });
1749
1775
  if (version !== 4) return yield* failure(`${deployment.name} answers store version ${version ?? "none"}, and this package needs store version 4.`);
1750
1776
  const remote = remoteOf(functions, options.run);
1751
1777
  /** The stack's output plus the record, when the run has one and the output has a place for it. */
@@ -1791,7 +1817,8 @@ const makeConvexState = (options) => Effect.gen(function* () {
1791
1817
  * in CONVEX_ACCESS_TOKEN or the Convex CLI login. A team access token names
1792
1818
  * its team, so `team` is optional with one; the Convex CLI login needs the
1793
1819
  * slug or the id. The first use of the run finds or creates the project and
1794
- * pushes the store when it is missing or older. `alchemy plan` on another
1820
+ * pushes the store app when the store is missing or an older seed. An older
1821
+ * base fails the run with {@link OutdatedBase}. `alchemy plan` on another
1795
1822
  * stack never touches Convex.
1796
1823
  */
1797
1824
  const state = (options = {}) => Layer.effect(State, Effect.gen(function* () {
@@ -1815,6 +1842,6 @@ const state = (options = {}) => Layer.effect(State, Effect.gen(function* () {
1815
1842
  }).pipe(Effect.provideContext(context), Effect.orDie));
1816
1843
  }));
1817
1844
  //#endregion
1818
- export { ACCESS_TOKEN_ENV, AbsoluteRootDir, AmbiguousDeployment, AmbiguousProject, CodePushFailed, ConvexApiError, ConvexAppNotFound, ConvexCliNotFound, CreatedProjectNotFound, Credentials, CredentialsError, DEPLOY_KEY_ENV, Deployment, DeploymentIdentityChange, EnvironmentNeedsDeployKey, ManagementApi, ProductionDeploymentNotFound, Project, ProjectDeploymentChange, ProjectNotFound, ProjectTeamChange, Providers, PushTargetMismatch, RUN_RECORD_KEY, RunRecordError, STORE_PROJECT_NAME, VariablesOwnedBySomeoneElse, bindComponent, fromConfig, fromValues, parseDeployOutput, providers, runRecord, state, withRunRecord };
1845
+ export { ACCESS_TOKEN_ENV, AbsoluteRootDir, AmbiguousDeployment, AmbiguousProject, CodePushFailed, ConvexApiError, ConvexAppNotFound, ConvexCliNotFound, CreatedProjectNotFound, Credentials, CredentialsError, DEPLOY_KEY_ENV, Deployment, DeploymentIdentityChange, EnvironmentNeedsDeployKey, ManagementApi, OutdatedBase, ProductionDeploymentNotFound, Project, ProjectDeploymentChange, ProjectNotFound, ProjectTeamChange, Providers, PushTargetMismatch, RUN_RECORD_KEY, RunRecordError, STORE_PROJECT_NAME, VariablesOwnedBySomeoneElse, bindComponent, fromConfig, fromValues, parseDeployOutput, providers, runRecord, state, withRunRecord };
1819
1846
 
1820
1847
  //# sourceMappingURL=index.js.map