Переглянути джерело

Add Kinobi interface and move default visitor to nodes-from-anchor (#19)

Loris Leiva 1 рік тому
батько
коміт
05ba4c4fd2

+ 2 - 0
packages/errors/src/codes.ts

@@ -30,6 +30,7 @@ export const KINOBI_ERROR__UNEXPECTED_NODE_KIND = 2 as const;
 export const KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND = 3 as const;
 export const KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND = 3 as const;
 export const KINOBI_ERROR__LINKED_NODE_NOT_FOUND = 4 as const;
 export const KINOBI_ERROR__LINKED_NODE_NOT_FOUND = 4 as const;
 export const KINOBI_ERROR__NODE_FILESYSTEM_FUNCTION_UNAVAILABLE = 5 as const;
 export const KINOBI_ERROR__NODE_FILESYSTEM_FUNCTION_UNAVAILABLE = 5 as const;
+export const KINOBI_ERROR__VERSION_MISMATCH = 6 as const;
 
 
 // Visitors-related errors.
 // Visitors-related errors.
 // Reserve error codes in the range [1200000-1200999].
 // Reserve error codes in the range [1200000-1200999].
@@ -75,6 +76,7 @@ export type KinobiErrorCode =
     | typeof KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND
     | typeof KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND
     | typeof KINOBI_ERROR__UNEXPECTED_NODE_KIND
     | typeof KINOBI_ERROR__UNEXPECTED_NODE_KIND
     | typeof KINOBI_ERROR__UNRECOGNIZED_NODE_KIND
     | typeof KINOBI_ERROR__UNRECOGNIZED_NODE_KIND
+    | typeof KINOBI_ERROR__VERSION_MISMATCH
     | typeof KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND
     | typeof KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND
     | typeof KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES
     | typeof KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES
     | typeof KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION
     | typeof KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION

+ 5 - 0
packages/errors/src/context.ts

@@ -25,6 +25,7 @@ import {
     KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NODE_KIND,
     KINOBI_ERROR__UNRECOGNIZED_NODE_KIND,
     KINOBI_ERROR__UNRECOGNIZED_NODE_KIND,
+    KINOBI_ERROR__VERSION_MISMATCH,
     KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND,
     KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND,
     KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES,
     KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES,
     KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION,
     KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION,
@@ -73,6 +74,10 @@ export type KinobiErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
     [KINOBI_ERROR__UNRECOGNIZED_NODE_KIND]: {
     [KINOBI_ERROR__UNRECOGNIZED_NODE_KIND]: {
         kind: string;
         kind: string;
     };
     };
+    [KINOBI_ERROR__VERSION_MISMATCH]: {
+        kinobiVersion: string;
+        rootVersion: string;
+    };
     [KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND]: {
     [KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND]: {
         account: AccountNode;
         account: AccountNode;
         missingField: CamelCaseString;
         missingField: CamelCaseString;

+ 3 - 0
packages/errors/src/messages.ts

@@ -11,6 +11,7 @@ import {
     KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NODE_KIND,
     KINOBI_ERROR__UNEXPECTED_NODE_KIND,
     KINOBI_ERROR__UNRECOGNIZED_NODE_KIND,
     KINOBI_ERROR__UNRECOGNIZED_NODE_KIND,
+    KINOBI_ERROR__VERSION_MISMATCH,
     KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND,
     KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND,
     KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES,
     KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES,
     KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION,
     KINOBI_ERROR__VISITORS__CANNOT_EXTEND_MISSING_VISIT_FUNCTION,
@@ -43,6 +44,8 @@ export const KinobiErrorMessages: Readonly<{
     [KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND]: 'Expected nested node of kind [$expectedKinds], got [$kind]',
     [KINOBI_ERROR__UNEXPECTED_NESTED_NODE_KIND]: 'Expected nested node of kind [$expectedKinds], got [$kind]',
     [KINOBI_ERROR__UNEXPECTED_NODE_KIND]: 'Expected node of kind [$expectedKinds], got [$kind].',
     [KINOBI_ERROR__UNEXPECTED_NODE_KIND]: 'Expected node of kind [$expectedKinds], got [$kind].',
     [KINOBI_ERROR__UNRECOGNIZED_NODE_KIND]: 'Unrecognized node kind [$kind].',
     [KINOBI_ERROR__UNRECOGNIZED_NODE_KIND]: 'Unrecognized node kind [$kind].',
+    [KINOBI_ERROR__VERSION_MISMATCH]:
+        'The provided RootNode version [$rootVersion] is not compatible with the installed Kinobi version [$kinobiVersion].',
     [KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND]: 'Account [$name] does not have a field named [$missingField].',
     [KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND]: 'Account [$name] does not have a field named [$missingField].',
     [KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES]:
     [KINOBI_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMES]:
         'Cannot add PDAs to program [$programName] because the following PDA names already exist [$duplicatedPdaNames].',
         'Cannot add PDAs to program [$programName] because the following PDA names already exist [$duplicatedPdaNames].',

+ 2 - 0
packages/library/package.json

@@ -54,7 +54,9 @@
         "test:types": "zx ../../node_modules/@kinobi-so/internals/scripts/test-types.mjs"
         "test:types": "zx ../../node_modules/@kinobi-so/internals/scripts/test-types.mjs"
     },
     },
     "dependencies": {
     "dependencies": {
+        "@kinobi-so/errors": "workspace:*",
         "@kinobi-so/nodes": "workspace:*",
         "@kinobi-so/nodes": "workspace:*",
+        "@kinobi-so/validators": "workspace:*",
         "@kinobi-so/visitors": "workspace:*"
         "@kinobi-so/visitors": "workspace:*"
     },
     },
     "license": "MIT",
     "license": "MIT",

+ 4 - 0
packages/library/src/index.ts

@@ -1,2 +1,6 @@
+export * from '@kinobi-so/errors';
 export * from '@kinobi-so/nodes';
 export * from '@kinobi-so/nodes';
+export * from '@kinobi-so/validators';
 export * from '@kinobi-so/visitors';
 export * from '@kinobi-so/visitors';
+
+export * from './kinobi';

+ 51 - 0
packages/library/src/kinobi.ts

@@ -0,0 +1,51 @@
+import { KINOBI_ERROR__VERSION_MISMATCH } from '@kinobi-so/errors';
+import { KinobiError } from '@kinobi-so/errors';
+import { assertIsNode, KinobiVersion, Node, RootNode } from '@kinobi-so/nodes';
+import { visit, Visitor } from '@kinobi-so/visitors';
+
+export interface Kinobi {
+    accept<T>(visitor: Visitor<T>): T;
+    clone(): Kinobi;
+    getJson(): string;
+    getRoot(): RootNode;
+    update(visitor: Visitor<Node | null>): void;
+}
+
+export function createFromRoot(root: RootNode): Kinobi {
+    let currentRoot = root;
+    validateKinobiVersion(currentRoot.version);
+    return {
+        accept<T>(visitor: Visitor<T>): T {
+            return visit(currentRoot, visitor);
+        },
+        clone(): Kinobi {
+            return createFromRoot({ ...currentRoot });
+        },
+        getJson(): string {
+            return JSON.stringify(currentRoot);
+        },
+        getRoot(): RootNode {
+            return currentRoot;
+        },
+        update(visitor: Visitor<Node | null>): void {
+            const newRoot = visit(currentRoot, visitor);
+            assertIsNode(newRoot, 'rootNode');
+            currentRoot = newRoot;
+        },
+    };
+}
+
+export function createFromJson(json: string): Kinobi {
+    return createFromRoot(JSON.parse(json) as RootNode);
+}
+
+function validateKinobiVersion(rootVersion: KinobiVersion): void {
+    const kinobiVersion = __VERSION__;
+    if (rootVersion === kinobiVersion) return;
+    const [rootMajor, rootMinor] = rootVersion.split('.').map(Number);
+    const [KinobiMajor, KinobiMinor] = kinobiVersion.split('.').map(Number);
+    const isZeroMajor = rootMajor === 0 && KinobiMajor === 0;
+    if (isZeroMajor && rootMinor === KinobiMinor) return;
+    if (rootMajor === KinobiMajor) return;
+    throw new KinobiError(KINOBI_ERROR__VERSION_MISMATCH, { kinobiVersion, rootVersion });
+}

+ 6 - 0
packages/library/src/types/global.d.ts

@@ -0,0 +1,6 @@
+declare const __BROWSER__: boolean;
+declare const __DEV__: boolean;
+declare const __NODEJS__: boolean;
+declare const __REACTNATIVE__: boolean;
+declare const __TEST__: boolean;
+declare const __VERSION__: string;

+ 1 - 0
packages/nodes-from-anchor/package.json

@@ -52,6 +52,7 @@
     "dependencies": {
     "dependencies": {
         "@kinobi-so/errors": "workspace:*",
         "@kinobi-so/errors": "workspace:*",
         "@kinobi-so/nodes": "workspace:*",
         "@kinobi-so/nodes": "workspace:*",
+        "@kinobi-so/visitors": "workspace:*",
         "@noble/hashes": "^1.4.0"
         "@noble/hashes": "^1.4.0"
     },
     },
     "license": "MIT",
     "license": "MIT",

+ 9 - 8
packages/visitors/src/defaultVisitor.ts → packages/nodes-from-anchor/src/defaultVisitor.ts

@@ -1,15 +1,16 @@
 import { assertIsNode, Node, RootNode } from '@kinobi-so/nodes';
 import { assertIsNode, Node, RootNode } from '@kinobi-so/nodes';
-import { rootNodeVisitor, visit, Visitor } from '@kinobi-so/visitors-core';
-
-import { deduplicateIdenticalDefinedTypesVisitor } from './deduplicateIdenticalDefinedTypesVisitor';
-import { flattenInstructionDataArgumentsVisitor } from './flattenInstructionDataArgumentsVisitor';
-import { setFixedAccountSizesVisitor } from './setFixedAccountSizesVisitor';
 import {
 import {
+    deduplicateIdenticalDefinedTypesVisitor,
+    flattenInstructionDataArgumentsVisitor,
     getCommonInstructionAccountDefaultRules,
     getCommonInstructionAccountDefaultRules,
+    rootNodeVisitor,
+    setFixedAccountSizesVisitor,
     setInstructionAccountDefaultValuesVisitor,
     setInstructionAccountDefaultValuesVisitor,
-} from './setInstructionAccountDefaultValuesVisitor';
-import { transformU8ArraysToBytesVisitor } from './transformU8ArraysToBytesVisitor';
-import { unwrapInstructionArgsDefinedTypesVisitor } from './unwrapInstructionArgsDefinedTypesVisitor';
+    transformU8ArraysToBytesVisitor,
+    unwrapInstructionArgsDefinedTypesVisitor,
+    visit,
+    Visitor,
+} from '@kinobi-so/visitors';
 
 
 export function defaultVisitor() {
 export function defaultVisitor() {
     return rootNodeVisitor(currentRoot => {
     return rootNodeVisitor(currentRoot => {

+ 6 - 0
packages/nodes-from-anchor/src/index.ts

@@ -1,5 +1,7 @@
 import { RootNode } from '@kinobi-so/nodes';
 import { RootNode } from '@kinobi-so/nodes';
+import { visit } from '@kinobi-so/visitors';
 
 
+import { defaultVisitor } from './defaultVisitor';
 import { IdlV00, rootNodeFromAnchorV00 } from './v00';
 import { IdlV00, rootNodeFromAnchorV00 } from './v00';
 import { IdlV01, rootNodeFromAnchorV01 } from './v01';
 import { IdlV01, rootNodeFromAnchorV01 } from './v01';
 
 
@@ -10,6 +12,10 @@ export * from './v01';
 export type AnchorIdl = IdlV00 | IdlV01;
 export type AnchorIdl = IdlV00 | IdlV01;
 
 
 export function rootNodeFromAnchor(idl: AnchorIdl): RootNode {
 export function rootNodeFromAnchor(idl: AnchorIdl): RootNode {
+    return visit(rootNodeFromAnchorWithoutDefaultVisitor(idl), defaultVisitor());
+}
+
+export function rootNodeFromAnchorWithoutDefaultVisitor(idl: AnchorIdl): RootNode {
     if (idl.metadata?.spec === '0.1.0') {
     if (idl.metadata?.spec === '0.1.0') {
         return rootNodeFromAnchorV01(idl as IdlV01);
         return rootNodeFromAnchorV01(idl as IdlV01);
     }
     }

+ 0 - 1
packages/visitors/src/index.ts

@@ -3,7 +3,6 @@ export * from '@kinobi-so/visitors-core';
 export * from './addPdasVisitor';
 export * from './addPdasVisitor';
 export * from './createSubInstructionsFromEnumArgsVisitor';
 export * from './createSubInstructionsFromEnumArgsVisitor';
 export * from './deduplicateIdenticalDefinedTypesVisitor';
 export * from './deduplicateIdenticalDefinedTypesVisitor';
-export * from './defaultVisitor';
 export * from './fillDefaultPdaSeedValuesVisitor';
 export * from './fillDefaultPdaSeedValuesVisitor';
 export * from './flattenInstructionDataArgumentsVisitor';
 export * from './flattenInstructionDataArgumentsVisitor';
 export * from './flattenStructVisitor';
 export * from './flattenStructVisitor';

+ 9 - 0
pnpm-lock.yaml

@@ -95,9 +95,15 @@ importers:
 
 
   packages/library:
   packages/library:
     dependencies:
     dependencies:
+      '@kinobi-so/errors':
+        specifier: workspace:*
+        version: link:../errors
       '@kinobi-so/nodes':
       '@kinobi-so/nodes':
         specifier: workspace:*
         specifier: workspace:*
         version: link:../nodes
         version: link:../nodes
+      '@kinobi-so/validators':
+        specifier: workspace:*
+        version: link:../validators
       '@kinobi-so/visitors':
       '@kinobi-so/visitors':
         specifier: workspace:*
         specifier: workspace:*
         version: link:../visitors
         version: link:../visitors
@@ -121,6 +127,9 @@ importers:
       '@kinobi-so/nodes':
       '@kinobi-so/nodes':
         specifier: workspace:*
         specifier: workspace:*
         version: link:../nodes
         version: link:../nodes
+      '@kinobi-so/visitors':
+        specifier: workspace:*
+        version: link:../visitors
       '@noble/hashes':
       '@noble/hashes':
         specifier: ^1.4.0
         specifier: ^1.4.0
         version: 1.4.0
         version: 1.4.0