diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml new file mode 100644 index 0000000..f81e93a --- /dev/null +++ b/.github/workflows/publish-npm.yml @@ -0,0 +1,29 @@ +name: Publish Package + +on: + push: + tags: + - 'v*' + +permissions: + id-token: write # Required for OIDC + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + # Ensure npm 11.5.1 or later is installed + - name: Update npm + run: npm install -g npm@latest + - run: npm ci + - run: npm run build --if-present + - run: npm test + - run: npm publish diff --git a/Dockerfile b/Dockerfile index e583ea9..6bf966b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ COPY package*.json ./ RUN npm ci COPY src ./src COPY tsconfig.json ./ +COPY schema.graphql ./ RUN npm run build # Stage 2: Copy the built code and the node modules @@ -13,7 +14,6 @@ WORKDIR /app COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/build ./build COPY package*.json ./ -COPY schema.graphql ./ # Don't run as root RUN addgroup -g 1001 -S nodejs diff --git a/README.md b/README.md index 43e7a27..24c1026 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,15 @@ This section aims to describe all the environment variables exposed to configure To see an example of sensible defaults, see the [env.example](./.env.example) configuration file. -## Installing from npm Registry +## Installing from npm + +This package is published to npm and is available for public access + +```sh +npm install mina-archive-node-graphql +``` + +## Installing from artifact registry This package is published to a Google Cloud Artifact Registry and is available for public access. @@ -157,11 +165,13 @@ To create a new release, follow these steps: 3. **Create and push a tag**: `git tag v1.2.3 && git push origin v1.2.3` The CI/CD workflow will automatically: + - Build and publish npm package to GCP registry with version from package.json - Build and push Docker image to both GCP Artifact Registry and GitHub Container Registry - Create semantic version tags (e.g., `1.2.3`, `1.2`, `1`, `latest`) **Published artifacts:** + - npm: `https://europe-southwest1-npm.pkg.dev/o1labs-192920/euro-npm/` - Docker (GCP): `europe-west3-docker.pkg.dev/o1labs-192920/euro-docker-repo/archive-node-api` - Docker (GitHub): `ghcr.io/o1-labs/archive-node-api` diff --git a/package-lock.json b/package-lock.json index 53e2702..489c655 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "archive-node-graphql", - "version": "1.0.0", + "name": "mina-archive-node-graphql", + "version": "0.0.7-prerelease-1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "archive-node-graphql", - "version": "1.0.0", + "name": "mina-archive-node-graphql", + "version": "0.0.7-prerelease-1", "license": "ISC", "dependencies": { "@envelop/core": "^4.0.0", @@ -672,7 +672,6 @@ "integrity": "sha512-4kEcaa2P/BFz+xy5tagbtzM08gbjHXyYqW+n6SJuUFK7N6bZNnA4cu1hVgHcqOqk8Dbwv7fiseGT0x3Hhqjwqg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -727,7 +726,6 @@ "integrity": "sha512-zgjyR4GyuONeDGJBKNt9lFJ8HfDX7rpxZZVR7LSXr9lUkjf6vUGgD2k/K4UAoOTWCKKCor6TA562ezGlA8su6Q==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -2060,7 +2058,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.509.0.tgz", "integrity": "sha512-uXT8wIq1k+m0mS/pC9U1cUTIjUB7/4PgxyiYsTxYPIULtWnQXltAlcPU3QzKTJMP60sqftRYZ2jFDLAVsipQxw==", "dev": true, - "peer": true, "dependencies": { "@aws-sdk/credential-provider-env": "3.502.0", "@aws-sdk/credential-provider-http": "3.503.1", @@ -2862,7 +2859,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3181,7 +3177,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@envelop/core/-/core-4.0.3.tgz", "integrity": "sha512-O0Vz8E0TObT6ijAob8jYFVJavcGywKThM3UAsxUIBBVPYZTMiqI9lo2gmAnbMUnrDcAYkUTZEW9FDYPRdF5l6g==", - "peer": true, "dependencies": { "@envelop/types": "4.0.1", "tslib": "^2.5.0" @@ -6801,7 +6796,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -6812,7 +6806,6 @@ "integrity": "sha512-JEV2RAqijAFdWeT6HddYymfnkiRu2ASxoTBr4WsnGJhOjWZkEy6vp+Sx9ozr1NaIODOa2HUyckExIqQjn6qywQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api": "^1.0.0" }, @@ -7138,7 +7131,6 @@ "integrity": "sha512-0CXMOYPXgAdLM2OzVkiUfAL6QQwWVhnMfUXCqLsITY42FZ9TxAhZIHkoc4mfVxvPuXsBnRYGR8UQZX86p87z4A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api": "^1.0.0" }, @@ -9277,7 +9269,6 @@ "integrity": "sha512-NYMp0bl52DxXfcLmivMKvOIE14aaB9qJjdHeUbs6GZ9yxgD5w0yeiOT+gWEL+1PzZgGWRxSFEpghID1YfXAc4w==", "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "@tapjs/processinfo": "^3.1.8", "@tapjs/stack": "2.0.1", @@ -10076,7 +10067,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", "devOptional": true, - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -10187,7 +10177,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -10540,7 +10529,6 @@ "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -11543,7 +11531,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -13360,7 +13347,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -14370,7 +14356,6 @@ "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -15906,7 +15891,6 @@ "integrity": "sha512-i1rBX5N7VPl0eYb6+mHNp52sEuaS2Wi8CDYx1X5sn9naevL78+265XJqy1qENEk7mRKwS06NHpUqiBwR7qeodw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 10.16.0" } @@ -18505,7 +18489,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -18869,7 +18852,6 @@ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -20566,7 +20548,6 @@ "integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "async-limiter": "~1.0.0" } @@ -20945,7 +20926,6 @@ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21447,7 +21427,6 @@ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -21530,7 +21509,6 @@ "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index cc2d1d6..b82770f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "archive-node-graphql", - "version": "1.0.0", + "name": "mina-archive-node-graphql", + "version": "0.0.8", "description": "A NodeJS GraphQL server for exposing data for o1js/zkApps", "repository": { "type": "git", @@ -8,9 +8,12 @@ }, "main": "build/src/index.js", "start": "build/src/index.js", + "files": [ + "build/" + ], "type": "module", "scripts": { - "build": "npm run clean && npx tsc", + "build": "npm run clean && npx tsc && cp schema.graphql build/", "start": "node build/src/index.js", "dev": "cross-env NODE_NO_WARNINGS=1 npx nodemon --exec 'node -r dotenv/config --loader ts-node/esm' src/index.ts", "test": "./run-tests.sh", diff --git a/src/resolvers.ts b/src/resolvers.ts index 52d3dcf..71ad70d 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -1,34 +1,52 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; -import { Resolvers } from './resolvers-types.js'; -import { visit, print, parse } from 'graphql'; import fs from 'fs'; +import { parse, print, visit } from 'graphql'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import { Resolvers } from './resolvers-types.js'; import { TracingState, setSpanNameFromGraphQLContext, } from './tracing/tracer.js'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const graphqlSchemaPath = join(__dirname, '..', 'schema.graphql'); + const fullResolvers: Resolvers = { Query: { events: async (_, { input }, context) => { - const graphQLSpan = setSpanNameFromGraphQLContext(context, 'events.graphql'); + const graphQLSpan = setSpanNameFromGraphQLContext( + context, + 'events.graphql' + ); return context.db_client.getEvents(input, { tracingState: new TracingState(graphQLSpan), }); }, actions: async (_, { input }, context) => { - const graphQLSpan = setSpanNameFromGraphQLContext(context, 'actions.graphql'); + const graphQLSpan = setSpanNameFromGraphQLContext( + context, + 'actions.graphql' + ); return context.db_client.getActions(input, { tracingState: new TracingState(graphQLSpan), }); }, networkState: async (_, __, context) => { - const graphQLSpan = setSpanNameFromGraphQLContext(context, 'networkState.graphql'); + const graphQLSpan = setSpanNameFromGraphQLContext( + context, + 'networkState.graphql' + ); return context.db_client.getNetworkState({ tracingState: new TracingState(graphQLSpan), }); }, blocks: async (_, { query, limit, sortBy }, context) => { - const graphQLSpan = setSpanNameFromGraphQLContext(context, 'blocks.graphql'); + const graphQLSpan = setSpanNameFromGraphQLContext( + context, + 'blocks.graphql' + ); return context.db_client.getBlocks(query, limit, sortBy, { tracingState: new TracingState(graphQLSpan), }); @@ -41,7 +59,9 @@ let typeDefs: string | undefined = undefined; // If the ENABLED_QUERIES environment variable is set, filter the schema and resolvers. if (process.env.ENABLED_QUERIES !== undefined) { - const enabledQueries = process.env.ENABLED_QUERIES.split(',').map((q) => q.trim()); + const enabledQueries = process.env.ENABLED_QUERIES.split(',').map((q) => + q.trim() + ); // If the list is not empty, filter the resolvers. if (enabledQueries.length > 0) { @@ -54,7 +74,7 @@ if (process.env.ENABLED_QUERIES !== undefined) { }; // Filter the schema AST. - const typeDefsString = fs.readFileSync('./schema.graphql', 'utf-8'); + const typeDefsString = fs.readFileSync(graphqlSchemaPath, 'utf-8'); const typeDefsAst = parse(typeDefsString); const modifiedAst = visit(typeDefsAst, { ObjectTypeDefinition(node) { @@ -76,7 +96,7 @@ if (process.env.ENABLED_QUERIES !== undefined) { // Create the executable schema. const schema = makeExecutableSchema({ resolvers: [resolvers], - typeDefs: typeDefs || fs.readFileSync('./schema.graphql', 'utf-8'), + typeDefs: typeDefs || fs.readFileSync(graphqlSchemaPath, 'utf-8'), }); export { resolvers, schema };