Repository: stateful/vscode-runme
Pull request #963 (2023-11-13 16:54:43)

Lifecycle identity refactor
Goes along with https://github.com/stateful/runme/pull/428.


Patch:
diff --git a/package-lock.json b/package-lock.json
index abe540f0..eaa12616 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
       "dependencies": {
         "@apollo/client": "^3.8.3",
         "@buf/grpc_grpc.community_timostamm-protobuf-ts": "^2.9.1-20230830152835-a5b3a068eb8c.1",
-        "@buf/stateful_runme.community_timostamm-protobuf-ts": "^2.9.1-20231110144831-4699ec757527.2",
+        "@buf/stateful_runme.community_timostamm-protobuf-ts": "^2.9.1-20231113171346-3df23492b8a4.2",
         "@protobuf-ts/grpc-transport": "^2.9.1",
         "@protobuf-ts/runtime": "^2.9.1",
         "@protobuf-ts/runtime-rpc": "^2.9.1",
@@ -1562,8 +1562,8 @@
       }
     },
     "node_modules/@buf/stateful_runme.community_timostamm-protobuf-ts": {
-      "version": "2.9.1-20231110144831-4699ec757527.2",
-      "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.community_timostamm-protobuf-ts/-/stateful_runme.community_timostamm-protobuf-ts-2.9.1-20231110144831-4699ec757527.2.tgz",
+      "version": "2.9.1-20231113171346-3df23492b8a4.2",
+      "resolved": "https://buf.build/gen/npm/v1/@buf/stateful_runme.community_timostamm-protobuf-ts/-/stateful_runme.community_timostamm-protobuf-ts-2.9.1-20231113171346-3df23492b8a4.2.tgz",
       "peerDependencies": {
         "@protobuf-ts/runtime": "^2.9.1",
         "@protobuf-ts/runtime-rpc": "^2.9.1"
diff --git a/package.json b/package.json
index 1dfc6338..ff3c00e6 100644
--- a/package.json
+++ b/package.json
@@ -427,9 +427,9 @@
               "TCP",
               "UDS"
             ],
-            "markdownDescription": "Connect to GRPC server via TCP or Unix Domain Socket (restart required - not available on Windows)"
+            "markdownDescription": "Connect to GRPC server via TCP or Unix Domain Socket (not available on Windows)"
           },
