@puzzmo/cli 1.0.51 → 1.0.52
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 +12 -2
- package/docs/games-changed.md +223 -0
- package/lib/commands/changed.d.ts +12 -0
- package/lib/commands/changed.js +178 -0
- package/lib/commands/changed.js.map +1 -0
- package/lib/commands/validate.d.ts +1 -1
- package/lib/commands/validate.js +1 -1
- package/lib/commands/validate.js.map +1 -1
- package/lib/index.js +52 -2
- package/lib/index.js.map +1 -1
- package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.d.ts +37 -0
- package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.js +256 -0
- package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.js.map +1 -0
- package/lib/queries/gameRuntimes.d.ts +14 -0
- package/lib/queries/gameRuntimes.js +52 -0
- package/lib/queries/gameRuntimes.js.map +1 -0
- package/lib/util/belay.d.ts +34 -0
- package/lib/util/belay.js +22 -0
- package/lib/util/belay.js.map +1 -0
- package/lib/util/discoverGames.d.ts +10 -2
- package/lib/util/discoverGames.js +14 -11
- package/lib/util/discoverGames.js.map +1 -1
- package/package.json +1 -1
- package/schemas/puzzmo-file-schema.json +1 -1
- package/src/commands/changed.ts +239 -0
- package/src/commands/validate.ts +1 -1
- package/src/index.ts +58 -2
- package/src/queries/__generated__/cliGameRuntimesQuery.graphql.ts +301 -0
- package/src/queries/gameRuntimes.ts +64 -0
- package/src/util/belay.ts +56 -0
- package/src/util/discoverGames.ts +23 -11
- package/templates/minesweeper/README.md +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { graphql, query } from "../util/belay.js"
|
|
2
|
+
import type { cliGameRuntimesQuery } from "./__generated__/cliGameRuntimesQuery.graphql.js"
|
|
3
|
+
|
|
4
|
+
const gameRuntimesQuery = graphql`
|
|
5
|
+
query cliGameRuntimesQuery($token: String!) {
|
|
6
|
+
teamForToken(token: $token) {
|
|
7
|
+
slug
|
|
8
|
+
games(first: 500) {
|
|
9
|
+
edges {
|
|
10
|
+
node {
|
|
11
|
+
slug
|
|
12
|
+
runtime {
|
|
13
|
+
currentVersion {
|
|
14
|
+
assetsSha
|
|
15
|
+
}
|
|
16
|
+
nextVersion {
|
|
17
|
+
assetsSha
|
|
18
|
+
}
|
|
19
|
+
previousVersion {
|
|
20
|
+
assetsSha
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
/** The deployed runtime build SHAs for a game, read from its runtime's current/next/previous version slots. */
|
|
31
|
+
export type GameVersions = {
|
|
32
|
+
/** SHA of the live/current runtime build */
|
|
33
|
+
current: string | null
|
|
34
|
+
/** SHA of the staged "next" runtime build, if one exists */
|
|
35
|
+
next: string | null
|
|
36
|
+
/** SHA of the previous runtime build, if one exists */
|
|
37
|
+
previous: string | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Fetches the runtime build SHAs for every game in the token's team, keyed by game slug. The token is sent both as
|
|
42
|
+
* the `teamForToken` argument and as the Bearer header — the latter is what unlocks the team-gated `runtime` field.
|
|
43
|
+
*/
|
|
44
|
+
export const fetchTeamGameVersions = async (apiURL: string, token: string): Promise<Map<string, GameVersions>> => {
|
|
45
|
+
const { data, errors } = await query<cliGameRuntimesQuery>(gameRuntimesQuery, {
|
|
46
|
+
variables: { token },
|
|
47
|
+
url: `${apiURL}/graphql`,
|
|
48
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (errors?.length) throw new Error(`teamForToken failed: ${errors.map((e) => e.message).join("; ")}`)
|
|
52
|
+
|
|
53
|
+
const versions = new Map<string, GameVersions>()
|
|
54
|
+
for (const edge of data?.teamForToken?.games?.edges ?? []) {
|
|
55
|
+
const game = edge?.node
|
|
56
|
+
if (!game) continue
|
|
57
|
+
versions.set(game.slug, {
|
|
58
|
+
current: game.runtime?.currentVersion?.assetsSha || null,
|
|
59
|
+
next: game.runtime?.nextVersion?.assetsSha || null,
|
|
60
|
+
previous: game.runtime?.previousVersion?.assetsSha || null,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return versions
|
|
64
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// "belay lite" — a local copy of the parts of @puzzmo-com/belay the CLI needs.
|
|
2
|
+
// The CLI is published to npm and ships unbundled, so it can't depend on the private,
|
|
3
|
+
// source-only belay package; this vendors the tiny query/graphql helpers instead.
|
|
4
|
+
// The `graphql` tag lets the Relay compiler validate these queries and generate types
|
|
5
|
+
// (see the "cli" project in relay.config.json). Keep this in sync with packages/belay.
|
|
6
|
+
|
|
7
|
+
/** Standard GraphQL error shape */
|
|
8
|
+
export type GraphQLError = {
|
|
9
|
+
message: string
|
|
10
|
+
locations?: Array<{ line: number; column: number }>
|
|
11
|
+
path?: Array<string | number>
|
|
12
|
+
extensions?: Record<string, unknown>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** GraphQL response shape */
|
|
16
|
+
export type GraphQLResponse<TData> = {
|
|
17
|
+
data?: TData
|
|
18
|
+
errors?: GraphQLError[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Operation type with variables and response — matches Relay's generated operation types */
|
|
22
|
+
export type OperationType = {
|
|
23
|
+
variables: Record<string, unknown>
|
|
24
|
+
response: unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Request options for the query function */
|
|
28
|
+
export type RequestOptions<TVariables> = {
|
|
29
|
+
variables: TVariables
|
|
30
|
+
url: string
|
|
31
|
+
headers?: HeadersInit
|
|
32
|
+
signal?: AbortSignal
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A graphql tagged template literal that returns the query string. This lets the Relay
|
|
37
|
+
* compiler pick up and validate the operation (and generate its types) without bundling relay-runtime.
|
|
38
|
+
*/
|
|
39
|
+
export const graphql = (strings: TemplateStringsArray): string => strings[0]!
|
|
40
|
+
|
|
41
|
+
/** Execute a GraphQL query via fetch, typed with a Relay-generated operation type. */
|
|
42
|
+
export const query = async <T extends OperationType>(
|
|
43
|
+
queryString: string,
|
|
44
|
+
options: RequestOptions<T["variables"]>,
|
|
45
|
+
): Promise<GraphQLResponse<T["response"]>> => {
|
|
46
|
+
const { variables, url, headers, signal } = options
|
|
47
|
+
|
|
48
|
+
const response = await fetch(url, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
51
|
+
signal,
|
|
52
|
+
body: JSON.stringify({ query: queryString, variables }),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return response.json() as Promise<GraphQLResponse<T["response"]>>
|
|
56
|
+
}
|
|
@@ -20,7 +20,10 @@ export type DiscoveredGame = {
|
|
|
20
20
|
puzzmoJsonDir: string
|
|
21
21
|
/** Validated puzzmo.json contents */
|
|
22
22
|
puzzmoFile: PuzzmoFile
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Absolute path to the dist directory containing the build artifacts (empty string when discovered with requireDist: false and none was
|
|
25
|
+
* found)
|
|
26
|
+
*/
|
|
24
27
|
distDir: string
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -37,8 +40,15 @@ export type DiscoveryResult = {
|
|
|
37
40
|
errors: DiscoveryError[]
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
/** Options for {@link discoverGames} */
|
|
44
|
+
export type DiscoverOptions = {
|
|
45
|
+
/** Require each game to have a non-empty dist folder (default true). Set false for commands that run before a build. */
|
|
46
|
+
requireDist?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
/** Walks `rootDir` looking for puzzmo.json files, validates each, and resolves their dist directory */
|
|
41
|
-
export const discoverGames = async (rootDir: string): Promise<DiscoveryResult> => {
|
|
50
|
+
export const discoverGames = async (rootDir: string, options: DiscoverOptions = {}): Promise<DiscoveryResult> => {
|
|
51
|
+
const { requireDist = true } = options
|
|
42
52
|
const root = path.resolve(rootDir)
|
|
43
53
|
const puzzmoJsonPaths = findPuzzmoJsonFiles(root)
|
|
44
54
|
|
|
@@ -66,18 +76,20 @@ export const discoverGames = async (rootDir: string): Promise<DiscoveryResult> =
|
|
|
66
76
|
const puzzmoJsonDir = path.dirname(puzzmoJsonPath)
|
|
67
77
|
const distDir = resolveDistDir(puzzmoFile, puzzmoJsonDir, root)
|
|
68
78
|
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
if (requireDist) {
|
|
80
|
+
if (!distDir) {
|
|
81
|
+
fileErrors.push(`Could not find a dist/build folder for ${puzzmoFile.game.slug}. Set "output.dir" in puzzmo.json.`)
|
|
82
|
+
} else if (!hasFiles(distDir)) {
|
|
83
|
+
fileErrors.push(`Dist folder is empty: ${distDir}`)
|
|
84
|
+
}
|
|
74
85
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
if (fileErrors.length) {
|
|
87
|
+
errors.push({ puzzmoJsonPath, slug: puzzmoFile.game.slug, errors: fileErrors })
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
games.push({ puzzmoJsonPath, puzzmoJsonDir, puzzmoFile, distDir: distDir
|
|
92
|
+
games.push({ puzzmoJsonPath, puzzmoJsonDir, puzzmoFile, distDir: distDir ?? "" })
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
return { games, errors }
|