From 508b7d87cc3ab556b2b2cf28af2552ee71750776 Mon Sep 17 00:00:00 2001 From: Michal Bock Date: Thu, 1 Aug 2024 16:49:48 +0100 Subject: [PATCH] feat: use latest hypertune version (#944) ### Description Use latest Hypertune sdk version and small tweaks to the recommended setup. ### Demo URL https://feature-flag-hypertune-alpha.vercel.app/ ### Type of Change - [ ] New Example - [ X ] Example updates (Bug fixes, new features, etc.) - [ ] Other (changes to the codebase, but not to examples) --------- Co-authored-by: Miraan Tabrez --- .../feature-flag-hypertune/app/layout.tsx | 18 +++- .../feature-flag-hypertune/app/page.tsx | 4 +- .../components/ClientComponentWrapper.tsx | 28 ------ .../generated/hypertune.react.tsx | 21 +++- .../generated/hypertune.ts | 99 ++++++------------- .../feature-flag-hypertune/package.json | 5 +- .../feature-flag-hypertune/pnpm-lock.yaml | 74 +++++++++++++- 7 files changed, 138 insertions(+), 111 deletions(-) delete mode 100644 edge-middleware/feature-flag-hypertune/components/ClientComponentWrapper.tsx diff --git a/edge-middleware/feature-flag-hypertune/app/layout.tsx b/edge-middleware/feature-flag-hypertune/app/layout.tsx index 531e616f3..c60a4bbd5 100644 --- a/edge-middleware/feature-flag-hypertune/app/layout.tsx +++ b/edge-middleware/feature-flag-hypertune/app/layout.tsx @@ -1,8 +1,9 @@ import type { ReactNode } from 'react' import { Layout, getMetadata } from '@vercel/examples-ui' import { VercelToolbar } from '@vercel/toolbar/next' -import { HypertuneSourceProvider } from '../generated/hypertune.react' +import { HypertuneProvider } from '../generated/hypertune.react' import '@vercel/examples-ui/globals.css' +import getHypertune from '../lib/getHypertune' export const metadata = getMetadata({ title: 'feature-flag-hypertune', @@ -14,9 +15,18 @@ export const runtime = 'edge' export default async function RootLayout({ children, }: Readonly<{ children: ReactNode }>) { + const hypertune = await getHypertune() + + const serverDehydratedState = hypertune.dehydrate() + const serverRootArgs = hypertune.getRootArgs() + return ( - @@ -32,6 +42,6 @@ export default async function RootLayout({ - + ) } diff --git a/edge-middleware/feature-flag-hypertune/app/page.tsx b/edge-middleware/feature-flag-hypertune/app/page.tsx index 964ff9e5c..eb9daf576 100644 --- a/edge-middleware/feature-flag-hypertune/app/page.tsx +++ b/edge-middleware/feature-flag-hypertune/app/page.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Text, Page, Link, List } from '@vercel/examples-ui' +import ClientComponent from '../components/ClientComponent' import ServerComponent from '../components/ServerComponent' -import ClientComponentWrapper from '../components/ClientComponentWrapper' export const metadata = { title: 'Vercel x Hypertune example', @@ -37,7 +37,7 @@ export default async function Home() {
- + Once you've deployed this project, open the{' '} diff --git a/edge-middleware/feature-flag-hypertune/components/ClientComponentWrapper.tsx b/edge-middleware/feature-flag-hypertune/components/ClientComponentWrapper.tsx deleted file mode 100644 index ade392891..000000000 --- a/edge-middleware/feature-flag-hypertune/components/ClientComponentWrapper.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { merge } from 'hypertune' -import { - HypertuneHydrator, - HypertuneRootProvider, -} from '../generated/hypertune.react' -import getHypertune from '../lib/getHypertune' -import ClientComponent from './ClientComponent' - -export default async function ClientComponentWrapper() { - const hypertune = await getHypertune() - - const serverRootArgs = hypertune.getRootArgs() - const serverDehydratedState = hypertune.dehydrate() - - return ( - - - - - - ) -} diff --git a/edge-middleware/feature-flag-hypertune/generated/hypertune.react.tsx b/edge-middleware/feature-flag-hypertune/generated/hypertune.react.tsx index 172a0e66a..af511eab5 100644 --- a/edge-middleware/feature-flag-hypertune/generated/hypertune.react.tsx +++ b/edge-middleware/feature-flag-hypertune/generated/hypertune.react.tsx @@ -52,6 +52,8 @@ export function HypertuneSourceProvider({ remoteLogging: { mode: typeof window === 'undefined' ? 'off' : undefined, }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + localLogger: typeof window === 'undefined' ? () => {} : undefined, ...createSourceOptions, }), // Don't recreate the source even if createSourceOptions changes @@ -129,14 +131,23 @@ export function HypertuneRootProvider({ } export function useHypertune(): hypertune.RootNode { - return React.useContext(HypertuneRootContext) + const hypertuneRoot = React.useContext(HypertuneRootContext) + + if (!hypertuneRoot.props.context) { + console.warn( + '[Hypertune] Calling `useHypertune` hook outside of the `HypertuneProvider`. Fallback values will be used.' + ) + } + return hypertuneRoot } export function HypertuneHydrator({ dehydratedState, + rootArgs, children, }: { dehydratedState?: hypertune.DehydratedState | null + rootArgs?: hypertune.RootArgs children: React.ReactElement | null }): React.ReactElement | null { const hypertuneSource = useHypertuneSource() @@ -145,5 +156,13 @@ export function HypertuneHydrator({ hypertuneSource.hydrate(dehydratedState) } + if (rootArgs) { + return ( + + {children} + + ) + } + return children } diff --git a/edge-middleware/feature-flag-hypertune/generated/hypertune.ts b/edge-middleware/feature-flag-hypertune/generated/hypertune.ts index 9ad98dffe..aa8701447 100644 --- a/edge-middleware/feature-flag-hypertune/generated/hypertune.ts +++ b/edge-middleware/feature-flag-hypertune/generated/hypertune.ts @@ -4,17 +4,23 @@ import * as sdk from 'hypertune' export const queryCode = `query FullQuery{root{exampleFlag}}` -export const query = { - Query: { - objectTypeName: 'Query', - selection: { - root: { - fieldArguments: { __isPartialObject__: true }, - fieldQuery: { - Root: { - objectTypeName: 'Root', - selection: { - exampleFlag: { fieldArguments: {}, fieldQuery: null }, +export const query: sdk.Query = { + variableDefinitions: {}, + fragmentDefinitions: {}, + fieldQuery: { + Query: { + type: 'InlineFragment', + objectTypeName: 'Query', + selection: { + root: { + fieldArguments: { __isPartialObject__: true }, + fieldQuery: { + Root: { + type: 'InlineFragment', + objectTypeName: 'Root', + selection: { + exampleFlag: { fieldArguments: {}, fieldQuery: null }, + }, }, }, }, @@ -23,57 +29,6 @@ export const query = { }, } -function mergeQueryAndArgs( - query: sdk.Query, - queryArgs: sdk.ObjectValueWithVariables | null, - unwrapObjectArgs = false -): sdk.Query { - return Object.fromEntries( - Object.entries(query).map(([objectTypeName, fragment]) => { - const objectArgs = unwrapObjectArgs - ? queryArgs && - queryArgs[objectTypeName] && - queryArgs[objectTypeName] instanceof Object - ? (queryArgs[objectTypeName] as sdk.ObjectValueWithVariables) - : null - : queryArgs - - return [ - objectTypeName, - { - objectTypeName, - selection: Object.fromEntries( - Object.entries(fragment.selection).map( - ([fieldName, { fieldQuery }]) => { - const fieldArgs = - objectArgs && - objectArgs[fieldName] && - objectArgs[fieldName] instanceof Object - ? (objectArgs[fieldName] as sdk.ObjectValueWithVariables) - : null - - return [ - fieldName, - { - fieldArguments: { - ...(fieldArgs && fieldArgs.args - ? (fieldArgs.args as sdk.ObjectValueWithVariables) - : {}), - }, - fieldQuery: fieldQuery - ? mergeQueryAndArgs(fieldQuery, fieldArgs, true) - : null, - }, - ] - } - ) - ), - }, - ] - }) - ) -} - /** * @deprecated use '@vercel/flags/providers/hypertune' package instead. */ @@ -123,7 +78,7 @@ export type Root = { const rootFallback = { exampleFlag: false } export class RootNode extends sdk.Node { - typeName = 'Root' as const + override typeName = 'Root' as const getRootArgs(): RootArgs { const { step } = this.props @@ -134,7 +89,7 @@ export class RootNode extends sdk.Node { get({ fallback = rootFallback as Root }: { fallback?: Root } = {}): Root { const getQuery = null - return this.evaluate(getQuery, fallback) as Root + return this.getValue({ query: getQuery, fallback }) as Root } /** @@ -148,7 +103,9 @@ export class RootNode extends sdk.Node { args?: Rec fallback: boolean }): boolean { - const props0 = this.getField('exampleFlag', args) + const props0 = this.getFieldNodeProps('exampleFlag', { + fieldArguments: args, + }) const expression0 = props0.expression if (expression0 && expression0.type === 'BooleanExpression') { @@ -177,7 +134,7 @@ export type Rec4 = { } export class SourceNode extends sdk.Node { - typeName = 'Query' as const + override typeName = 'Query' as const get({ args, @@ -186,12 +143,16 @@ export class SourceNode extends sdk.Node { args: Rec4 fallback?: Source }): Source { - const getQuery = mergeQueryAndArgs(query, args) - return this.evaluate(getQuery, fallback) as Source + const getQuery = sdk.mergeFieldQueryAndArgs( + query.fragmentDefinitions, + sdk.getFieldQueryForPath(query.fragmentDefinitions, query.fieldQuery, []), + args + ) + return this.getValue({ query: getQuery, fallback }) as Source } root({ args }: { args: RootArgs }): RootNode { - const props0 = this.getField('root', args) + const props0 = this.getFieldNodeProps('root', { fieldArguments: args }) const expression0 = props0.expression if ( diff --git a/edge-middleware/feature-flag-hypertune/package.json b/edge-middleware/feature-flag-hypertune/package.json index b93a4bf46..82c159bc6 100644 --- a/edge-middleware/feature-flag-hypertune/package.json +++ b/edge-middleware/feature-flag-hypertune/package.json @@ -15,7 +15,7 @@ "@vercel/examples-ui": "^2.0.3", "@vercel/flags": "2.5.1", "@vercel/toolbar": "^0.1.15", - "hypertune": "2.3.3", + "hypertune": "2.4.0", "next": "^14.1.4", "react": "latest", "react-dom": "latest", @@ -32,6 +32,7 @@ "eslint-plugin-prettier": "^5.1.3", "postcss": "^8.4.38", "prettier": "^3.2.5", - "tailwindcss": "^3.4.3" + "tailwindcss": "^3.4.3", + "turbo": "1.13.4" } } diff --git a/edge-middleware/feature-flag-hypertune/pnpm-lock.yaml b/edge-middleware/feature-flag-hypertune/pnpm-lock.yaml index c37207cbc..662e3d707 100644 --- a/edge-middleware/feature-flag-hypertune/pnpm-lock.yaml +++ b/edge-middleware/feature-flag-hypertune/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: ^0.1.15 version: 0.1.15(next@14.1.4(react-dom@18.2.0(react@18.3.1))(react@18.3.1))(react@18.3.1) hypertune: - specifier: 2.3.3 - version: 2.3.3(encoding@0.1.13) + specifier: 2.4.0 + version: 2.4.0(encoding@0.1.13) next: specifier: ^14.1.4 version: 14.1.4(react-dom@18.2.0(react@18.3.1))(react@18.3.1) @@ -69,6 +69,9 @@ importers: tailwindcss: specifier: ^3.4.3 version: 3.4.3 + turbo: + specifier: 1.13.4 + version: 1.13.4 packages: @@ -965,8 +968,8 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - hypertune@2.3.3: - resolution: {integrity: sha512-AAIeRlLzG6YwkZgpOnJ8SBzoTF6YFnrraxEeYzDXh9zP87KmPnS6LqymfeNm0FfLxrxVh9ZB8Xis30iROsbQ4g==} + hypertune@2.4.0: + resolution: {integrity: sha512-lhYKOT+qeaBMvfTSu92MWGeE3/Oj6XyREtkytOcgcQD0S6B0wlpHB+xBnXGdo31uNS9CTCGKesdFmjj/maBl6w==} hasBin: true iconv-lite@0.6.3: @@ -1709,6 +1712,40 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + turbo-darwin-64@1.13.4: + resolution: {integrity: sha512-A0eKd73R7CGnRinTiS7txkMElg+R5rKFp9HV7baDiEL4xTG1FIg/56Vm7A5RVgg8UNgG2qNnrfatJtb+dRmNdw==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@1.13.4: + resolution: {integrity: sha512-eG769Q0NF6/Vyjsr3mKCnkG/eW6dKMBZk6dxWOdrHfrg6QgfkBUk0WUUujzdtVPiUIvsh4l46vQrNVd9EOtbyA==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@1.13.4: + resolution: {integrity: sha512-Bq0JphDeNw3XEi+Xb/e4xoKhs1DHN7OoLVUbTIQz+gazYjigVZvtwCvgrZI7eW9Xo1eOXM2zw2u1DGLLUfmGkQ==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@1.13.4: + resolution: {integrity: sha512-BJcXw1DDiHO/okYbaNdcWN6szjXyHWx9d460v6fCHY65G8CyqGU3y2uUTPK89o8lq/b2C8NK0yZD+Vp0f9VoIg==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@1.13.4: + resolution: {integrity: sha512-OFFhXHOFLN7A78vD/dlVuuSSVEB3s9ZBj18Tm1hk3aW1HTWTuAw0ReN6ZNlVObZUHvGy8d57OAGGxf2bT3etQw==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@1.13.4: + resolution: {integrity: sha512-u5A+VOKHswJJmJ8o8rcilBfU5U3Y1TTAfP9wX8bFh8teYF1ghP0EhtMRLjhtp6RPa+XCxHHVA2CiC3gbh5eg5g==} + cpu: [arm64] + os: [win32] + + turbo@1.13.4: + resolution: {integrity: sha512-1q7+9UJABuBAHrcC4Sxp5lOqYS5mvxRrwa33wpIyM18hlOCpRD/fTJNxZ0vhbMcJmz15o9kkVm743mPn7p6jpQ==} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2834,7 +2871,7 @@ snapshots: human-signals@2.1.0: {} - hypertune@2.3.3(encoding@0.1.13): + hypertune@2.4.0(encoding@0.1.13): dependencies: cac: 6.7.14 core-js: 3.33.0 @@ -3578,6 +3615,33 @@ snapshots: tslib@2.6.2: {} + turbo-darwin-64@1.13.4: + optional: true + + turbo-darwin-arm64@1.13.4: + optional: true + + turbo-linux-64@1.13.4: + optional: true + + turbo-linux-arm64@1.13.4: + optional: true + + turbo-windows-64@1.13.4: + optional: true + + turbo-windows-arm64@1.13.4: + optional: true + + turbo@1.13.4: + optionalDependencies: + turbo-darwin-64: 1.13.4 + turbo-darwin-arm64: 1.13.4 + turbo-linux-64: 1.13.4 + turbo-linux-arm64: 1.13.4 + turbo-windows-64: 1.13.4 + turbo-windows-arm64: 1.13.4 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1