@s2script/sdk 0.11.0 → 0.13.0

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 CHANGED
@@ -34,6 +34,59 @@ per-capability subpaths (`@s2script/sdk/entity`, `@s2script/sdk/timers`, `@s2scr
34
34
  game-specific schema types ship separately in
35
35
  [`@s2script/cs2`](https://www.npmjs.com/package/@s2script/cs2).
36
36
 
37
+ ## Workspaces
38
+
39
+ A directory whose root `package.json` carries an `s2script.workspace.plugins` glob list — next to
40
+ npm's own `workspaces` field — is a **workspace**: a repo holding several plugins that build,
41
+ publish, and version together.
42
+
43
+ ```json
44
+ {
45
+ "name": "my-server-plugins",
46
+ "private": true,
47
+ "workspaces": ["plugins/*", "packages/*"],
48
+ "s2script": { "workspace": { "plugins": ["plugins/*"] } }
49
+ }
50
+ ```
51
+
52
+ ```bash
53
+ npx @s2script/sdk create --workspace my-server-plugins
54
+ cd my-server-plugins
55
+ npx @s2script/sdk create shop # detects the workspace, writes plugins/shop (no per-plugin devDeps)
56
+ npm install # links every member into node_modules
57
+ npx @s2script/sdk build # every plugin, dependency order; --filter <pattern> to narrow
58
+ npx @s2script/sdk deploy # build + publish, skipping `private` and already-shipped versions
59
+ npx @s2script/sdk version # applies pending changesets, cascading sibling version ranges
60
+ ```
61
+
62
+ Finding no `s2script.workspace` marker above the target directory, every command behaves exactly as
63
+ it does today — workspace mode is opt-in by construction. `s2s build <one-plugin-dir>` (naming a
64
+ specific plugin rather than the root) always stays single-plugin mode even inside a workspace;
65
+ `--filter` is the workspace-mode way to narrow to a subset, by package name (`@me/shop`, `@me/*`) or
66
+ by path glob (`plugins/sh*`). `--stamp-version <v>` (build-only) rewrites every plugin's version
67
+ *and* the sibling ranges that would otherwise break.
68
+
69
+ **Sibling interfaces need no `.s2script/types/` copy.** npm already symlinks every workspace member
70
+ into `node_modules` — declared as a dependency or not — so a consumer's `import type { ShopAPI }
71
+ from "@me/shop"` resolves straight to `@me/shop`'s own published `types` file under
72
+ `moduleResolution: "bundler"`, no config and no copy required. Contrast this with depending on a
73
+ **registry** package (`s2s add @edge/admin-core`): that pulls the producer's `.d.ts` down into your
74
+ own `.s2script/types/<dep>/index.d.ts`, because a registry producer isn't sitting in the same
75
+ checkout — a byte-copy (hashed into `manifest.compiledAgainst`, re-verified against the producer's
76
+ published hash at load) is the only way your build can see its shape at all, and it goes stale the
77
+ moment the producer's interface changes until you re-run `s2s add`. A workspace sibling's contract
78
+ can never go stale that way: the file your build resolves to *is* the producer's current contract,
79
+ on the same disk, in the same commit. If a plugin still carries a pre-migration
80
+ `.s2script/types/<dep>/` copy after joining a workspace, the live sibling wins and the build warns
81
+ that the copy is now dead weight.
82
+
83
+ **`private: true` means built, never published.** Ordinary npm/Changesets semantics, not a new
84
+ field: `s2s build` builds every plugin in a workspace regardless of `private`, but `s2s deploy`
85
+ prints a per-plugin plan that skips a `private: true` package by name instead of shipping it. That's
86
+ what lets a workspace hold a large first-party or in-house plugin suite that must never reach the
87
+ public registry — build it, drop the `.s2sp`s into your own server, never `s2s deploy` it. (`s2s
88
+ deploy <dir>` on a single private plugin outside a workspace refuses the same way.)
89
+
37
90
  ## Docs
38
91
 
39
92
  **[s2script.com/docs](https://s2script.com/docs)** — getting started, guides, and the full API
package/commands.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** @s2script/commands — register server commands. NO runtime code (injected at load). */
2
2
 
3
+ import type { HookResultValue } from "./events";
4
+
3
5
  /**
4
6
  * Where a command was invoked from — SourceMod's *reply source*. Set by the dispatch path and
5
7
  * exposed as {@link CommandInvocation.replySource}; it is what {@link CommandInvocation.reply}
@@ -112,6 +114,23 @@ export declare const Commands: {
112
114
  handleChatTrigger(slot: number, message: string): { silent: boolean; ran: boolean } | null;
113
115
  /** The trigger characters — SM PublicChatTrigger (`"!"`) / SilentChatTrigger (`"/"`). Mutate to reconfigure. */
114
116
  readonly triggers: { public: string; silent: string };
117
+ /**
118
+ * Observe a CLIENT command by name — SourceMod's `AddCommandListener`.
119
+ *
120
+ * Unlike {@link register}, which CREATES a command, this hooks one that already exists — including
121
+ * commands the engine itself owns (`player_ping`, `jointeam`, `drop`, `buy`). Registering a
122
+ * ConCommand of an engine-owned name fails outright ("unable to link multiple ConCommands named
123
+ * X"), so this is the only way to see those.
124
+ *
125
+ * OBSERVE-BY-DEFAULT: the engine still handles the command as normal. Return
126
+ * `>= HookResult.Handled` to suppress its engine-side handling — but note that for a command with
127
+ * a visible effect (a ping marker, a team change) suppressing is usually NOT what you want.
128
+ *
129
+ * @example
130
+ * // Middle-mouse ping opens the shop, and still places the ping.
131
+ * ctx.commands.onClientCommand("player_ping", (slot) => { openShop(slot); });
132
+ */
133
+ onClientCommand(name: string, handler: (slot: number, argString: string) => HookResultValue | void): void;
115
134
  /** Every globally-registered command with its required admin `flags`: `0` = anyone, `-1` = console/server-only,
116
135
  * else the `ADMFLAG` bit mask (map bits→names in your plugin). The `sm_help` backend. */
117
136
  list(): { name: string; flags: number }[];