-          "runme.server.persistIdentity": {
+          "runme.server.lifecycleIdentity": {
             "type": "number",
             "scope": "window",
             "default": 0,
@@ -445,7 +445,7 @@
               "Document",
               "Cell"
             ],
-            "markdownDescription": "Select whether or not identities of document and/or cells should be presistent inside markdown"
+            "markdownDescription": "Select whether or not lifecycle identities of document and/or cells should be presistent inside markdown"
           },
           "runme.terminal.backgroundTask": {
             "type": "boolean",
@@ -842,7 +842,7 @@
   "dependencies": {
     "@apollo/client": "^3.8.3",
     "@buf/grpc_grpc.community_timostamm-protobuf-ts": "^2.9.1-20230830152835-a5b3a068eb8c.1",
-    "@buf/stateful_runme.community_timostamm-protobuf-ts": "^2.9.1-20231110144831-4699ec757527.2",
+    "@buf/stateful_runme.community_timostamm-protobuf-ts": "^2.9.1-20231113171346-3df23492b8a4.2",
     "@protobuf-ts/grpc-transport": "^2.9.1",
     "@protobuf-ts/runtime": "^2.9.1",
     "@protobuf-ts/runtime-rpc": "^2.9.1",
diff --git a/src/extension/extension.ts b/src/extension/extension.ts
index c082822d..13c97153 100644
--- a/src/extension/extension.ts
+++ b/src/extension/extension.ts
@@ -144,6 +144,7 @@ export class RunmeExtension {
 
     const omitKeys: Serializer.Metadata = {
       ['runme.dev/name']: undefined,
+      ['runme.dev/id']: undefined,
       ['runme.dev/uuid']: undefined,
       ['runme.dev/textRange']: undefined,
     }
diff --git a/src/extension/serializer.ts b/src/extension/serializer.ts
index 4907b741..ea09c93f 100644
--- a/src/extension/serializer.ts
+++ b/src/extension/serializer.ts
@@ -19,13 +19,14 @@ import { GrpcTransport } from '@protobuf-ts/grpc-transport'
 
 import { Serializer } from '../types'
 import { VSCODE_LANGUAGEID_MAP } from '../constants'
-import { ServerPersistIdentity, getServerConfigurationValue } from '../utils/configuration'
+import { ServerLifecycleIdentity, getServerConfigurationValue } from '../utils/configuration'
 
 import {
   DeserializeRequest,
   SerializeRequest,
   Notebook,
   RunmeIdentity,
+  CellKind,
 } from './grpc/serializerTypes'
 import { initParserClient, ParserServiceClient } from './grpc/client'
 import Languages from './languages'
@@ -306,8 +307,11 @@ export class WasmSerializer extends SerializerBase {
 export class GrpcSerializer extends SerializerBase {
   private client?: ParserServiceClient
   protected ready: ReadyPromise
-  protected readonly persistIdentity: ServerPersistIdentity =
-    getServerConfigurationValue<ServerPersistIdentity>('persistIdentity', RunmeIdentity.UNSPECIFIED)
+  protected readonly lifecycleIdentity: ServerLifecycleIdentity =
+    getServerConfigurationValue<ServerLifecycleIdentity>(
+      'lifecycleIdentity',
+      RunmeIdentity.UNSPECIFIED,
+    )
 
   private serverReadyListener: Disposable | undefined
 
@@ -334,14 +338,35 @@ export class GrpcSerializer extends SerializerBase {
     this.client = initParserClient(transport ?? (await this.server.transport()))
   }
 
+  protected applyIdentity(data: Notebook): Notebook {
+    const identity = this.lifecycleIdentity
+    switch (identity) {
+      case RunmeIdentity.UNSPECIFIED:
+      case RunmeIdentity.DOCUMENT: {
+        break
+      }
+      default: {
+        data.cells.forEach((cell) => {
+          if (cell.kind !== CellKind.CODE) {
+            return
+          }
+          if (!cell.metadata?.['id'] && cell.metadata?.['runme.dev/id']) {
+            cell.metadata['id'] = cell.metadata['runme.dev/id']
+          }
+        })
+      }
+    }
+
+    return data
+  }
+
   protected async saveNotebook(
     data: NotebookData,
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     token: CancellationToken,
   ): Promise<Uint8Array> {
-    const identity = this.persistIdentity
     const notebook = Notebook.clone(data as any)
-    const serialRequest = <SerializeRequest>{ notebook, options: { identity } }
+    const serialRequest = <SerializeRequest>{ notebook }
 
     const request = await this.client!.serialize(serialRequest)
 
@@ -358,7 +383,7 @@ export class GrpcSerializer extends SerializerBase {
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     token: CancellationToken,
   ): Promise<Serializer.Notebook> {
-    const identity = this.persistIdentity
+    const identity = this.lifecycleIdentity
     const deserialRequest = DeserializeRequest.create({ source: content, options: { identity } })
     const request = await this.client!.deserialize(deserialRequest)
 
@@ -367,8 +392,10 @@ export class GrpcSerializer extends SerializerBase {
       throw new Error('deserialization failed to revive notebook')
     }
 
+    const _notebook = this.applyIdentity(notebook)
+
     // we can remove ugly casting once we switch to GRPC
-    return notebook as unknown as Serializer.Notebook
+    return _notebook as unknown as Serializer.Notebook
   }
 
   public dispose(): void {
diff --git a/src/extension/utils.ts b/src/extension/utils.ts
index 2fad63e7..14e60a60 100644
--- a/src/extension/utils.ts
+++ b/src/extension/utils.ts
@@ -66,6 +66,7 @@ export function getAnnotations(raw: unknown): CellAnnotations | undefined {
 
   const schema = {
     ...metadata,
+    id: metadata.id || metadata['runme.dev/id'],
     name: metadata.name || metadata['runme.dev/name'],
   }
 
diff --git a/src/schema.ts b/src/schema.ts
index 71f47d22..d6c9a10c 100644
--- a/src/schema.ts
+++ b/src/schema.ts
@@ -47,7 +47,9 @@ const boolify = (defaultValue: boolean, invalidTypeError: string = 'expected a b
   )
 
 export const AnnotationSchema = {
-  'runme.dev/uuid': z.string().uuid().optional(),
+  'runme.dev/id': z.string().optional(),
+  id: z.string().optional(),
+  'runme.dev/uuid': z.string().optional(),
   background: boolify(false),
   interactive: boolify(true),
   closeTerminalOnSuccess: boolify(true),
diff --git a/src/types.ts b/src/types.ts
index 588af57d..06798c9c 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -54,6 +54,7 @@ export namespace Serializer {
   }
 
   export interface Metadata {
+    id?: string
     name?: string
     background?: string
     interactive?: string
@@ -63,6 +64,7 @@ export namespace Serializer {
     category?: string
     ['runme.dev/name']?: string
     ['runme.dev/uuid']?: string
+    ['runme.dev/id']?: string
     ['runme.dev/denoState']?: DenoState
     ['runme.dev/vercelState']?: VercelState
     ['runme.dev/githubState']?: GitHubState
diff --git a/src/utils/configuration.ts b/src/utils/configuration.ts
index e2348acf..c63ede05 100644
--- a/src/utils/configuration.ts
+++ b/src/utils/configuration.ts
@@ -58,7 +58,7 @@ const configurationSchema = {
     enableTLS: z.boolean().default(true),
     tlsDir: z.string().optional(),
     transportType: z.enum(['TCP', 'UDS']).default('TCP'),
-    persistIdentity: z.nativeEnum(RunmeIdentity).default(RunmeIdentity.UNSPECIFIED),
+    lifecycleIdentity: z.nativeEnum(RunmeIdentity).default(RunmeIdentity.UNSPECIFIED),
   },
   codelens: {
     enable: z.boolean().default(true),
@@ -81,7 +81,7 @@ const configurationSchema = {
 const notebookTerminalSchemaObject = z.object(notebookTerminalSchema)
 export type TerminalConfiguration = z.infer<typeof notebookTerminalSchemaObject>
 export type ServerTransportType = z.infer<typeof configurationSchema.server.transportType>
-export type ServerPersistIdentity = z.infer<typeof configurationSchema.server.persistIdentity>
+export type ServerLifecycleIdentity = z.infer<typeof configurationSchema.server.lifecycleIdentity>
 
 const getActionsConfigurationValue = <T>(
   configName: keyof typeof configurationSchema.actions,


Test patch:
diff --git a/tests/extension/configuration.test.ts b/tests/extension/configuration.test.ts
index 612a5743..a07fbf93 100644
--- a/tests/extension/configuration.test.ts
+++ b/tests/extension/configuration.test.ts
@@ -133,9 +133,9 @@ suite('Configuration', () => {
   })
 
   test('getServerConfigurationValue should give default persist identity', () => {
-    expect(getServerConfigurationValue<number>('persistIdentity', RunmeIdentity.ALL)).toStrictEqual(
-      RunmeIdentity.UNSPECIFIED,
-    )
+    expect(
+      getServerConfigurationValue<number>('lifecycleIdentity', RunmeIdentity.ALL),
+    ).toStrictEqual(RunmeIdentity.UNSPECIFIED)
   })
 
   test('getCodeLensEnabled should return true by default', () => {
diff --git a/tests/extension/utils.test.ts b/tests/extension/utils.test.ts
index fd97ad06..ec780e84 100644
--- a/tests/extension/utils.test.ts
+++ b/tests/extension/utils.test.ts
@@ -240,6 +240,7 @@ suite('#getAnnotations', () => {
       category: '',
       excludeFromRunAll: false,
       promptEnv: true,
+      id: undefined,
       'runme.dev/uuid': '48d86c43-84a4-469d-8c78-963513b0f9d0',
       interpreter: '',
     })
@@ -254,6 +255,7 @@ suite('#getAnnotations', () => {
     }
 
     expect(getAnnotations(hello)).toStrictEqual({
+      id: undefined,
       background: false,
       closeTerminalOnSuccess: true,
       interactive: true,